1jQuery(function() { 2 //another json plugin may have allready initialized this 3 if(!json_plugin.initialized) { 4 json_plugin.init(); 5 } 6}); 7 8//global variable accessible by other json-type plugins 9var json_plugin = { 10 initialized: false, 11 12 init: function() { 13 //if user wants to leave the page and if this variable is not empty, 14 //then alert will ask user, if he wants to leave the page or stay on it. 15 var leavePageList = {}; 16 17 json_plugin.initialized = true; 18 19 //initialize <jsonxxx> elements 20 jQuery('.json-tabs').each(function() { 21 22 var $tabs = jQuery(this), 23 id = $tabs.data('json-id'), 24 hash = $tabs.data('json-hash'), 25 active = $tabs.data('active'), 26 $button = $tabs.find('.json-save-button'), 27 $data_original = $tabs.find('.json-data-original'), 28 $textarea = $tabs.find('.json-data-inline'), 29 $data_combined = $tabs.find('.json-data-combined'), 30 $highlight = $tabs.find('.lang-json'); 31 32 33 //save object with interface to this tabs, so other json plugins can use it 34 var o = { 35 //json data, before inline data was combined, readonly 36 get data_original() { 37 return ($data_original.length === 1) ? $data_original.text() : undefined; 38 }, 39 40 //inline json data from textarea, writeable 41 get data_inline() { 42 return ($textarea.length === 1) ? $textarea.val() : undefined; 43 }, 44 set data_inline(text) { 45 if ($textarea.length === 1) { 46 $textarea.val(text); 47 } 48 }, 49 50 //complete json data, data_original combined with data_inline 51 get data_combined() { 52 return ($data_combined.length === 1) ? $data_combined.text() : undefined; 53 }, 54 55 //jQuery button element. It triggers 'jsonBeforeSave' and 'jsonAfterSave' events. 56 $button: $button, 57 58 //function must be called by other json plugins on change of data 59 showButton: function() { 60 if(jQuery.isEmptyObject(leavePageList)) { 61 window.onbeforeunload = function(e) { 62 e.preventDefault(); 63 e.returnValue = ''; 64 }; 65 } 66 leavePageList[id] = true; 67 $button.show('slow'); 68 } 69 }; 70 $tabs.data('o', o); 71 72 73 //generate jQuery UI tabs 74 $tabs.tabs({ 75 collapsible: true, 76 active: (active === "false") ? false : parseInt(active) 77 }); 78 79 80 //button will save data to dokuwiki via ajax call 81 $button.on('click', function (event) { 82 //other json plugins may prepare data here 83 $button.trigger('jsonBeforeSave'); 84 85 var text = o.data_inline; 86 87 //validate JSON 88 if(text.trim().length > 0) { 89 try { 90 JSON.parse(text.replace(/%\$.*?%/g, 'null')); 91 } 92 catch(e) { 93 alert(e); 94 return; 95 } 96 } 97 98 //save data 99 jQuery.post( 100 DOKU_BASE + 'lib/exe/ajax.php', 101 { 102 call: 'json_plugin_save_inline',//server function 103 file: JSINFO.id, //dokuwiki file id 104 id: id, //id of the <json id=___> element 105 hash: hash, //MD5 hash of the old_text from <json ...>old_text</json> element 106 text: text //new text to write into <json ...>text</json> element 107 }, 108 function(data) { 109 if(data.response === 'OK') { 110 hash = data.hash; 111 112 //save successful, prepare event handlers, hide button 113 $textarea.one('keydown change', o.showButton); 114 $button.trigger('jsonAfterSave'); 115 $button.hide('slow'); 116 117 delete(leavePageList[id]); 118 if(jQuery.isEmptyObject(leavePageList)) { 119 window.onbeforeunload = null; 120 } 121 } 122 else if(data.response === 'error') { 123 alert(data.error); 124 } 125 else { 126 alert('Internal communication error.'); 127 } 128 }, 129 'json' 130 ); 131 }); 132 133 134 //show 'save' button once after first key is pressed 135 $textarea.one('keydown change', o.showButton); 136 137 138 //highlight json code 139 $highlight.each(function() { 140 hljs.highlightBlock(this); 141 }); 142 }); 143 144 // Make button, which will trigger ajax call for archiving data 145 // When JSON data are archived, then all json-data-original 146 // will be stored into <json> element itself. 'src' and 'src_ext' 147 // attibutes from the element will then be ignored. 148 var archives = jQuery('.json-tabs.json-make-archive'); 149 if(archives.length > 0) { 150 var $archive_button = jQuery('<button class="json-archive-button">'+LANG.plugins.json.archive_button+'</button>'); 151 152 jQuery('#dokuwiki__header').append($archive_button); 153 154 $archive_button.on('click', function (event) { 155 var json_data_original = [], 156 subdir = prompt(LANG.plugins.json.archive_move); 157 158 if (typeof subdir != 'string') { 159 return; 160 } 161 162 archives.each(function() { 163 json_data_original.push(jQuery(this).find('.json-data-original').text()); 164 }); 165 166 //save data 167 jQuery.post( 168 DOKU_BASE + 'lib/exe/ajax.php', 169 { 170 call: 'json_plugin_archive',//server function 171 file: JSINFO.id, //dokuwiki file id 172 lastmod: JSINFO.json_lastmod,//dokuwiki timestamp of the last modification to the current page 173 data: json_data_original, //array of data 174 subdir: subdir //move to subdirectory 175 }, 176 function(data) { 177 if(data.response === 'OK') { 178 //save successful, hide button 179 $archive_button.hide('slow'); 180 } 181 else if(data.response === 'error') { 182 alert(data.error); 183 } 184 else { 185 alert('Internal communication error.'); 186 } 187 }, 188 'json' 189 ); 190 }); 191 } 192 193 194 //initialize some extractors 195 //highlight json code 196 jQuery('.json-extract-code').each(function() { 197 hljs.highlightBlock(this); 198 }); 199 200 //ejs template 201 if(JSINFO.enable_ejs) { 202 jQuery('.json-extract-ejs').each(function() { 203 var $span = jQuery(this), 204 data = JSON.parse($span.find('#data').text()), 205 template = $span.find('#template').text(), 206 result = ''; 207 try { 208 result = ejs.render(template, {d : data}); 209 } 210 catch (e) { 211 console.log(e); 212 } 213 $span.text(result); 214 }); 215 } 216 }, 217 218 219 //helper function: get diff of two objects (data_original, data_combined) 220 //https://stackoverflow.com/questions/8431651/getting-a-diff-of-two-json-objects 221 diff: function (obj1, obj2) { 222 if(!obj1 || typeof obj1 !== 'object' || !obj2 || typeof obj2 !== 'object') { 223 return obj2; 224 } 225 const result = {}; 226 Object.keys(obj2).forEach(key => { 227 if(typeof obj2[key] === 'object' && typeof obj1[key] === 'object') { 228 const value = this.diff(obj1[key], obj2[key]); 229 if (Object.keys(value).length !== 0) { 230 result[key] = value; 231 } 232 } 233 else if(obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) { 234 result[key] = obj2[key]; 235 } 236 }); 237 return result; 238 } 239}; 240