1/*
2# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
3# All rights reserved.  See LICENSE file for licensing details
4*/
5
6(function(serendipity, $, undefined ) {
7    // Fires functions which are generated dynamically in backend PHP files
8    // (i.e. include/functions_entries_admin.inc.php) which load the various
9    // WYSIWYG editors in entries editor, HTML nuggets etc.
10    serendipity.spawn = function() {
11        if (self.Spawnextended) {
12            Spawnextended();
13        }
14
15        if (self.Spawnbody) {
16            Spawnbody();
17        }
18
19        if (self.Spawnnugget) {
20            Spawnnugget();
21        }
22    }
23
24    // Generic function to set cookies
25    serendipity.SetCookie = function(name, value) {
26        var today  = new Date();
27        var expire = new Date();
28        expire.setTime(today.getTime() + (60*60*24*30*1000));
29        // get array like or simple string argument items
30        if (name.indexOf("[") != -1) {
31            document.cookie = 'serendipity' + name + '=' + escape(value) + ';expires=' + expire.toGMTString();
32        } else {
33            document.cookie = 'serendipity[' + name + ']=' + escape(value) + ';expires=' + expire.toGMTString();
34        }
35    }
36
37    serendipity.GetCookie = function(name) {
38        var nameEQ = name + "=";
39        var ca = document.cookie.split(';');
40        for(var i=0;i < ca.length;i++) {
41            var c = ca[i];
42            while (c.charAt(0)==' ') c = c.substring(1,c.length);
43            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
44        }
45        return null;
46    }
47
48    /**
49     * Based upon code written by chris wetherell
50     * http://www.massless.org
51     * chris [THE AT SIGN] massless.org
52     */
53
54    // Returns "position" of selection in textarea
55    // Used internally by wrapSelectionWithLink()
56    serendipity.getSelection = function($txtarea) {
57        var start = $txtarea[0].selectionStart;
58        var end = $txtarea[0].selectionEnd;
59        return $txtarea.val().substring(start, end);
60    }
61
62    // Used by non-wysiwyg editor toolbar buttons to wrap selection
63    // in a element associated with toolbar button
64    serendipity.wrapSelection = function(txtarea, openTag, closeTag) {
65        scrollPos = false;
66
67        if (txtarea.scrollTop) {
68            scrollPos = txtarea.scrollTop;
69        }
70
71        // http://stackoverflow.com/questions/1712417/jquery-wrap-selected-text-in-a-textarea
72        var $txtarea = $(txtarea);
73
74        if (!$txtarea.length) {
75            return;
76        }
77
78        var len = $txtarea.val().length;
79        var start = $txtarea[0].selectionStart;
80        var end = $txtarea[0].selectionEnd;
81        var selectedText = $txtarea.val().substring(start, end);
82        var replacement = openTag + selectedText + closeTag;
83        $txtarea.val($txtarea.val().substring(0, start) + replacement + $txtarea.val().substring(end, len));
84
85        $txtarea[0].selectionStart = start + replacement.length;
86        $txtarea[0].selectionEnd = start + replacement.length;
87
88        if (scrollPos) {
89            txtarea.focus();
90            txtarea.scrollTop = scrollPos;
91        }
92    }
93
94    // Used by non-wysiwyg editor toolbar buttons to wrap selection
95    // in <a> element (only)
96    serendipity.wrapSelectionWithLink = function(txtarea) {
97        var my_link = prompt("Enter URL:","http://");
98
99        if (my_link) {
100            if (serendipity.getSelection($(txtarea) ) == "") {
101                var my_desc = prompt("Enter Description", '');
102            }
103            var my_title = prompt("Enter title/tooltip:", "");
104        }
105
106        html_title = "";
107
108        if (my_title != "" && my_title != null) {
109            html_title = ' title="' + my_title + '"';
110        }
111
112        if (my_link != null) {
113            lft = "<a href=\"" + my_link + "\"" + html_title + ">";
114
115            if (my_desc != null && my_desc != "") {
116                rgt = my_desc + "</a>";
117            } else {
118                rgt = "</a>";
119            }
120
121            serendipity.wrapSelection(txtarea, lft, rgt);
122        }
123
124        return;
125    }
126    /* end chris w. script */
127
128    // Adds img element to selected text
129    // Used internally by wrapInsImage()
130    serendipity.insertText = function(txtarea, str) {
131        $txtarea = $(txtarea);
132        var selLength = $txtarea.val().length;
133        var selStart = $txtarea[0].selectionStart;
134        var selEnd = $txtarea[0].selectionEnd;
135
136        if (selEnd==1 || selEnd==2) {
137            selEnd=selLength;
138        }
139
140        var before = $txtarea.val().substring(0,selStart);
141        var after = $txtarea.val().substring(selStart);
142
143        $txtarea.val(before + str + after);
144
145        $txtarea[0].selectionStart = selStart + str.length
146        $txtarea[0].selectionEnd = selStart + str.length
147    }
148
149    // Used by non-wysiwyg editor toolbar buttons to wrap selection
150    // in <img> element (only); does not really "wrap", merely inserts
151    // an <img> element before selected text
152    serendipity.wrapInsImage = function(txtarea) {
153        var loc = prompt('Enter the image location: ');
154
155        if (loc) {
156            var alttxt = prompt('Enter alternative text for this image: ');
157            serendipity.insertText(txtarea,'<img src="'+ loc + '" alt="' + alttxt + '">');
158        }
159    }
160    /* end Better-Editor functions */
161
162    // Switches preview of image selected from media db
163    serendipity.change_preview = function(input, output) {
164        var filename = document.getElementById(input).value;
165        var $target = $('#' + output + '_preview > img');
166        $target.attr('src', filename);
167    }
168
169    // Opens media db image selection in new window
170    serendipity.choose_media = function(id) {
171        serendipity.openPopup('serendipity_admin.php?serendipity[adminModule]=media&serendipity[noBanner]=true&serendipity[noSidebar]=true&serendipity[noFooter]=true&serendipity[showMediaToolbar]=false&serendipity[showUpload]=true&serendipity[htmltarget]=' + id + '&serendipity[filename_only]=true');
172    }
173
174    // "Transfer" value from media db popup to form element, used for example for selecting a category-icon
175    serendipity.serendipity_imageSelector_addToElement = function(str, id) {
176        id = serendipity.escapeBrackets(id);
177        var $input = $('#' + id);
178        $input.val(str);
179
180        if ($input.attr('type') != 'hidden') {
181            $input.focus();    // IE would generate an error when focusing an hidden element
182        }
183
184        // calling the change-event for doing stuff like generating the preview-image
185        $input.change();
186    }
187
188    // Escape [ and ] to be able to use the string as selector
189    // jQuery fails to select the input when the selector contains unescaped [ or ]
190    serendipity.escapeBrackets = function(str) {
191        str = str.replace(/\[/g, "\\[");
192        str = str.replace(/\]/g, "\\]");
193        return str;
194    }
195
196    // Add another (image) keyword
197    serendipity.AddKeyword = function(keyword)  {
198        s = document.getElementById('keyword_input').value;
199        document.getElementById('keyword_input').value = (s != '' ? s + ';' : '') + keyword;
200    }
201
202    // "Transfer" value from media db popup to textarea, including wysiwyg
203    // This gets textarea="body"/"extended" and tries to insert into the textarea
204    // named serendipity[body]/serendipity[extended]
205    serendipity.serendipity_imageSelector_addToBody = function(str, textarea) {
206        var oEditor;
207        if (typeof(FCKeditorAPI) != 'undefined') {
208            oEditor = FCKeditorAPI.GetInstance('serendipity[' + textarea + ']') ;
209
210            if (oEditor.EditMode == FCK_EDITMODE_WYSIWYG) {
211                oEditor.InsertHtml(str);
212                return;
213            }
214        } else if(typeof(xinha_editors) != 'undefined') {
215            if (typeof(xinha_editors['serendipity[' + textarea + ']']) != 'undefined') {
216                oEditor = xinha_editors['serendipity['+ textarea +']'];
217            }
218
219            if (oEditor) {
220                oEditor.insertHTML(str);
221                return;
222            }
223        } else if(typeof(HTMLArea) != 'undefined') {
224            if (textarea == 'body' && typeof(editorbody) != 'undefined') {
225                oEditor = editorbody;
226            } else if (textarea == 'extended' && typeof(editorextended) != 'undefined') {
227                oEditor = editorextended;
228            } else if (typeof(htmlarea_editors) != 'undefined' && typeof(htmlarea_editors[textarea]) != 'undefined') {
229                oEditor =  htmlarea_editors[textarea];
230            }
231
232            if (oEditor._editMode != 'textmode') {
233                oEditor.insertHTML(str);
234                return;
235            }
236        } else if(typeof(TinyMCE) != 'undefined') {
237            // for the TinyMCE editor we do not have a text mode insert
238            tinyMCE.execInstanceCommand('serendipity[' + textarea + ']', 'mceInsertContent', false, str);
239            return;
240        } else if (typeof(CKEDITOR) != 'undefined') {
241            oEditor = (typeof(isinstance) == 'undefined') ? CKEDITOR.instances[textarea] : isinstance;
242            if (typeof(oEditor) == 'undefined') oEditor = popupEditorInstance;
243            if (oEditor.mode == "wysiwyg") {
244                oEditor.insertHtml(str);
245                return;
246            }
247        }
248
249        serendipity.noWysiwygAdd(str, textarea);
250    }
251
252    // The noWysiwygAdd JS function is the vanila serendipity_imageSelector_addToBody js function
253    // which works fine in NO WYSIWYG mode
254    // NOTE: the serendipity_imageSelector_addToBody could add any valid HTML string to the textarea
255    serendipity.noWysiwygAdd = function(str, textarea) {
256        escapedElement = serendipity.escapeBrackets(textarea);
257        if ($('#' + escapedElement).length) {
258            // Proper ID was specified (hopefully by plugins)
259        } else {
260            // Let us try the serendipity[] prefix
261            escapedElement = serendipity.escapeBrackets('serendipity[' + textarea + ']');
262
263            if (!$('#' + escapedElement).length) {
264                console.log("Serendipity plugin error: " + escapedElement + " not found.");
265            }
266        }
267
268        serendipity.wrapSelection($('#'+escapedElement), str, '');
269    }
270
271    // Inserting media db img markup including s9y-specific container markup
272    serendipity.serendipity_imageSelector_done = function(textarea) {
273        var insert = '';
274        var img = '';
275        var src = '';
276        var alt = '';
277        var title = '';
278
279        var f = document.forms['serendipity[selForm]'].elements;
280
281        img           = f['imgName'].value;
282        var imgWidth  = f['imgWidth'].value;
283        var imgHeight = f['imgHeight'].value;
284        if (f['serendipity[linkThumbnail]'] && f['serendipity[linkThumbnail]'][0].checked == true) {
285            img       = f['thumbName'].value;
286            imgWidth  = f['imgThumbWidth'].value;
287            imgHeight = f['imgThumbHeight'].value;
288        }
289
290        if (parent.self.opener == undefined) {
291            // in iframes, there is no opener, and the magnific popup is wrapped
292            parent.self = window.parent.parent.$.magnificPopup;
293            parent.self.opener = window.parent.parent;
294        }
295
296        if (f['serendipity[filename_only]']) {
297            // this part is used when selecting only the image without further markup (-> category-icon)
298            var starget = f['serendipity[htmltarget]'] ? f['serendipity[htmltarget]'].value : 'serendipity[' + textarea + ']';
299
300            switch(f['serendipity[filename_only]'].value) {
301            case 'true':
302                parent.self.opener.serendipity.serendipity_imageSelector_addToElement(img, f['serendipity[htmltarget]'].value);
303                parent.self.close();
304                return true;
305            case 'id':
306                parent.self.opener.serendipity.serendipity_imageSelector_addToElement(f['imgID'].value, starget);
307                parent.self.close();
308                return true;
309            case 'thumb':
310                parent.self.opener.serendipity.serendipity_imageSelector_addToElement(f['thumbName'].value, starget);
311                parent.self.close();
312                return true;
313            case 'big':
314                parent.self.opener.serendipity.serendipity_imageSelector_addToElement(f['imgName'].value, starget);
315                parent.self.close();
316                return true;
317            }
318        }
319
320        alt = f['serendipity[alt]'].value.replace(/"/g, "&quot;");
321        title = f['serendipity[title]'].value.replace(/"/g, "&quot;");
322
323        var imgID = 0;
324        if (f['imgID']) {
325            imgID = f['imgID'].value;
326        }
327
328        var floating = $(':input[name="serendipity[align]"]:checked').val();
329        if (floating == "") {
330            floating = "center";
331        }
332        img = "<!-- s9ymdb:" + imgID + " --><img class=\"serendipity_image_"+ floating +"\" width=\"" + imgWidth + "\" height=\"" + imgHeight + '"  src="' + img + "\" " + (title != '' ? 'title="' + title + '"' : '') + " alt=\"" + alt + "\">";
333
334        if ($(':input[name="serendipity[isLink]"]:checked').val() == "yes") {
335            // wrap the img in a link to the image. TODO: The label in the media_chooser.tpl explains it wrong
336            var targetval = $('#select_image_target').val();
337
338            var prepend   = '';
339            var ilink     = f['serendipity[url]'].value;
340            var itarget = '';
341
342            switch (targetval) {
343            case 'js':
344                var itarget = ' onclick="F1 = window.open(\'' + f['serendipity[url]'].value + '\',\'Zoom\',\''
345                        + 'height=' + (parseInt(f['imgHeight'].value) + 15) + ','
346                        + 'width='  + (parseInt(f['imgWidth'].value)  + 15) + ','
347                        + 'top='    + (screen.height - f['imgHeight'].value) /2 + ','
348                        + 'left='   + (screen.width  - f['imgWidth'].value)  /2 + ','
349                        + 'toolbar=no,menubar=no,location=no,resize=1,resizable=1,scrollbars=yes\'); return false;"';
350                break;
351            case '_blank':
352                var itarget = ' target="_blank"';
353                break;
354            case 'plugin':
355                var itarget = ' id="s9yisphref' + imgID + '" onclick="javascript:this.href = this.href + \'&amp;serendipity[from]=\' + self.location.href;"';
356                prepend = '<a title="' + ilink + '" id="s9yisp' + imgID + '"></a>';
357                ilink   = f['baseURL'].value + 'serendipity_admin_image_selector.php?serendipity[step]=showItem&amp;serendipity[image]=' + imgID;
358                break;
359            }
360
361            var img = prepend + "<a class=\"serendipity_image_link\" " + (title != '' ? 'title="' + title + '"' : '') + " href='" + ilink + "'" + itarget + ">" + img + "</a>";
362        }
363
364        if ($('#serendipity_imagecomment').val() != '') {
365            var comment = f['serendipity[imagecomment]'].value;
366
367            var img = '<figure class="serendipity_imageComment_' + floating + '" style="width: ' + imgWidth + 'px">'
368                  +     '<div class="serendipity_imageComment_img">' + img + '</div>'
369                  +     '<figcaption class="serendipity_imageComment_txt">' + comment + '</figcaption>'
370                  + '</figure>';
371        }
372
373        if (parent.self.opener.serendipity == undefined) {
374            // in iframes, there is no opener, and the magnific popup is wrapped
375            parent.self = window.parent.parent.$.magnificPopup;
376            parent.self.opener = window.parent.parent;
377        }
378        parent.self.opener.serendipity.serendipity_imageSelector_addToBody(img, textarea);
379        parent.self.close();
380    }
381
382    // Toggle extended entry editor
383    serendipity.toggle_extended = function(setCookie) {
384        if ($('#toggle_extended').length == 0) {
385            // this function got called on load of the editor
386            var toggleButton = '#toggle_extended';
387            $('#extended_entry_editor').parent().find('label').first().wrap('<button id="toggle_extended" class="icon_link" type="button"></button>');
388            $(toggleButton).prepend('<span class="icon-down-dir" aria-hidden="true"></span> ');
389            $(toggleButton).click(function(e) {
390                e.preventDefault();
391                serendipity.toggle_extended(true);
392            });
393            if (localStorage !== null && localStorage.show_extended_editor == "true") {
394                // the editor is visible by default - note the string, as bool is not supported yet in localStorage
395                return;
396            }
397        }
398
399        if ($('#extended_entry_editor:hidden').length > 0) {
400            $('#extended_entry_editor').show(); // use name selector instead of id here; id does not work
401            $('#tools_extended').show();
402            $('#toggle_extended').find('> .icon-right-dir').removeClass('icon-right-dir').addClass('icon-down-dir');
403            if (localStorage !== null) {
404                localStorage.show_extended_editor = "true";
405            }
406        } else {
407            $('#extended_entry_editor').hide();
408            $('#tools_extended').hide();
409            $('#toggle_extended').find('> .icon-down-dir').removeClass('icon-down-dir').addClass('icon-right-dir');
410            if (localStorage !== null) {
411                localStorage.show_extended_editor = "false";
412            }
413        }
414        if (setCookie) {
415            document.cookie = 'serendipity[toggle_extended]=' + (($('#extended_entry_editor:hidden').length == 0) ? "true" : "") + ';';
416        }
417    }
418
419    // Collapses/expands the category selector
420    var categoryselector_stored_categories = null;
421    serendipity.toggle_category_selector = function(id) {
422        if ($('#toggle_' + id).length == 0) {
423            // this function got called on load of the editor
424            var toggleButton = '#toggle_' + id;
425
426            $('#'+id).before('<button id="toggle_' + id + '" class="button_link" type="button" href="#' + id + '"><span class="icon-right-dir" aria-hidden="true"></span><span class="visuallyhidden"> {$CONST.TOGGLE_ALL}</span></button>');
427
428            $(toggleButton).click(function(e) {
429                e.preventDefault();
430                $(this).toggleClass('active');
431                serendipity.toggle_category_selector(id);
432            });
433            $('#'+id).change(function(e) {
434                categoryselector_stored_categories = null;
435            });
436
437            if ($('#'+id).children('*[selected="selected"]').length > 1) {
438                // when loading the page new for the preview and more than one category was
439                // selected, collapsing the category-selector would lose those categories
440                $('#'+id).attr("size", $('#'+id).children().size);
441                $('#toggle_' + id).find('> .icon-right-dir').removeClass('icon-right-dir').addClass('icon-down-dir');
442                return
443            }
444
445        }
446
447        if ($('#'+id).attr("multiple")) {
448            if ($('#'+id).children(':selected').filter('[value!="0"]').length > 1) {
449                // when collapsing, all multiple selection needs to be saved to be restoreable if the click was a mistake
450                var selected_categories = '';
451                $('#'+id).children(':selected').filter('[value!="0"]').each(function(i, child) {
452                    selected_categories += child.value + ','
453                });
454                categoryselector_stored_categories = selected_categories;
455            }
456            $('#'+id).removeAttr("multiple");
457            $('#'+id).removeAttr("size");
458            $('#toggle_' + id).find('> .icon-down-dir').removeClass('icon-down-dir').addClass('icon-right-dir');
459
460        } else {
461            $('#'+id).attr("multiple", "");
462            $('#'+id).attr("size", $('#'+id).children().size);
463            $('#toggle_' + id).find('> .icon-right-dir').removeClass('icon-right-dir').addClass('icon-down-dir');
464
465            var selected_categories = categoryselector_stored_categories;
466            if (selected_categories != null) {
467                selected_categories = selected_categories.split(',');
468                selected_categories.forEach(function(cat_id) {
469                    if(cat_id) {
470                        $('#'+id).find('[value="'+ cat_id +'"]').attr('selected', 'selected');
471                    }
472                });
473            }
474
475        }
476    }
477
478    // save in the cookie which options were selected when inserting a image from the media db
479    serendipity.rememberMediaOptions = function() {
480        $('#imageForm :input').each(function(index, element) {
481            if (! (element.type == 'radio' && element.checked == false)) {
482                serendipity.SetCookie(element.name.replace(/\[/g, '_').replace(/\]/g, ''), $(element).val());
483            }
484        });
485    }
486
487    // Rescale image
488    serendipity.rescale = function(dim, newval) {
489        var ratio          = $('#serendipityScaleImg').attr('data-imgheight')/$('#serendipityScaleImg').attr('data-imgwidth');
490        var trans          = new Array();
491        trans['width']     = new Array('serendipity[height]', ratio);
492        trans['height']    = new Array('serendipity[width]', 1/ratio);
493
494        if ($('#resize_keepprops').is(':checked')) {
495            document.serendipityScaleForm.elements[trans[dim][0]].value=Math.round(trans[dim][1]*newval);
496        }
497    }
498
499    // Rename file in media db
500    serendipity.rename = function(id, fname) {
501        var newname;
502        var media_rename = '{$CONST.ENTER_NEW_NAME}';
503        if (newname = prompt(media_rename + fname, fname)) {
504            var media_token_url = $('input[name*="serendipity[token]"]').val();
505            $.ajax({
506                type: 'POST',
507                url: '?serendipity[adminModule]=images&serendipity[adminAction]=rename&serendipity[fid]='+ escape(id) +'&serendipity[newname]='+ escape(newname) +'&serendipity[token]='+ media_token_url,
508                async: true,
509                cache: false,
510                success: function(response) {
511                    $response = (response.trim() == '')
512                        ? '<p>{$CONST.DONE}!</p>\
513                           <button id="rename_ok" class="button_link state_submit" type="button" >{$CONST.GO}</button>\
514                          '
515                        : response + '\
516                           <input class="go_back" type="button" onClick="$.magnificPopup.close();" value="{$CONST.BACK}">\
517                          ';
518                    $.magnificPopup.open({
519                        items: {
520                            type: 'inline',
521                                src: $('<div id="rename_msg">\
522                                        <h4>{$CONST.MEDIA_RENAME}</h4>\
523                                        '+ $response +'\
524                                        </div>')
525                        },
526                        type: 'inline',
527                        midClick: true,
528                        callbacks: {
529                            open: function() {
530                                this.content.on('click', '#rename_ok', function() {
531                                    window.parent.parent.location.href= '?serendipity[adminModule]=images&serendipity[adminAction]=default';
532                                });
533                            },
534                        }
535                    });
536                },
537                error: function(XMLHttpRequest, textStatus, errorThrown) {
538                    $.magnificPopup.open({
539                        items: {
540                            type: 'inline',
541                                src: $('<div id="rename_msg">\
542                                        <h4>{$CONST.MEDIA_RENAME}</h4>\
543                                        <p>"Status: " + textStatus</p>\
544                                        '+ errorThrown +'\
545                                        <button id="rename_error" class="button_link state_submit" type="button" >{$CONST.GO}</button>\
546                                        </div>')
547                        },
548                        type: 'inline',
549                        midClick: true,
550                        callbacks: {
551                            open: function() {
552                                this.content.on('click', '#rename_error', function() {
553                                    window.parent.parent.location.href= '?serendipity[adminModule]=images&serendipity[adminAction]=default';
554                                });
555                            },
556                        }
557                    });
558                }
559            });
560        }
561    }
562
563    // Delete file from ML
564    serendipity.deleteFromML = function(id, fname) {
565        if (confirm('{$CONST.DELETE}?')) {
566            var media_token_url = $('input[name*="serendipity[token]"]').val();
567            $.post('?serendipity[adminModule]=images&serendipity[adminAction]=doDelete&serendipity[fid]='+ escape(id) +'&serendipity[token]='+ media_token_url)
568            .done(function(jqXHR, textStatus) {
569                if (textStatus == 'success') {
570                    $.magnificPopup.open({
571                        items: {
572                            type: 'inline',
573                                src: $('<div id="delete_msg">\
574                                        <h4>{$CONST.MEDIA_DELETE}</h4>\
575                                        '+ jqXHR + '\
576                                        <button id="delete_ok" class="button_link state_submit" type="button" >{$CONST.GO}</button>\
577                                        </div>')
578                        },
579                        type: 'inline',
580                        midClick: true,
581                        callbacks: {
582                            open: function() {
583                                this.content.on('click', '#delete_ok', function() {
584                                    window.parent.parent.location.href= '?serendipity[adminModule]=images&serendipity[adminAction]=default';
585                                });
586                            },
587                        }
588                    });
589                }
590            })
591            .fail(function(jqXHR, textStatus, errorThrown) {
592                if (textStatus != null) { // what could that be ? "timeout", "error", "abort", "parsererror" ?
593                    $.magnificPopup.open({
594                        items: {
595                            type: 'inline',
596                                src: $('<div id="delete_msg">\
597                                        <h4>{$CONST.MEDIA_DELETE}</h4>\
598                                        '+ jqXHR + '\
599                                        <p>"Status: " + textStatus</p>\
600                                        '+ errorThrown +'\
601                                        <button id="delete_error" class="button_link state_submit" type="button" >{$CONST.GO}</button>\
602                                        </div>')
603                        },
604                        type: 'inline',
605                        midClick: true,
606                        callbacks: {
607                            open: function() {
608                                this.content.on('click', '#delete_error', function() {
609                                    window.parent.parent.location.href= '?serendipity[adminModule]=images&serendipity[adminAction]=default';
610                                });
611                            },
612                        }
613                    });
614                }
615            });
616        }
617    }
618
619    // Collapse/expand tree view in media db choose img popup window
620    var tree_toggle_state = 'expand';
621
622    serendipity.treeToggleAll = function() {
623        if (tree_toggle_state == 'expand') {
624            tree_toggle_state = 'collapse';
625            tree.expandAll();
626        } else {
627            tree_toggle_state = 'expand';
628            tree.collapseAll();
629            coreNode.expand();
630        }
631    }
632
633    // Used by media_upload.tpl to ..?
634    serendipity.getfilename = function(value) {
635        re = /^.+[\/\\]+?(.+)$/;
636        return value.replace(re, "$1");
637    }
638
639    var inputStorage = new Array();
640
641    serendipity.checkInputs = function() {
642        upload_fieldcount = $('.uploadform_userfile').length;
643
644        for (i = 1; i <= upload_fieldcount; i++) {
645            if (!inputStorage[i]) {
646                serendipity.fillInput(i, i);
647            } else if (inputStorage[i] == document.getElementById('target_filename_' + i).value) {
648                serendipity.fillInput(i, i);
649            }
650        }
651    }
652
653    serendipity.fillInput = function(source, target) {
654        sourceval = serendipity.getfilename(document.getElementById('userfile_' + source).value);
655
656        if (sourceval.length > 0) {
657            document.getElementById('target_filename_' + target).value = sourceval;
658            inputStorage[target] = sourceval;
659        }
660    }
661
662    // check that the entry is validated for saving, used mainly by serendipity_event_entrycheck
663    serendipity.checkSave = function() {
664        {serendipity_hookPlugin hook='backend_entry_checkSave' hookAll='true'}
665        return true;
666    }
667
668    // set a category to checked
669    serendipity.enableCategory = function(catNumber) {
670        $("#" + "serendipity_category_" + catNumber).prop("checked", true);
671    }
672
673    // is no category enabled?
674    serendipity.hasNoCategoryEnabled = function() {
675        var $source = $('#edit_entry_category');
676        var $selected = $source.find('input:checkbox:checked');
677
678        if ($selected.length > 0) {
679            return false;
680        }
681        else {
682            return true;
683        }
684    }
685
686    // save in which directory the first uploaded files is stored (the default when only inserting one file)
687    serendipity.rememberUploadOptions = function() {
688        serendipity.SetCookie("addmedia_directory", $('#target_directory_1').val());
689    }
690
691    // Clones the upload form template
692    serendipity.addUploadField = function() {
693        upload_fieldcount = $('.uploadform_userfile').length + 1;
694
695        var $fields = $('#uploads > div:last-child').clone(true);
696        $fields.attr('id', 'upload_form_' + upload_fieldcount);
697
698        var userfile             = $('.uploadform_userfile',               $fields);
699        var userfile_label       = $('.uploadform_userfile_label',         $fields);
700        var targetfilename       = $('.uploadform_target_filename',        $fields);
701        var targetfilename_label = $('.uploadform_target_filename_label',  $fields);
702        var targetdir            = $('.uploadform_target_directory',       $fields);
703        var targetdir_label      = $('.uploadform_target_directory_label', $fields);
704
705        userfile.attr('id', 'userfile_' + upload_fieldcount);
706        userfile.attr('name', 'serendipity[userfile][' + upload_fieldcount + ']');
707        userfile_label.attr('for', 'userfile_' + upload_fieldcount);
708
709        targetfilename.attr('id', 'target_filename_' + upload_fieldcount);
710        targetfilename.attr('name', 'serendipity[target_filename][' + upload_fieldcount + ']');
711        targetfilename.val('');
712        targetfilename_label.attr('for', 'target_filename_' + upload_fieldcount);
713
714        targetdir.attr('id', 'target_directory_' + upload_fieldcount);
715        targetdir.attr('name', 'serendipity[target_directory][' + upload_fieldcount + ']');
716        // This looks weird, but works. If anyone can improve this, by all means do so.
717        targetdir.val($($('#target_directory_' + (upload_fieldcount - 1))).val());
718        targetdir_label.attr('for', 'target_directory_' + upload_fieldcount);
719
720        $fields.appendTo('#uploads');
721    }
722
723    // Inverts a selection of checkboxes
724    serendipity.invertSelection = function() {
725        $('#formMultiDelete .multidelete').each(function() {
726            var $box = $(this);
727            var boxId = $box.attr('id');
728            if($box.is(':checked')) {
729                $(boxId).prop('checked', false);
730            } else {
731                $(boxId).prop('checked', true);
732            }
733            $box.trigger('click');
734        });
735    }
736
737    // Highlight/dehighlight elements in lists
738    serendipity.highlightComment = function(id, checkvalue) {
739        $('#' + id).toggleClass('multidel_selected');
740    }
741
742    serendipity.closeCommentPopup = function() {
743        {if $use_backendpopups || $force_backendpopups.comments}
744            parent.self.close();
745        {else}
746            window.parent.parent.$.magnificPopup.close();
747        {/if}
748    }
749
750    serendipity.openPopup = function(url) {
751        {if $use_backendpopups || $force_backendpopups.images}
752            window.open(url,
753                        'ImageSel',
754                        'width=800,height=600,toolbar=no,scrollbars=1,scrollbars,resize=1,resizable=1');
755        {else}
756            $.magnificPopup.open({
757              items: {
758                src: url
759              },
760              type: 'iframe'
761            });
762        {/if}
763
764    };
765
766    serendipity.reloadImage = function(img) {
767        $(img).attr('src', $(img).attr('src')+'?'+Math.random());
768    }
769
770    serendipity.catsList = function() {
771        var $source = $('#edit_entry_category');
772        var $target = $('#cats_list > ul');
773        var $selected = $source.find('input:checkbox:checked');
774
775        $target.empty();
776
777        if ($selected.length > 0) {
778            $selected.each(function() {
779                var catText = $(this).next('label').text();
780                catText = $('<span>').text(catText).html();
781                $('<li class="selected">'+ catText +'</li>').appendTo($target);
782            });
783        } else {
784            $('<li>{$CONST.NO_CATEGORIES}</li>').appendTo($target);
785        }
786    }
787
788    serendipity.tagsList = function() {
789        var $source = $('#properties_freetag_tagList').val();
790        var $target = $('#tags_list > ul');
791
792        if (typeof $source !== 'undefined') {
793            var tagged = $source.split(',');
794            $target.empty();
795
796            if (tagged == '') {
797                $('<li>{$CONST.EDITOR_NO_TAGS}</li>').appendTo($target);
798            } else {
799                $.each(tagged, function(key, tag) {
800                    $('<li class="selected">'+ tag +'</li>').appendTo($target);
801                });
802            }
803        }
804    }
805
806    serendipity.liveFilters = function(input, target, elem) {
807        var currentInput = $(input).val().toLowerCase();
808
809        if (currentInput == '') {
810            $(target).toggle(true);
811        } else {
812            $(target).each(function() {
813                var $el = $(this);
814
815                if ($el.find(elem).html().toLowerCase().indexOf(currentInput) > -1) {
816                    $el.toggle(true);
817                } else {
818                    $el.toggle(false);
819                }
820            });
821        }
822    }
823
824    if(Modernizr.indexeddb && {$use_autosave}) {
825        serendipity.startEntryEditorCache = function() {
826            if ($('textarea[name="serendipity[body]"]').val() == "") {
827                serendipity.getCached("serendipity[body]",  function(res) {
828                    if (res && res != null && res != "null") {
829                        $('textarea[name="serendipity[body]"]').text(res);
830                    }
831                });
832                serendipity.getCached("serendipity[extended]",  function(res) {
833                    if (res && res != null && res != "null") {
834                        if ($('textarea[name="serendipity[extended]"]').val() == "") {
835                            $('textarea[name="serendipity[extended]"]').text(res);
836                            if (! $('textarea[name="serendipity[extended]"]').is(':visible')) {
837                                serendipity.toggle_extended();
838                            }
839                        }
840                    }
841                });
842            }
843
844            $('textarea[name="serendipity[body]"]').one('keyup', function() {
845                setInterval(function() {
846                    serendipity.cache("serendipity[body]", $('textarea[name="serendipity[body]"]').val())
847                }, 5000);
848            });
849            $('textarea[name="serendipity[extended]"]').one('keyup', function() {
850                setInterval(function() {
851                    serendipity.cache("serendipity[extended]", $('textarea[name="serendipity[extended]"]').val());
852                }, 5000);
853            });
854        }
855
856        serendipity.eraseEntryEditorCache = function() {
857            serendipity.cache("serendipity[body]", "");
858            serendipity.cache("serendipity[extended]", "");
859        }
860
861        var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
862
863        serendipity.cache = function (id, data) {
864            if (typeof indexedDB !== 'undefined') {
865                var request = indexedDB.open("cache", 1);
866                request.onupgradeneeded = function (event) {
867                    event.target.result.createObjectStore("cache");
868                };
869                request.onsuccess = function(event) {
870                    event.target.result.transaction(["cache"], 'readwrite').objectStore("cache").put(data, id);
871                };
872            }
873        }
874
875        serendipity.getCached = function(id, success) {
876            if (typeof indexedDB !== 'undefined') {
877                var request = indexedDB.open("cache", 1);
878                request.onupgradeneeded = function (event) {
879                    event.target.result.createObjectStore("cache");
880                };
881                request.onsuccess = function(event) {
882                    event.target.result.transaction(["cache"], 'readwrite').objectStore("cache").get(id).onsuccess = function (event) {
883                        success(event.target.result);
884                    };
885                };
886            }
887        }
888    }
889
890    serendipity.toggle_collapsible = function(toggler, target, stateClass, stateIcon, stateOpen, stateClosed) {
891        // argument defaults
892        stateClass = stateClass || 'additional_info';
893        stateIcon = stateIcon || '> span';
894        stateOpen = stateOpen || 'icon-down-dir';
895        stateClosed = stateClosed || 'icon-right-dir';
896
897        var $toggleIcon = $(toggler).find(stateIcon);
898        var toggleState = $toggleIcon.attr('class');
899
900        // if toggler does not have an id, do not store state
901        var togglerId = $(toggler).attr('id');
902        if (togglerId !== undefined) {
903            var storageKey = 'show_' + $(toggler).attr('id');
904        }
905
906        if(toggleState == stateOpen) {
907            $toggleIcon.removeClass(stateOpen).addClass(stateClosed);
908            if (togglerId !== undefined && localStorage !== null) {
909                localStorage.setItem(storageKey, "false");
910            }
911        } else {
912            $toggleIcon.removeClass(stateClosed).addClass(stateOpen);
913            if (togglerId !== undefined && localStorage !== null) {
914                localStorage.setItem(storageKey, "true");
915            }
916        }
917
918        $(target).toggleClass(stateClass);
919    }
920
921    serendipity.sync_heights = function() {
922        if($('.equal_heights').length > 0) {
923            if($('html').hasClass('lt-ie9')) {
924                $('.equal_heights').syncHeight({
925                    updateOnResize: false
926                });
927            } else {
928                $('.equal_heights').syncHeight({
929                    updateOnResize: true
930                });
931            }
932        }
933    }
934
935    serendipity.skipScroll = function(target, time) {
936        time = typeof time !== 'undefined' ? time : 250;
937
938        $('html, body').animate({
939            scrollTop: $(target).offset().top
940        }, time);
941    }
942
943    serendipity.updateAll = function() {
944        var $overlay = $('<div id="overlay" />');
945        $.get('?serendipity[adminModule]=plugins&serendipity[adminAction]=renderOverlay')
946        .done(function(data) {
947            $overlay.append(data);
948            $overlay.appendTo(document.body);
949            $('#updateProgress').attr('max', $('.plugin_status').length);
950            serendipity.updateNext();
951        });
952    }
953
954    serendipity.updateNext = function() {
955        $('#updateMessage').text("Updating " + $('.plugins_installable > li:visible h4').first().text());
956        $.get($('.plugin_status .button_link:visible').first().attr('href'))
957        .done(function(messages) {
958            $('.plugins_installable > li:visible').first().fadeOut();
959            $('#updateProgress').attr('value', parseInt($('#updateProgress').attr('value')) + 1);
960            if ($('.plugins_installable > li:visible').length > 0) {
961                serendipity.updateNext();
962            } else {
963                $('#overlay').fadeOut("normal", function () {
964                    window.location = $('#back').attr('href') + '&serendipity[updateAllMsg]=true';
965                });
966            }
967        })
968        .fail(function(data) {
969            $('#content').prepend(data.responseText);
970            $('#updateAll').hide();
971            $('#overlay').fadeOut();
972        });
973    }
974
975}( window.serendipity = window.serendipity || {}, jQuery ))
976
977$(function() {
978    // Breakpoints for responsive JS
979    var mq_small = Modernizr.mq('(min-width:640px)');
980    // IE 8 should always be larger than mq_small
981    if($('html').hasClass('lt-ie9')) { mq_small = true; }
982
983    // Fire responsive nav
984    if($('#main_menu').length > 0) {
985        $('#nav-toggle').click(function(e) {
986            var $el = $(this);
987            var $target = $('body');
988            var $icon = $el.find('span:first-child');
989            $($target).toggleClass('active_nav');
990            if($($target).hasClass('active_nav')) {
991                $icon.removeClass('icon-menu').addClass('icon-cancel');
992            } else {
993                $icon.removeClass('icon-cancel').addClass('icon-menu');
994            }
995            e.preventDefault();
996        });
997    }
998
999    // Fire details polyfill
1000    $('html').addClass($.fn.details.support ? 'details' : 'no-details');
1001    $('details').details();
1002
1003    // Fire WYSIWYG editor(s)
1004    serendipity.spawn();
1005
1006    // Fire a11y helper script
1007    AccessifyHTML5({
1008        header: '#top',
1009        footer: '#meta'
1010    });
1011
1012    // Layout helpers for IE < 9
1013    if($('html').hasClass('lt-ie9')) {
1014        if($('#dashboard').length > 0) {
1015            // For some reason, .addClass() does not work here
1016            $('.dashboard_widget:nth-child(odd)').css('clear', 'left');
1017            $('.dashboard_widget:nth-child(even)').css('margin', '0 0 1em 2%');
1018        }
1019    }
1020
1021    // Editor-area
1022    if($('#serendipityEntry').length > 0) {
1023        serendipity.catsList();
1024        serendipity.tagsList();
1025        serendipity.toggle_category_selector('categoryselector');
1026        serendipity.toggle_extended();
1027    }
1028
1029    // Form submit events
1030    $('#uploadform').submit(function() {
1031        serendipity.rememberUploadOptions();
1032    });
1033
1034    $('#imageForm').submit(function() {
1035        serendipity.serendipity_imageSelector_done();
1036    });
1037
1038    $('#serendipityEntry').submit(function() {
1039        return serendipity.checkSave();
1040    });
1041
1042    // Click events
1043    //
1044    // Make the timestamp readable in browser not supporting datetime-local.
1045    // Has no effect in those supporting it, as the timestamp is invalid in HTML5
1046    if($('#serendipityEntry').length > 0) {
1047        if(!Modernizr.inputtypes.date) {
1048            $('#serendipityNewTimestamp').val($('#serendipityNewTimestamp').val().replace("T", " "));
1049        }
1050        if(Modernizr.indexeddb && {$use_autosave}) {
1051            serendipity.startEntryEditorCache();
1052        }
1053    }
1054
1055    // Set entry timestamp
1056    $('#reset_timestamp').click(function(e) {
1057        $('#serendipityNewTimestamp').val($(this).attr('data-currtime'));
1058        if(!Modernizr.inputtypes.date) {
1059            $('#serendipityNewTimestamp').val($('#serendipityNewTimestamp').val().replace("T", " "));
1060        }
1061        e.preventDefault();
1062        // Inline notification, we might want to make this reuseable
1063        $('<span id="msg_timestamp" class="msg_notice"><span class="icon-info-circled" aria-hidden="true"></span>{$CONST.TIMESTAMP_RESET} <a class="remove_msg" href="#msg_timestamp"><span class="icon-cancel" aria-hidden="true"></span><span class="visuallyhidden">{$CONST.HIDE}</span></a></span>').insertBefore('#edit_entry_title');
1064        // Remove timestamp msg
1065        $('.remove_msg').click(function(e) {
1066            e.preventDefault();
1067            var $target = $(this).parent();
1068            $target.fadeOut(250, function() { $target.remove(); });
1069        });
1070        // Automagic removal after 5 secs
1071        setTimeout(function() {
1072            $('#msg_timestamp').fadeOut(250).remove();
1073        }, 5000);
1074    });
1075
1076    // Switch entry status
1077    $('#switch_entry_status').click(function(e) {
1078        var $el = $(this);
1079        var oldState = $el.attr('title');
1080        var newState = $el.attr('data-title-alt');
1081        var entryState = $('#entry_status option');
1082        var stateIcon = $el.find("[class^='icon']");
1083
1084        $(entryState).filter(function() {
1085            return $(this).html() == oldState;
1086        }).prop('selected', false);
1087
1088        $(entryState).filter(function() {
1089            return $(this).html() == newState;
1090        }).prop('selected', true);
1091
1092        $el.attr({
1093            'title': newState,
1094            'data-title-alt': oldState
1095        }).find('> .visuallyhidden').text(newState);
1096
1097        if(stateIcon.hasClass('icon-toggle-on')) {
1098            stateIcon.removeClass('icon-toggle-on').addClass('icon-toggle-off');
1099        } else {
1100            stateIcon.removeClass('icon-toggle-off').addClass('icon-toggle-on');
1101        }
1102
1103        // Inline notification, we might want to make this reuseable
1104        $('<span id="msg_entrystatus" class="msg_notice"><span class="icon-info-circled" aria-hidden="true"></span>{$CONST.ENTRY_STATUS}: ' + newState + ' <a class="remove_msg" href="#msg_entrystatus"><span class="icon-cancel" aria-hidden="true"></span><span class="visuallyhidden">{$CONST.HIDE}</span></a></span>').insertBefore('#edit_entry_title');
1105        // Remove entrystatus msg
1106        $('.remove_msg').click(function(e) {
1107            e.preventDefault();
1108            var $target = $(this).parent();
1109            $target.fadeOut(250, function() { $target.remove(); });
1110        });
1111        // Automagic removal after 5 secs
1112        setTimeout(function() {
1113            $('#msg_entrystatus').fadeOut(250).remove();
1114        }, 5000);
1115
1116        e.preventDefault();
1117    });
1118
1119    // Matching change event
1120    $('#entry_status').change(function() {
1121        $('#switch_entry_status').trigger('click');
1122    });
1123
1124    // Editor tools
1125    $('.wrap_selection').click(function() {
1126        var $el = $(this);
1127        var $tagOpen = $el.attr('data-tag-open');
1128        var $tagClose = $el.attr('data-tag-close');
1129        //var target = document.forms['serendipityEntry']['serendipity[' + $el.attr('data-tarea') + ']'];
1130        var target =  $('#'+serendipity.escapeBrackets($el.attr('data-tarea')));
1131        if ($el.hasClass('lang-html')) {
1132            var open = '<' + $tagOpen + '>';
1133            var close = '</' + $tagClose + '>';
1134        } else {
1135            var open = $tagOpen;
1136            var close = $tagClose;
1137        }
1138        serendipity.wrapSelection(target, open, close);
1139    });
1140
1141    $('.wrap_insimg').click(function() {
1142        var target =  $('#'+serendipity.escapeBrackets($(this).attr('data-tarea')));
1143        serendipity.wrapInsImage(target);
1144    });
1145
1146    $('.wrap_insurl').click(function() {
1147        var target =  $('#'+serendipity.escapeBrackets($(this).attr('data-tarea')));
1148        serendipity.wrapSelectionWithLink(target);
1149    });
1150
1151    $('.wrap_insmedia').click(function() {
1152        serendipity.openPopup('serendipity_admin.php?serendipity[adminModule]=media&serendipity[noBanner]=true&serendipity[noSidebar]=true&serendipity[noFooter]=true&serendipity[showMediaToolbar]=false&serendipity[showUpload]=true&serendipity[textarea]=' + $(this).attr('data-tarea'));
1153    });
1154
1155    // Entry metadata
1156    if($('#edit_entry_metadata').length > 0) {
1157        $('#toggle_metadata').click(function() {
1158            serendipity.toggle_collapsible($(this), '#meta_data');
1159        });
1160        if (localStorage !== null && localStorage.getItem("show_toggle_metadata") == "true") {
1161            $('#toggle_metadata').click();
1162        }
1163    }
1164
1165    // Show category selector
1166    {if $use_backendpopups || $force_backendpopups.categories}
1167        if($('#serendipityEntry').length > 0) {
1168            $('#select_category').click(function(e) {
1169                e.preventDefault();
1170
1171                if ($('#meta_data').hasClass('additional_info')) {
1172                    $('#toggle_metadata').click();
1173                }
1174
1175                serendipity.skipScroll($(this).attr('href'));
1176            });
1177        }
1178    {else}
1179        $('#meta_data #edit_entry_category').addClass('mfp-hide');
1180
1181        if($('#serendipityEntry').length > 0) {
1182            var btnText = '{$CONST.DONE}';
1183
1184            $('#select_category').magnificPopup({
1185                type: "inline",
1186                closeMarkup: '<button title="%title%" class="mfp-close" type="button">'+ btnText +'</button>',
1187                callbacks: {
1188                    open: function() {
1189                        // Accessibility helper
1190                        $('#edit_entry_category .form_check input[type="checkbox"]').attr('aria-hidden', 'true');
1191                        if(localStorage !== null && localStorage.cat_view_state == "compact") {
1192                            $('.mfp-content').addClass('compact_categories');
1193                            $('#toggle_cat_view').find('.icon-th').removeClass('icon-th').addClass('icon-th-list');
1194                        }
1195                    },
1196                    afterClose: function() {
1197                        // Accessibility helper
1198                        $('#edit_entry_category .form_check input[type="checkbox"]').attr('aria-hidden', 'false');
1199                        serendipity.catsList();
1200                    }
1201                }
1202            });
1203
1204            $('#cats_list').on('click', 'h3, li', function() {
1205                $('#select_category').trigger('click');
1206            });
1207        }
1208    {/if}
1209
1210    // Switch category view
1211    if($('#serendipityEntry').length > 0) {
1212        $('#toggle_cat_view').click(function() {
1213            var $el = $(this);
1214            var $target = $('.mfp-content');
1215
1216            if($target.hasClass('compact_categories')) {
1217                $target.removeClass('compact_categories');
1218                $el.find('.icon-th-list').removeClass('icon-th-list').addClass('icon-th');
1219                if (localStorage !== null) {
1220                    localStorage.cat_view_state = "hierarchical";
1221                }
1222            } else {
1223                $target.addClass('compact_categories');
1224                $el.find('.icon-th').removeClass('icon-th').addClass('icon-th-list');
1225                if (localStorage !== null) {
1226                    localStorage.cat_view_state = "compact";
1227                }
1228            }
1229        });
1230    };
1231
1232    // Show tag selector
1233    {if $use_backendpopups || $force_backendpopups.tags}
1234        if($('#serendipityEntry').length > 0) {
1235            $('#select_tags').click(function(e) {
1236                e.preventDefault();
1237
1238                if ($('#adv_opts').hasClass('additional_info')) {
1239                    $('#toggle_advanced').click();
1240                }
1241
1242                serendipity.skipScroll($(this).attr('href'));
1243            });
1244        }
1245    {else}
1246        $('#adv_opts #edit_entry_freetags').addClass('mfp-hide');
1247
1248        if($('#serendipityEntry').length > 0) {
1249            var btnText = '{$CONST.DONE}';
1250
1251            $('#select_tags').magnificPopup({
1252                type: "inline",
1253                closeMarkup: '<button title="%title%" class="mfp-close" type="button">'+ btnText +'</button>',
1254                callbacks: {
1255                    afterClose: function() {
1256                        serendipity.tagsList();
1257                    }
1258                }
1259            });
1260
1261            $('#backend_freetag_list > a').click(function(e) {
1262                e.preventDefault();
1263            });
1264
1265            $('#tags_list').on('click', 'h3, li', function() {
1266                $('#select_tags').trigger('click');
1267            });
1268        }
1269    {/if}
1270
1271    // Category live filter
1272    $('#categoryfilter').keyup(function() {
1273        serendipity.liveFilters($(this), '#edit_entry_category .form_check', 'label');
1274    });
1275
1276    // Oldie helper for selecting categories
1277    $('#edit_entry_category .form_check input').change(function(e) {
1278        var $el = $(this);
1279
1280        if($el.is(":checked")) {
1281            $el.siblings('label').addClass('selected');
1282        } else {
1283            $el.siblings('label').removeClass('selected');
1284        }
1285    });
1286
1287    // Plugins live filter
1288    $('#pluginfilter').keyup(function() {
1289        serendipity.liveFilters($(this), '.plugins_installable > li', '.plugin_features');
1290    });
1291
1292    // Reset button for live filters
1293    $('.reset_livefilter').click(function() {
1294        var target = '#' + $(this).attr('data-target');
1295        $(target).val('').keyup();
1296    });
1297
1298    // Advanced options
1299    if($('#advanced_options').length > 0) {
1300        $('#toggle_advanced').click(function() {
1301            serendipity.toggle_collapsible($(this), '#adv_opts');
1302        });
1303        if (localStorage !== null && localStorage.getItem("show_toggle_advanced") == "true") {
1304            $('#toggle_advanced').click();
1305        }
1306    }
1307
1308    // Entry preview
1309    $('.entry_preview').click(function() {
1310        document.forms['serendipityEntry'].elements['serendipity[preview]'].value='true';
1311    });
1312
1313    // Collapsible configuration elements
1314    if($('#serendipity_config_options, #serendipity_category, #image_directory_edit_form').length > 0) {
1315        var optsCollapsed = true;
1316
1317        $('.show_config_option').click(function(e) {
1318            var $el = $(this);
1319            if ($el.attr('href')) {
1320                var $toggled= $el.attr('href');
1321            } else {
1322                var $toggled = $el.data('href');
1323            }
1324
1325            serendipity.toggle_collapsible($el, $toggled);
1326            e.preventDefault();
1327        });
1328
1329        $('#show_config_all').click(function(e) {
1330            if ($(this).attr('href')) {
1331                var $container = $(this).attr('href');
1332            } else {
1333                var $container = $(this).data('href');
1334            }
1335            var $toggleIcon = $(this).find('span:first-child');
1336            var $toggleIcons = $($container).find('.show_config_option > span');
1337            var $toggleOption = $($container).find('.config_optiongroup');
1338            if(optsCollapsed) {
1339                $toggleIcons.removeClass('icon-right-dir').addClass('icon-down-dir');
1340                $toggleOption.removeClass('additional_info');
1341                $toggleIcon.removeClass('icon-right-dir').addClass('icon-down-dir');
1342                optsCollapsed = false;
1343            } else {
1344                $toggleIcons.removeClass('icon-down-dir').addClass('icon-right-dir');
1345                $toggleOption.addClass('additional_info');
1346                $toggleIcon.removeClass('icon-down-dir').addClass('icon-right-dir');
1347                optsCollapsed = true;
1348            }
1349            $(this).toggleClass('active');
1350            e.preventDefault();
1351        });
1352
1353        $('.show_config_option:not(.show_config_option_now)').click();
1354    }
1355
1356    $('.change_preview').change(function() {
1357        serendipity.change_preview($(this).attr('id'), $(this).attr('data-configitem'));
1358    });
1359
1360    $('.choose_media').click(function() {
1361        var configitem = $(this).parent().find('.change_preview').attr('id');
1362        serendipity.choose_media(configitem);
1363    });
1364
1365    $('.customfieldMedia').click(function() {
1366        var configitem = $(this).parent().find('textarea').attr('id');
1367        serendipity.choose_media(configitem);
1368    });
1369
1370    $('#tree_toggle_all').click(function(e) {
1371        e.preventDefault();
1372        serendipity.treeToggleAll();
1373    });
1374
1375    // Comments
1376    $('.comments_delete, .comments_multidelete, .build_cache').click(function() {
1377        var $msg = $(this).attr('data-delmsg');
1378        return confirm($msg);
1379    });
1380
1381    $('.comments_reply').click(function(e) {
1382        e.preventDefault();
1383        serendipity.openPopup($(this).attr('href'));
1384    });
1385
1386    // Selection for multidelete
1387    $('.multidelete, .multiinsert').click(function() {
1388        var $el = $(this);
1389        serendipity.highlightComment($el.attr('data-multidelid'), $el.attr('checked'));
1390        var selectionAmount = $('.multidelete:checked, .multiinsert:checked').length;
1391        if (selectionAmount > 0) {
1392            $('#media_galleryinsert').fadeIn();
1393        } else {
1394            $('#media_galleryinsert').fadeOut();
1395        }
1396    });
1397
1398    // Also marks checkbox when header is clicked
1399    $('.media_file h3').on('click', function() {
1400        $(this).parent().find('.multiinsert').click();
1401    });
1402
1403    // When clicking the 'Insert all' button, check whether only one or multiple
1404    // images are selected
1405    $('#media_galleryinsert .image_insert').on('click', function(e) {
1406        var selectionAmount = $('.multidelete:checked, .multiinsert:checked').length;
1407        if (selectionAmount == 0) {
1408            e.preventDefault();
1409            return false;
1410        }
1411
1412        // Actually do a single insert
1413        if (selectionAmount == 1) {
1414            e.preventDefault();
1415            var actualTargetElement = $('.multidelete:checked, .multiinsert:checked').closest('.media_file').find('.media_file_preview a').first();
1416            // This below is a hack. Triggering actualTargetElement.click() does not work. Probably because MFP overrides click events within a modal popup.
1417            // So we need to do something dirty and directly access the location href. Don't tell my mom.
1418            location.href = actualTargetElement.attr('href');
1419            return false;
1420        }
1421    });
1422
1423    // hide gallery insert button until an image is selected
1424    $('#media_galleryinsert').hide();
1425
1426    // Invert checkboxes
1427    $('.invert_selection').click(function() {
1428        serendipity.invertSelection();
1429    });
1430
1431    // Go back one step
1432    $('.go_back').click(function() {
1433        history.go(-1);
1434    });
1435
1436    // Add media db upload field
1437    $('#add_upload').click(function(e) {
1438        e.preventDefault();
1439        serendipity.addUploadField();
1440    });
1441
1442    // Check media db inputs
1443    $('.check_input').change(function() {
1444        serendipity.checkInputs();
1445    });
1446
1447    $('.check_inputs').click(function() {
1448        serendipity.checkInputs();
1449    });
1450
1451    // Extra function for media db download
1452    $('#imageurl').change(function() {
1453        sourceval = serendipity.getfilename(document.getElementById('imageurl').value);
1454
1455        if (sourceval.length > 0) {
1456            document.getElementById('imagefilename').value = sourceval;
1457        }
1458    });
1459
1460    // Dashboard bookmarklet hint
1461    $('.s9y_bookmarklet').click(function(e) {
1462        e.preventDefault();
1463        alert('{$CONST.FURTHER_LINKS_S9Y_BOOKMARKLET_DESC}');
1464    });
1465
1466    // Show media file info, template info, label info or filters
1467    $('.media_show_info, .template_show_info, .filters_toolbar li > a, .toggle_info').click(function(e) {
1468        var $el = $(this);
1469        if ($el.attr('href')) {
1470            $($el.attr('href')).toggleClass('additional_info');
1471        } else {
1472            $($el.data('href')).toggleClass('additional_info');
1473        }
1474        if (mq_small) {
1475            $el.closest('.has_info').toggleClass('info_expanded');
1476        }
1477        $el.toggleClass('active');
1478        e.preventDefault();
1479    });
1480
1481    // Show further links
1482    {if $use_backendpopups || $force_backendpopups.links}
1483        if($('#dashboard').length > 0) {
1484            $('.toggle_links').click(function(e) {
1485                $('#s9y_links').toggleClass('mfp-hide');
1486
1487                e.preventDefault();
1488                serendipity.skipScroll($(this).attr('href'));
1489            });
1490        }
1491    {else}
1492        if($('#dashboard').length > 0) {
1493            $('.toggle_links').magnificPopup({ type: "inline" });
1494        }
1495    {/if}
1496
1497    // Media file actions
1498    {if $use_backendpopups || $force_backendpopups.images}
1499    $('.media_fullsize').click(function(e) {
1500        e.preventDefault();
1501        var $el = $(this);
1502        var filepath = $el.attr('href');
1503        var pwidth = $el.attr('data-pwidth');
1504        var pheight = $el.attr('data-pheight');
1505        var ptop = (screen.height - pheight)/2;
1506        var pleft = (screen.width - pwidth)/2;
1507        window.open(filepath, 'Zoom', 'height='+pheight+',width='+pwidth+',top='+ptop+',left='+pleft+',toolbar=no,menubar=no,location=no,resize=1,resizable=1,scrollbars=yes');
1508    });
1509    {else}
1510    if ($('.media_fullsize').length > 0) {
1511        $('.media_fullsize').magnificPopup({ type:'image' });
1512    }
1513    {/if}
1514
1515    $('.media_rename').click(function(e) {
1516        e.preventDefault();
1517        var $el = $(this);
1518        serendipity.rename($el.attr('data-fileid'), $el.attr('data-filename'));
1519    });
1520
1521    $('.media_delete').click(function(e) {
1522        e.preventDefault();
1523        var $el = $(this);
1524        serendipity.deleteFromML($el.attr('data-fileid'), $el.attr('data-filename'));
1525    });
1526
1527
1528    $('#media_crop').click(function(e) {
1529        window.open($(this).attr('href'), 'ImageCrop', 'width=800,height=600,toolbar=no,scrollbars=1,scrollbars,resize=1,resizable=1').focus();
1530        e.preventDefault();
1531    });
1532
1533    $('.add_keyword').click(function(e) {
1534        serendipity.AddKeyword($(this).attr('data-keyword'));
1535        e.preventDefault();
1536    });
1537
1538    // Confirm media scale
1539    $('.image_scale').click(function() {
1540        if (confirm('{$CONST.REALLY_SCALE_IMAGE}')) document.serendipityScaleForm.submit();
1541    });
1542
1543    // Media scale change events
1544    $('#resize_width').change(function() {
1545        serendipity.rescale('width' , $(this).val());
1546    });
1547
1548    $('#resize_height').change(function() {
1549        serendipity.rescale('height' , $(this).val());
1550    });
1551
1552    // Show extended comment
1553    $('.toggle_comment_full').click(function(e) {
1554        var $el = $(this);
1555        if ($el.attr('href')) {
1556            var $toggled = $($el.attr('href'));
1557        } else {
1558            var $toggled = $($el.data('href'));
1559        }
1560
1561        serendipity.toggle_collapsible($el, $toggled);
1562
1563        $toggled.prev().toggleClass('additional_info');
1564        e.preventDefault();
1565    });
1566
1567    // MediaDB-Filter-Buttons should react instantly
1568    $('input[name="serendipity[filter][fileCategory]"]').on('change', function() {
1569        $('#media_library_control').submit();
1570    });
1571
1572    // Clone form submit buttons
1573    $('#sort_entries > .form_buttons').clone().appendTo('#filter_entries');
1574    $('#media_pane_sort > .form_buttons').clone().appendTo('#media_pane_filter');
1575
1576    // Clone pagination - this could become a function, which should also
1577    // check if the cloned pagination already exists
1578    $('.media_pane .pagination').clone().prependTo('.media_pane');
1579    $('.comments_pane .pagination').clone().prependTo('.comments_pane');
1580    $('.entries_pane .pagination').clone().prependTo('.entries_pane');
1581    $('.karma_pane .pagination').clone().prependTo('.karma_pane');
1582
1583    // close comment reply on button click
1584    if ($('#comment_replied').length > 0) {
1585        $('#comment_replied').click(function() {
1586            serendipity.closeCommentPopup();
1587        });
1588    }
1589
1590    // UI-flow when selecting multiple images in ML upload
1591    if ($('.uploadform_userfile').length > 0) {
1592        $('.uploadform_userfile').change(function() {
1593            if ($(this).get(0).files.length > 1) {
1594                $(this).parent().siblings(':first').fadeOut();
1595                $(this).parent().siblings(':first').find('input').val('');
1596                $(this).attr('name', $(this).attr('name') + '[]');
1597            }
1598            if ($(this).get(0).files.length == 1) {
1599                $(this).parent().siblings(':first').fadeIn();
1600            }
1601        });
1602    }
1603
1604    // update all button in plugin upgrade menu
1605    if ($('#updateAll').length > 0) {
1606        $('#updateAll').click(function() {
1607            serendipity.updateAll();
1608        });
1609    }
1610
1611    // minify images before upload, approach taken from https://github.com/joelvardy/javascript-image-upload/
1612    {if {serendipity_getConfigVar key='uploadResize'} && ({serendipity_getConfigVar key='maxImgWidth'} > 0 || {serendipity_getConfigVar key='maxImgHeight'} > 0)}
1613        if ($('#uploadform').length > 0) {
1614            $('input[name="go_properties"]').hide();
1615            var progressIcon = document.createElement('span');
1616            progressIcon.className = 'uploadIcon icon-info-circled';
1617            errorIcon = document.createElement('span');
1618            errorIcon.className = 'uploadIcon icon-attention-circled';
1619            successIcon = document.createElement('span');
1620            successIcon.className = 'uploadIcon icon-ok-circled';
1621            $('#uploadform').submit(function(event) {
1622                if (! $('#imageurl').val()) {
1623                    event.preventDefault();
1624                    $('#uploadform .check_inputs').attr('disabled', true);
1625                    var sendDataToML = function(data, progressContainer, progress) {
1626                        $.ajax({
1627                            type: 'post',
1628                            url: $('#uploadform').attr('action'),
1629                            data: data,
1630                            cache: false,
1631                            processData: false,
1632                            contentType: false,
1633                            xhr: function() {
1634                                var xhr = $.ajaxSettings.xhr();
1635                                xhr.upload.onprogress = function(e) {
1636                                    if (e.lengthComputable) {
1637                                        progress.value = e.loaded / e.total * 100;
1638                                    }
1639                                };
1640                                return xhr;
1641                            }
1642                        }).done(function(data) {
1643                            progress.value = 100;
1644                            progressContainer.className = "msg_success";
1645                            $(progressContainer).find('.uploadIcon').replaceWith(successIcon.cloneNode(true));
1646                        }).fail(function(data) {
1647                            progressContainer.className = "msg_error";
1648                            progress.disabled = true;
1649                            progressContainer.innerHTML += "{$CONST.ERROR_UNKNOWN_NOUPLOAD}";
1650                            $(progressContainer).find('.uploadIcon').replaceWith(errorIcon.cloneNode(true));
1651                        }).always(function() {
1652                            if ($('#ml_link').length == 0) {
1653                                var mlLink = document.createElement('a');
1654                                mlLink.id = "ml_link";
1655                                mlLink.className = "button_link";
1656                                mlLink.href = $('#uploadform').attr('action');
1657                                mlLink.innerHTML = "{$CONST.MEDIA_LIBRARY}";
1658                                $(mlLink).hide();
1659                                $('.form_buttons').prepend(mlLink);
1660                                $(mlLink).fadeIn();
1661                            }
1662                            $('#uploadform .check_inputs').removeAttr('disabled');
1663                        });
1664                    };
1665                    $('.uploadform_userfile').each(function() {
1666                        var files = this.files;
1667                        for (var i = 0; i < files.length; i++) {
1668                            var reader = new FileReader();
1669                            reader.file = files[i];
1670                            reader.onload = function(readerEvent) {
1671                                var image = new Image();
1672                                var file = this.file;
1673                                var type = file.type;
1674                                var data = new FormData();
1675                                data.append('serendipity[action]', 'admin');
1676                                data.append('serendipity[adminModule]', 'media');
1677                                data.append('serendipity[adminAction]', 'add');
1678                                data.append('serendipity[token]', $('input[name*="serendipity[token]"]').val());
1679                                data.append('serendipity[target_filename][1]', $('input[name*="serendipity[target_filename][1]"]').val());
1680                                data.append('serendipity[target_directory][1]', $('select[name*="serendipity[target_directory][1]"]').val());
1681                                var progress = document.createElement('progress');
1682                                var progressContainer = document.createElement('span');
1683                                progressContainer.className = 'msg_notice';
1684                                progress.max = 100;
1685                                progress.value = 0;
1686                                $(progressContainer).append(progressIcon);
1687                                progressContainer.innerHTML += file.name + ": "
1688                                $(progressContainer).append(progress);
1689                                $('.form_buttons').append(progressContainer);
1690                                if (type.substring(0, 6) == "image/") {
1691                                    image.onload = function (imageEvent) {
1692                                        var canvas = document.createElement('canvas'),
1693                                            max_width = {if {serendipity_getConfigVar key='maxImgWidth'}}{serendipity_getConfigVar key='maxImgWidth'}{else}0{/if},
1694                                            max_height = {if {serendipity_getConfigVar key='maxImgHeight'}}{serendipity_getConfigVar key='maxImgHeight'}{else}0{/if},
1695                                            width = image.width,
1696                                            height = image.height;
1697
1698                                        if (max_width > 0 && width > max_width) {
1699                                            height *= max_width / width;
1700                                            width = max_width;
1701                                        }
1702                                        if (max_height > 0 && height > max_height) {
1703                                            width  *= max_height / height;
1704                                            height = max_height;
1705                                        }
1706
1707                                        canvas.width = width;
1708                                        canvas.height = height;
1709                                        canvas.getContext('2d').drawImage(image, 0, 0, width, height);
1710
1711                                        if (type == "image/bmp") {
1712                                            {* bmp is not supported *}
1713                                            type = "image/png";
1714                                            data.append('serendipity[target_filename][1]', file.name.replace('.bmp', '.png'));
1715                                        }
1716                                        canvas.toBlob(function(blob) {
1717                                            data.append('serendipity[userfile][1]', blob, file.name);
1718                                            sendDataToML(data, progressContainer, progress);
1719                                        }, type);
1720                                    }
1721                                    image.src = readerEvent.target.result;
1722                                } else {
1723                                    data.append('serendipity[userfile][1]', file, file.name);
1724                                    sendDataToML(data, progressContainer, progress);
1725                                }
1726                            }
1727                            reader.readAsDataURL(reader.file);
1728                        }
1729                    });
1730                }
1731            });
1732        }
1733    {/if}
1734
1735    if ($('#serendipity_only_path').length > 0) {
1736        $('#serendipity_only_path').change(function() {
1737            serendipity.SetCookie('serendipity_only_path', $('#serendipity_only_path').val());
1738        });
1739    }
1740
1741    if ($('.image_move').length > 0) {
1742        $('.image_move').removeClass('hidden');
1743        $('.image_move').magnificPopup({
1744            type: 'inline',
1745        });
1746        $('#move-popup form').submit(function(e) {
1747            e.preventDefault();
1748            $('#newDir').val($('#move-popup form select').val());
1749            $.magnificPopup.close();
1750            $('input[name="toggle_move"]').click();
1751        });
1752    }
1753
1754    // reopen detail element after spamblock action
1755    if ($('#serendipity_comments_list').length > 0 && window.location.hash && $('#' + window.location.hash.replace('#', '')).length > 0) {
1756        $('#' + window.location.hash.replace('#', '')).find(".toggle_info").click();
1757    }
1758
1759    // ajaxify image rotate, solving cache issue
1760    $('.media_rotate_right,.media_rotate_left').click(function(e) {
1761        e.preventDefault();
1762        var $rotateButton = $(this)
1763        $.get($rotateButton.attr('href'), function() {
1764            serendipity.reloadImage($rotateButton.closest('.media_file').find('img'));
1765        });
1766    })
1767
1768    // Tabs
1769    if($('.tabs').length > 0) {
1770        var currTabText = '{$CONST.CURRENT_TAB}';
1771
1772        $('.tabs').accessibleTabs({
1773            wrapperClass: 'tabcontent',
1774            currentClass: 'on',
1775            tabhead: 'h3',
1776            tabheadClass: 'visuallyhidden',
1777            tabbody: '.panel',
1778            fx: 'fadeIn',
1779            currentInfoText: currTabText,
1780            currentInfoClass: 'visuallyhidden',
1781            syncheights: false,
1782            saveState: true
1783        });
1784    }
1785
1786    // Drag 'n' drop
1787    if (! Modernizr.touch){
1788        function getDragdropConfiguration(group) {
1789            return {
1790                containerSelector: '.pluginmanager_container',
1791                group: group,
1792                handle: '.pluginmanager_grablet',
1793
1794                onDrop: function ($item, container, _super) {
1795                    var placement = $item.parents('.pluginmanager_container').data("placement");
1796                    $item.find('select[name$="placement]"]').val(placement);
1797                    $item.removeClass('dragged').removeAttr('style');
1798                    $('body').removeClass('dragging');
1799                    $.autoscroll.stop();
1800                },
1801                onDragStart: function ($item, container, _super) {
1802                    $.autoscroll.init();
1803                    $item.css({
1804                        height: $item.height(),
1805                        width: $item.width()
1806                    });
1807                    $item.addClass('dragged');
1808                    $('body').addClass('dragging');
1809                }
1810            }
1811        }
1812
1813        $('.pluginmanager_sidebar .pluginmanager_container').sortable(getDragdropConfiguration('plugins_sidebar'));
1814        $('.pluginmanager_event .pluginmanager_container').sortable(getDragdropConfiguration('plugins_event'));
1815        $('.configuration_group .pluginmanager_container').sortable(getDragdropConfiguration('plugins_event'));
1816    }
1817
1818    // Equal Heights
1819    $(window).load(function() {
1820        if(!Modernizr.flexbox) {
1821            if (mq_small) {
1822                serendipity.sync_heights();
1823            }
1824        }
1825    });
1826
1827    // Make sure plugin list heights are recalculated when switching tabs
1828    $('#pluginlist_tabs a').click(function() {
1829        if(!Modernizr.flexbox) {
1830            if (mq_small) {
1831                serendipity.sync_heights();
1832            }
1833        }
1834    });
1835});
1836
1837// This is kept for older plugins. Use of $(document).ready() is encouraged.
1838// At some point, these will be removed.
1839addLoadEvent = function(func) {
1840    var oldonload = window.onload;
1841    if (typeof window.onload != 'function') {
1842        window.onload = func;
1843    } else {
1844        window.onload = function() {
1845            oldonload();
1846            func();
1847        }
1848    }
1849}
1850
1851// Several plugins use this in the global scope. Those API functions are
1852// vital, so they reference to our new serendipity scope. This global
1853// scope is deprecated and subject to removal in the future.
1854serendipity_imageSelector_addToBody = function(block, textarea) {
1855    return serendipity.serendipity_imageSelector_addToBody(block, textarea);
1856}
1857
1858serendipity_imageSelector_done = function(textarea) {
1859    return serendipity.serendipity_imageSelector_done(textarea);
1860}
1861
1862serendipity_imageSelector_addToElement = function(str, id) {
1863    return serendipity.serendipity_imageSelector_addToElement(str, id);
1864}
1865