Увага: Після публікування слід очистити кеш браузера, щоб побачити зміни.

  • Firefox / Safari: тримайте Shift, коли натискаєте Оновити, або натисніть Ctrl-F5 чи Ctrl-Shift-R (⌘-R на Apple Mac)
  • Google Chrome: натисніть Ctrl-Shift-R (⌘-Shift-R на Apple Mac)
  • Edge: тримайте Ctrl, коли натискаєте Оновити, або натисніть Ctrl-F5.
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },   hasProp = {}.hasOwnProperty;  if (typeof unsafeWindow !== "undefined" && unsafeWindow !== null) {   window.$ = unsafeWindow.$;   window.etSubsets = unsafeWindow.etSubsets; }  window.rast = {   arrayMove: function(array, from, to) {     return array.splice(to, 0, array.splice(from, 1)[0]);   },   $getTextarea: function() {     return $('#wpTextbox1');   },   $getCurrentInput: function() {     return $(document.activeElement);   },   insertion: {     replaceSpecsymbols: function(s, symbols, toFunc) {       var c, i, res;       res = '';       c = void 0;       i = 0;       while (i < s.length) {         c = s.charAt(i);         if (rast.insertion.isEscaped(s, i)) {           res += c;         } else if (symbols.indexOf(c) > -1) {           res += toFunc(c);         } else {           res += c;         }         i++;       }       return res;     },     isEscaped: function(s, i) {       var escSymbols;       escSymbols = 0;       i--;       while (i > -1 && s.charAt(i) === '\\') {         escSymbols++;         i--;       }       return escSymbols % 2 === 1;     },     indexOfUnescaped: function(s, symbol) {       var i, index;       index = -1;       i = 0;       while (i < s.length) {         if (s.charAt(i) === symbol && !rast.insertion.isEscaped(s, i)) {           index = i;           break;         }         i++;       }       return index;     }   },   installJQueryPlugins: function() {     $.fn.extend({       throbber: function(visibility, position, size) {         var $elem, $throbber;         $elem = $(this);         $throbber = $elem.data('rastThrobber');         if ($throbber) {           $throbber.toggle(visibility);         } else {           size = size || '20px';           $throbber = $('<img>');           $throbber.attr('src', 'https://upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif');           $throbber.css('width', size);           $throbber.css('height', size);           $elem.data('rastThrobber', $throbber);           $elem[position]($throbber);           $elem.addClass('withRastThrobber');         }         return $elem;       },       asnavSelect: function(id) {         var $tabs, first, tabContent;         $tabs = $(this);         $tabs.find('.asnav-content').hide();         $tabs.find('.asnav-tabs .asnav-selectedtab').removeClass('asnav-selectedtab');         tabContent = $tabs.find('.asnav-tabs [data-contentid="' + id + '"]:first');         if (tabContent.length) {           tabContent.addClass('asnav-selectedtab');           return $tabs.find('#' + id).show();         } else {           first = $tabs.find('.asnav-tabs [data-contentid]:first').addClass('asnav-selectedtab');           return $tabs.find('#' + first.attr('data-contentid')).show();         }       },       etMakeTabs: function(activeTabId) {         var selectFunc, tabs;         tabs = $(this);         selectFunc = function(a) {           var $a;           $a = $(a);           tabs.asnavSelect($a.attr('data-contentid'));           return $a.trigger('asNav:select', $a.attr('data-contentid'));         };         tabs.on('click', '.asnav-tabs [data-contentid]', function() {           return selectFunc(this);         });         return tabs.asnavSelect(activeTabId);       }     });     return $.fn.extend({       insertTag: function(beginTag, endTag) {         return this.each(function() {           var SelReplace, pos, sel;           SelReplace = void 0;           pos = void 0;           sel = void 0;           SelReplace = function(s) {             return rast.insertion.replaceSpecsymbols(s, '\\$', function(c) {               if (c === '\\') {                 return '';               } else if (c === '$') {                 return sel;               }             });           };           $(this).focus();           sel = $(this).textSelection('getSelection');           beginTag = SelReplace(beginTag);           endTag = endTag ? SelReplace(endTag) : '';           $(this).textSelection('encapsulateSelection', {             pre: beginTag || '',             peri: '',             post: endTag || '',             replace: true           });           if (endTag && sel !== '') {             pos = $(this).textSelection('getCaretPosition');             return $(this).textSelection('setSelection', {               start: pos - endTag.length             });           }         });       },       setSelection: function(text) {         return this.textSelection('encapsulateSelection', {           post: text,           replace: true         });       },       getSelection: function(text) {         return this.textSelection('getSelection');       }     });   },   name: function(constructor) {     return 'rast.' + constructor.name;   },   clone: (function() {     var clone;      /**      * Clones (copies) an Object using deep copying.     #      * This function supports circular references by default, but if you are certain      * there are no circular references in your object, you can save some CPU time      * by calling clone(obj, false).     #      * Caution: if `circular` is false and `parent` contains circular references,      * your program may enter an infinite loop and crash.     #      * @param `parent` - the object to be cloned      * @param `circular` - set to true if the object to be cloned may contain      *    circular references. (optional - true by default)      * @param `depth` - set to a number if the object is only to be cloned to      *    a particular depth. (optional - defaults to Infinity)      * @param `prototype` - sets the prototype to be used when cloning an object.      *    (optional - defaults to parent prototype).      */     var __getRegExpFlags, __isArray, __isDate, __isRegExp, __objToStr, clone;     clone = function(parent, circular, depth, prototype) {       var _clone, allChildren, allParents, filter, useBuffer;       filter = void 0;       _clone = function(parent, depth) {         var attrs, child, i, index, proto;         if (parent === null) {           return null;         }         if (depth === 0) {           return parent;         }         child = void 0;         proto = void 0;         if (typeof parent !== 'object') {           return parent;         }         if (clone.__isArray(parent)) {           child = [];         } else if (clone.__isRegExp(parent)) {           child = new RegExp(parent.source, __getRegExpFlags(parent));           if (parent.lastIndex) {             child.lastIndex = parent.lastIndex;           }         } else if (clone.__isDate(parent)) {           child = new Date(parent.getTime());         } else if (useBuffer && Buffer.isBuffer(parent)) {           child = new Buffer(parent.length);           parent.copy(child);           return child;         } else {           if (typeof prototype === 'undefined') {             proto = Object.getPrototypeOf(parent);             child = Object.create(proto);           } else {             child = Object.create(prototype);             proto = prototype;           }         }         if (circular) {           index = allParents.indexOf(parent);           if (index !== -1) {             return allChildren[index];           }           allParents.push(parent);           allChildren.push(child);         }         for (i in parent) {           attrs = void 0;           if (proto) {             attrs = Object.getOwnPropertyDescriptor(proto, i);           }           if (attrs && attrs.set === null) {             continue;           }           child[i] = _clone(parent[i], depth - 1);         }         return child;       };       if (typeof circular === 'object') {         depth = circular.depth;         prototype = circular.prototype;         filter = circular.filter;         circular = circular.circular;       }       allParents = [];       allChildren = [];       useBuffer = typeof Buffer !== 'undefined';       if (typeof circular === 'undefined') {         circular = true;       }       if (typeof depth === 'undefined') {         depth = Infinity;       }       return _clone(parent, depth);     };     __objToStr = function(o) {       return Object.prototype.toString.call(o);     };     __isDate = function(o) {       return typeof o === 'object' && __objToStr(o) === '[object Date]';     };     __isArray = function(o) {       return typeof o === 'object' && __objToStr(o) === '[object Array]';     };     __isRegExp = function(o) {       return typeof o === 'object' && __objToStr(o) === '[object RegExp]';     };     __getRegExpFlags = function(re) {       var flags;       flags = '';       if (re.global) {         flags += 'g';       }       if (re.ignoreCase) {         flags += 'i';       }       if (re.multiline) {         flags += 'm';       }       return flags;     };     'use strict';      /**      * Simple flat clone using prototype, accepts only objects, usefull for property      * override on FLAT configuration object (no nested props).     #      * USE WITH CAUTION! This may not behave as you wish if you do not know how this      * works.      */     clone.clonePrototype = function(parent) {       var c;       if (parent === null) {         return null;       }       c = function() {};       c.prototype = parent;       return new c;     };     clone.__objToStr = __objToStr;     clone.__isDate = __isDate;     clone.__isArray = __isArray;     clone.__isRegExp = __isRegExp;     clone.__getRegExpFlags = __getRegExpFlags;     return clone;   })(),   focusWithoutScroll: function(elem) {     var x, y;     x = void 0;     y = void 0;     x = void 0;     y = void 0;     if (typeof window.pageXOffset !== 'undefined') {       x = window.pageXOffset;       y = window.pageYOffset;     } else if (typeof window.scrollX !== 'undefined') {       x = window.scrollX;       y = window.scrollY;     } else if (document.documentElement && typeof document.documentElement.scrollLeft !== 'undefined') {       x = document.documentElement.scrollLeft;       y = document.documentElement.scrollTop;     } else {       x = document.body.scrollLeft;       y = document.body.scrollTop;     }     elem.focus();     if (typeof x !== 'undefined') {       return setTimeout((function() {         window.scrollTo(x, y);       }), 100);     }   },   processSelection: function(txtFunc) {     var $textarea, txt;     $textarea = rast.$getTextarea();     txt = $textarea.getSelection();     return $textarea.setSelection(txtFunc(txt));   },   perLineReplace: function(str, regex, to) {     var i, len;     str = str.split('\n');     len = str.length;     i = 0;     while (i < len) {       str[i] = str[i].replace(regex, to);       i += 1;     }     return str.join('\n');   },   linkifyList: function(s) {     return rast.perLineReplace(s, /[^*;#—\s,][^*\.#—;,]+/g, '[[$&]]');   },   simpleList: function(s) {     return rast.perLineReplace(s, /(([\*#]*)\s*)(.+)/g, '*$2 $3');   },   numericList: function(s) {     return rast.perLineReplace(s, /(([\*#]*)\s*)(.+)/g, '#$2 $3');   },   dot: '·п',   searchAndReplace: {     getReplaceForm: function() {       return '\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div id="et-replace-message">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div id="et-replace-nomatch">Нема збігів</div>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div id="et-replace-success">Заміни виконано</div>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div id="et-replace-emptysearch">Вкажіть рядок до пошуку</div>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div id="et-replace-invalidregex">Неправильний регулярний вираз</div>\u0009\u0009\u0009\u0009\u0009\u0009\u0009</div>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span class="et-field-wrapper">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<label for="et-replace-search" style="float: left; min-width: 6em;">Шукати</label>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span style="display: block; overflow: hidden;">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009  <input type="text" id="et-replace-search" style="width: 100%;"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div style="clear: both;"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span class="et-field-wrapper">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<label for="et-replace-replace" style="float: left; min-width: 6em;">Заміна</label>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span style="display: block; overflow: hidden;">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009  <input type="text" id="et-replace-replace" style="width: 100%;"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<div style="clear: both;"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<input id="et-tool-replace-button-findnext" type="button" value="Шукати" />\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<input id="et-tool-replace-button-replace" type="button" value="Замінити" />\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<input id="et-tool-replace-button-replaceall" type="button" value="Замінити все" />\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span class="et-field-wrapper">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<input type="checkbox" id="et-replace-case"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<label for="et-replace-case">Враховувати регістр</label>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<span class="et-field-wrapper">\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<input type="checkbox" id="et-replace-regex"/>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009<label for="et-replace-regex">Регулярний вираз</label>\u0009\u0009\u0009\u0009\u0009\u0009\u0009\u0009</span>\u0009\u0009\u0009';     },     replaceFormInit: function() {       rast.searchAndReplace.offset = 0;       rast.searchAndReplace.matchIndex = 0;       $(document).off('click', '#et-tool-replace-button-findnext').on('click', '#et-tool-replace-button-findnext', function(e) {         return rast.searchAndReplace.doSearchReplace('find');       });       $(document).off('click', '#et-tool-replace-button-replace').on('click', '#et-tool-replace-button-replace', function(e) {         return rast.searchAndReplace.doSearchReplace('replace');       });       $(document).off('click', '#et-tool-replace-button-replaceall').on('click', '#et-tool-replace-button-replaceall', function(e) {         return rast.searchAndReplace.doSearchReplace('replaceAll');       });       return $('#et-replace-nomatch, #et-replace-success,\u0009\u0009\u0009 #et-replace-emptysearch, #et-replace-invalidregex').hide();     },     doSearchReplace: function(mode) {       var $textarea, actualReplacement, context, e, end, flags, i, index, isRegex, match, matchCase, newText, offset, regex, replaceStr, searchStr, start, text, textRemainder;       offset = void 0;       textRemainder = void 0;       regex = void 0;       index = void 0;       i = void 0;       start = void 0;       end = void 0;       $('#et-replace-nomatch, #et-replace-success,\u0009\u0009\u0009 #et-replace-emptysearch, #et-replace-invalidregex').hide();       searchStr = $('#et-replace-search').val();       if (searchStr === '') {         $('#et-replace-emptysearch').show();         return;       }       replaceStr = $('#et-replace-replace').val();       flags = 'm';       matchCase = $('#et-replace-case').is(':checked');       if (!matchCase) {         flags += 'i';       }       isRegex = $('#et-replace-regex').is(':checked');       if (!isRegex) {         searchStr = mw.RegExp.escape(searchStr);       }       if (mode === 'replaceAll') {         flags += 'g';       }       try {         regex = new RegExp(searchStr, flags);       } catch (_error) {         e = _error;         $('#et-replace-invalidregex').show();         return;       }       $textarea = rast.$getTextarea();       text = $textarea.textSelection('getContents');       match = false;       if (mode !== 'replaceAll') {         if (mode === 'replace') {           offset = rast.searchAndReplace.matchIndex;         } else {           offset = rast.searchAndReplace.offset;         }         textRemainder = text.substr(offset);         match = textRemainder.match(regex);       }       if (!match) {         offset = 0;         textRemainder = text;         match = textRemainder.match(regex);       }       if (!match) {         $('#et-replace-nomatch').show();         return;       }       if (mode === 'replaceAll') {         newText = text.replace(regex, replaceStr);         $textarea.select().textSelection('encapsulateSelection', {           'peri': newText,           'replace': true         });         $('#et-replace-success').text('Здійснено замін: ' + match.length).show();         rast.searchAndReplace.offset = 0;         return rast.searchAndReplace.matchIndex = 0;       } else {         if (mode === 'replace') {           actualReplacement = void 0;           if (isRegex) {             actualReplacement = match[0].replace(regex, replaceStr);           } else {             actualReplacement = replaceStr;           }           if (match) {             $textarea.textSelection('encapsulateSelection', {               'peri': actualReplacement,               'replace': true             });             text = $textarea.textSelection('getContents');           }           offset = offset + match[0].length + actualReplacement.length;           textRemainder = text.substr(offset);           match = textRemainder.match(regex);           if (match) {             start = offset + match.index;             end = start + match[0].length;           } else {             textRemainder = text;             match = textRemainder.match(regex);             if (match) {               start = match.index;               end = start + match[0].length;             } else {               start = 0;               end = 0;             }           }         } else {           start = offset + match.index;           end = start + match[0].length;         }         rast.searchAndReplace.matchIndex = start;         $textarea.textSelection('setSelection', {           'start': start,           'end': end         });         $textarea.textSelection('scrollToCaretPosition');         rast.searchAndReplace.offset = end;         context = rast.searchAndReplace.context;         return $textarea[0].focus();       }     }   },   ieVersion: function() {     var all, div, v;     v = 3;     div = document.createElement('div');     all = div.getElementsByTagName('i');     while ((div.innerHTML = '<!--[if gt IE ' + ++v + ']><i></i><![endif]-->', all[0])) {       0;     }     if (v > 4) {       return v;     } else {       return void 0;     }   } };  rast.PanelDrawer = (function() {   function PanelDrawer($panel1, subsetWrapper1, index1, mode1, subsets1, eventsHandler) {     this.$panel = $panel1;     this.subsetWrapper = subsetWrapper1;     this.index = index1;     this.mode = mode1;     this.subsets = subsets1;     this.eventsHandler = eventsHandler;   }    PanelDrawer.prototype.draw = function() {     var generateMethod;     if (this.mode === 'edit') {       return this.drawEditMode();     } else if (this.mode === 'view') {       generateMethod = 'generateHtml';       return this.generateHtml(this.$panel, this.subsetWrapper.slots, generateMethod);     }   };    PanelDrawer.prototype.sortableSlots = function($slots) {     return $($slots).sortable({       delay: 150,       containment: $slots,       forceHelperSize: true,       forcePlaceholderSize: true,       items: '[data-id]',       start: function(event, ui) {         var copy;         return copy = $(ui.item[0].outerHTML).clone();       },       placeholder: {         element: function(copy, ui) {           return $('<span class="ui-state-highlight">' + copy[0].innerHTML + '</li>');         },         update: function() {}       },       receive: (function(_this) {         return function(event, ui) {           var index, newSlot, slotClass;           slotClass = eval(ui.item.attr('data-slot-class'));           index = $(event.target).data().sortable.currentItem.index();           newSlot = _this.subsets.addSlot(slotClass, _this.subsetWrapper, index);           return _this.eventsHandler.onSlotAdded(newSlot);         };       })(this),       update: (function(_this) {         return function(event, ui) {           var newSlotIndex, slot, slotId;           newSlotIndex = ui.item.index('[data-id]') - 1;           if (newSlotIndex < 0) {             return;           }           if (!$(ui.item).attr('data-id')) {             return;           }           slotId = parseInt($(ui.item).attr('data-id'));           slot = _this.subsets.slotById(slotId);           _this.rearrangeSlot(slot, newSlotIndex);           return _this.updatePreview();         };       })(this),       revert: true     });   };    PanelDrawer.prototype.drawEditMode = function() {     var $nameInput, $nameInputContainer, $nameLabel, $panelRemoveButton, $preview, $previewContent, $removeIcon, $slots, generateMethod;     $nameLabel = $('<label class="panelNameLabel">Назва:</label>');     $nameInputContainer = $('<div>');     $nameInputContainer.append($nameLabel);     $nameInput = $('<input type="text">').val(this.subsetWrapper.caption);     $nameInput.change({       subsetWrapper: this.subsetWrapper     }, this.eventsHandler.onTabNameChanged);     $nameInputContainer.append($nameInput);     $nameInputContainer.appendTo(this.$panel);     $panelRemoveButton = $('<span class="panelRemoveButton">Вилучити панель</span>');     $panelRemoveButton.click((function(_this) {       return function() {         return _this.eventsHandler.onRemoveSubsetClick(_this.subsetWrapper);       };     })(this));     $removeIcon = $('<span class="removeIcon">');     $nameInputContainer.append($panelRemoveButton);     $panelRemoveButton.append($removeIcon);     $slots = $('<div class="slots">');     this.$panel.append($slots);     generateMethod = 'generateEditHtml';     this.sortableSlots($slots);     if (!this.subsetWrapper.slots.length) {       $slots.append('<span>Щоб додати комірку, сюди перетягніть потрібний вид з бічної панелі.</span>');     } else {       this.generateHtml($slots, this.subsetWrapper.slots, generateMethod);     }     $preview = $('<div>').css('border-top', '1px solid color: #aaa').addClass('preview');     $preview.append($('<div>Попередній перегляд:</div>'));     $previewContent = $('<div class="content">');     this.generateHtml($previewContent, this.subsetWrapper.slots, 'generateHtml');     $preview.append($previewContent);     return this.$panel.append($preview);   };    PanelDrawer.prototype.generateHtml = function($slotsContainer, slots, generateMethod) {     var k, len1, results1, slot;     results1 = [];     for (k = 0, len1 = slots.length; k < len1; k++) {       slot = slots[k];       results1.push($slotsContainer.append(slot[generateMethod]()));     }     return results1;   };    PanelDrawer.prototype.updatePreview = function(subsetWrapper) {     var $previewContent;     $previewContent = this.$panel.find('.preview .content');     $previewContent.empty();     return this.generateHtml($previewContent, this.subsetWrapper.slots, 'generateHtml');   };    PanelDrawer.prototype.rearrangeSlot = function(slot, newSlotIndex) {     var slotIndex;     slotIndex = this.subsets.slotIndex(slot);     return rast.arrayMove(this.subsetWrapper.slots, slotIndex, newSlotIndex);   };    return PanelDrawer;  })();  rast.Drawer = (function() {   function Drawer() {}    Drawer.prototype.$editButtonIcon = function() {     return $('<div class="gear">');   };    Drawer.prototype.$editButton = function() {     var $editButton;     $editButton = this.$editButtonIcon();     $editButton.addClass('menuButton edit');     return $editButton.attr('title', 'Редагувати символи.');   };    Drawer.prototype.drawMenu = function() {     var $aboutLink, $cancelButton, $dot, $editButton, $menu, $persistButton, $resetButton, $saveButton;     $menu = $('<div class="rastMenu">');     $menu.addClass(this.mode);     if (this.mode === 'view') {       $editButton = this.$editButton();       $editButton.click(this.eventsHandler.onEditClick);       $menu.append($editButton);     } else if (this.mode === 'edit') {       $dot = function() {         return $('<span> · </span>');       };       $persistButton = $('<span class="menuButton">').attr('title', 'Символи буде збережено у Вашому особистому просторі. Для цього виконається редагування підсторінки від Вашого імени.');       $persistButton.text('зберегти на підсторінку').click(this.eventsHandler.onPersistClick);       $saveButton = $('<span class="menuButton">').attr('title', 'Зміни збережуться тільки на час редагування сторінки і втратяться після закриття або перевантаження сторінки.');       $saveButton.text('зберегти').click(this.eventsHandler.onSaveClick);       $cancelButton = $('<span class="menuButton">');       $cancelButton.text('скасувати').click(this.eventsHandler.onCancelClick).attr('title', 'Всі зміни цієї сесії редагування будуть відкинуті.');       $resetButton = $('<span class="menuButton">');       $resetButton.text('відновити звичаєві').click(this.eventsHandler.onResetClick).attr('title', 'Буде відновлено набір символів за промовчанням.');       $aboutLink = $("<a class=\"aboutLink\" target=\"_blank\" href=\"" + this.docLink + "\">про додаток</a>");       $menu.append($persistButton, $dot(), $saveButton, $dot(), $cancelButton, $dot(), $resetButton, $aboutLink);     }     return this.$container.append($menu);   };    Drawer.prototype.drawTab = function($container, text) {     var $a, $adiv;     $a = $('<a>');     $adiv = $('<div>');     $a.text(text);     $adiv.append($a);     $container.append($adiv);     return $adiv;   };    Drawer.prototype.drawTabs = function($container) {     var $adiv, i, id;     i = 0;     while (i < this.subsets.subsets.length) {       $adiv = this.drawTab($container, this.subsets.subsets[i].caption);       id = 'etTabContent' + i;       if (this.activeTab === id) {         $adiv.addClass('asnav-selectedtab');       }       $adiv.attr('data-contentid', id);       $adiv.click(this.eventsHandler.onTabClick);       i++;     }     return $container;   };    Drawer.prototype.drawNavigation = function() {     var $addNewdiv, $outline, $tabs;     $outline = $('<span>').addClass('asnav-tabs').addClass('specialchars-tabs');     $tabs = $('<div class="existingTabs">');     this.drawTabs($tabs);     $outline.append($tabs);     if (this.mode === 'edit') {       $addNewdiv = this.drawTab($outline, '+ панель');       $addNewdiv.addClass('newPanelButton');       $addNewdiv.attr('title', 'Додати нову панель');       $addNewdiv.click(this.eventsHandler.onAddSubsetClick);     }     this.$container.append($outline);     if (this.mode === 'edit' && this.subsets.subsets.length) {       return this.drawSlotClasses($outline);     }   };    Drawer.prototype.drawSlotClasses = function($outline) {     var $hint, $slot, $slots, k, len1, ref, slotClass;     $slots = $('<div class="slotClasses">');     $hint = $('<div title="Щоб створити комірку, перетягніть потрібний вид в область редагування (область редагування обведена штриховим обідком).">Види комірок:</div>');     $slots.append($hint);     ref = this.slotClasses;     for (k = 0, len1 = ref.length; k < len1; k++) {       slotClass = ref[k];       $slot = $('<div class="slotClass">');       $slot.attr('data-slot-class', rast.name(slotClass));       $slot.text(slotClass.caption);       $slot.attr('title', 'Перетягніть на панель, щоб вставити цей вид комірки');       $slots.append($slot);     }     $outline.append($slots);     return $slots.find('.slotClass').draggable({       connectToSortable: '.etPanel .slots',       helper: 'clone'     });   };    Drawer.prototype.draw = function() {     this.$container.find('[original-title]').each(function(i, elem) {       var base;       return typeof (base = $(elem)).tipsy === "function" ? base.tipsy('hide') : void 0;     });     return mw.loader.using(['jquery.ui', 'jquery.tipsy'], (function(_this) {       return function() {         var $titled;         _this.$container.empty();         _this.drawMenu();         _this.drawMessage();         _this.drawNavigation();         _this.drawPanels();         $titled = _this.$container.find('[title]');         $titled.tipsy({           trigger: 'manual'         });         $titled.mouseenter(function() {           var $this, hideTimeout;           $this = $(this);           $this.tipsy('show');           hideTimeout = setTimeout(function() {             return $this.tipsy('hide');           }, 3000);           return $this.data('hideTimeout', hideTimeout);         });         return $titled.mouseleave(function() {           var $this, hideTimeout;           $this = $(this);           $this.tipsy('hide');           hideTimeout = $this.data('hideTimeout');           if (hideTimeout) {             return clearTimeout(hideTimeout);           }         });       };     })(this));   };    Drawer.prototype.drawMessage = function() {     return this.$container.append(this.message);   };    Drawer.prototype.drawPanels = function() {     var $content, $subset, $subsetDiv, i;     $content = $('<div>').attr('id', 'etContent').addClass('overflowHidden');     this.$container.append($content);     i = 0;     while (i < this.subsets.subsets.length) {       $subset = this.drawPanel(this.subsets.subsets[i], i);       $subsetDiv = $('<div>').attr('id', 'etTabContent' + i).attr('data-id', this.subsets.subsets[i].id).appendTo($content).addClass('asnav-content').append($subset);       i++;     }     this.$container.etMakeTabs(true);     this.$container.append($('<div>').css('clear', 'both'));     return this.$container.asnavSelect(this.activeTab);   };    Drawer.prototype.drawPanel = function(subsetWrapper, index) {     var $panel, panelDrawer;     $panel = $('<div>').attr('id', 'spchars-' + index).addClass('etPanel');     panelDrawer = new rast.PanelDrawer($panel, subsetWrapper, index, this.mode, this.subsets, this.eventsHandler);     panelDrawer.draw();     return $panel;   };    return Drawer;  })();  rast.PlainObjectParser = (function() {   function PlainObjectParser() {}    PlainObjectParser.charinsertDivider = ' ';    PlainObjectParser.parseTokens = function(arr, hotkeysHandler) {     var k, len1, slot, slots, token;     slots = [];     for (k = 0, len1 = arr.length; k < len1; k++) {       token = arr[k];       if (typeof token === 'string') {         slots = slots.concat(this.strToMultipleInsertionsSlot(token));       } else if (Object.prototype.toString.call(token) === '[object Array]') {         slots.push(this.slotFromArr(token));       } else if (typeof token === 'object') {         slot = this.slotFromPlainObj(token, hotkeysHandler);         if (slot) {           slots.push(slot);         }       }     }     return slots;   };    PlainObjectParser.slotFromArr = function(arr) {     return new rast.InsertionSlot({       insertion: arr[0],       caption: arr[1]     });   };    PlainObjectParser.slotsFromStr = function(str) {     var k, len1, slot, slots, token, tokens;     tokens = str.split(' ');     slots = [];     slot = void 0;     for (k = 0, len1 = tokens.length; k < len1; k++) {       token = tokens[k];       slot = this.slotFromStr(token);       slots.push(slot);     }     return slots;   };    PlainObjectParser.strToMultipleInsertionsSlot = function(str) {     var slot, slots;     slots = [];     slot = new rast.MultipleInsertionsSlot({       insertion: str     });     slots.push(slot);     return slots;   };    PlainObjectParser.lineReplace = function(c) {     if (c === '\\') {       return '\\';     } else if (c === '_') {       return ' ';     }   };    PlainObjectParser.slotFromStr = function(token) {     var modifiers, readModifiers, slot, tags;     readModifiers = function() {       var c, i, res;       res = {         bold: false,         plain: false,         italic: false       };       i = token.length - 1;       c = void 0;       while (i > -1 && !rast.insertion.isEscaped(token, i)) {         c = token.charAt(i).toLowerCase();         if (c === 'ж') {           res.bold = true;         } else if (c === 'н') {           res.italic = true;         } else if (c === 'п') {           res.plain = true;         } else {           break;         }         token = token.substring(0, i);         i--;       }       return res;     };     modifiers = readModifiers();     slot = void 0;     if (modifiers.plain || token === '' || token === '_') {       slot = new rast.PlainTextSlot({         bold: modifiers.bold,         italic: modifiers.italic       });       if (token === '' || token === '_') {         slot.text = this.charinsertDivider + ' ';       } else {         slot.text = rast.insertion.replaceSpecsymbols(token, '\\_', this.lineReplace) + ' ';       }     } else {       tags = this.parseInsertion(token, '');       slot = new rast.InsertionSlot({         bold: modifiers.bold,         italic: modifiers.italic,         insertion: token,         caption: tags.caption       });     }     return slot;   };    PlainObjectParser.generateLink = function(obj) {     var slot;     slot = void 0;     if (obj.ins || obj.insert) {       slot = new rast.InsertionSlot({});       $.extend(slot, this.parseInsertion(obj.ins || obj.insert, obj.cap || obj.caption, {         bold: obj.b || obj.bold,         italic: obj.i || obj.italic       }));     } else if (obj.func) {       slot = new rast.InsertionSlot({         clickFunc: obj.func,         useClickFunc: true,         caption: obj.cap || obj.caption || obj.ins       });       $.extend(slot, {         bold: obj.b || obj.bold,         italic: obj.i || obj.italic       });     }     return slot;   };    PlainObjectParser.parseInsertion = function(token, caption) {     var n, tagClose, tagOpen;     tagOpen = token;     tagClose = '';     n = rast.insertion.indexOfUnescaped(token, '+');     if (n > -1) {       tagOpen = token.substring(0, n);       tagClose = token.substring(n + 1);     }     tagOpen = rast.insertion.replaceSpecsymbols(tagOpen, '\\_', this.lineReplace);     tagClose = rast.insertion.replaceSpecsymbols(tagClose, '\\_', this.lineReplace);     if (!caption) {       caption = tagOpen + tagClose + ' ';       caption = rast.insertion.replaceSpecsymbols(caption, '\\$', function(c) {         if (c === '$') {           return '';         } else if (c === '\\') {           return '';         }       });     }     return {       caption: caption,       tagOpen: tagOpen,       tagClose: tagClose     };   };    PlainObjectParser.slotFromPlainObj = function(obj, hotkeysHandler) {     var slot;     slot = void 0;     if (obj.plain) {       slot = new rast.PlainTextSlot({         text: obj.cap || obj.caption,         bold: obj.b || obj.bold,         italic: obj.i || obj.italic       });     } else if (obj.html) {       slot = new rast.HtmlSlot({         html: obj.html,         onload: obj.onload       });     } else {       slot = this.generateLink(obj);       if (!slot) {         return;       }       hotkeysHandler.processShortcut(slot, obj);     }     return slot;   };    return PlainObjectParser;  })();  rast.SubsetsManager = (function() {   function SubsetsManager() {     this.reset();   }    SubsetsManager.prototype.slotById = function(id) {     var k, l, len1, len2, ref, ref1, slot, subset;     ref = this.subsets;     for (k = 0, len1 = ref.length; k < len1; k++) {       subset = ref[k];       ref1 = subset.slots;       for (l = 0, len2 = ref1.length; l < len2; l++) {         slot = ref1[l];         if (slot.id === id) {           return slot;         }       }     }     return null;   };    SubsetsManager.prototype.slotIndex = function(slot) {     var k, len1, ref, slotIndex, subset;     ref = this.subsets;     for (k = 0, len1 = ref.length; k < len1; k++) {       subset = ref[k];       slotIndex = subset.slots.indexOf(slot);       if (slotIndex > -1) {         return slotIndex;       }     }     return null;   };    SubsetsManager.prototype.subsetBySlot = function(slot) {     var k, len1, ref, subset;     ref = this.subsets;     for (k = 0, len1 = ref.length; k < len1; k++) {       subset = ref[k];       if (subset.slots.indexOf(slot) > -1) {         return subset;       }     }     return null;   };    SubsetsManager.prototype.subsetBySlotId = function(slot) {     var k, l, len1, len2, ref, ref1, subset;     ref = this.subsets;     for (k = 0, len1 = ref.length; k < len1; k++) {       subset = ref[k];       ref1 = subset.slots;       for (l = 0, len2 = ref1.length; l < len2; l++) {         slot = ref1[l];         if (slot.id === id) {           return subset;         }       }     }     return null;   };    SubsetsManager.prototype.subsetById = function(id) {     var k, len1, ref, subset;     ref = this.subsets;     for (k = 0, len1 = ref.length; k < len1; k++) {       subset = ref[k];       if (subset.id === id) {         return subset;       }     }     return null;   };    SubsetsManager.prototype.processShortcut = function(slot, obj) {     var key;     if (obj.key) {       if (typeof obj.key === 'string') {         key = obj.key[0].toUpperCase();         slot.key = key;         if (obj.func) {           this.hotkeys[key] = obj.func;         }         if (obj.ins || obj.insert) {           return this.hotkeys[key] = (function(a) {             return a;           })(slot);         }       }     }   };    SubsetsManager.prototype.addSubset = function(caption, index) {     var subset;     subset = {       caption: caption,       slots: [],       id: this.uniqueSubsetId()     };     return this.insertOrAppend(this.subsets, index, subset);   };    SubsetsManager.prototype.deleteSubset = function(subsetToBeRemoved) {     return this.subsets = $.grep(this.subsets, function(subset, index) {       return subsetToBeRemoved.id !== subset.id;     });   };    SubsetsManager.prototype.addSlot = function(slotClassOrSlot, subset, index) {     var slot;     if (slotClassOrSlot instanceof rast.Slot) {       slot = slotClassOrSlot;       slot.id = this.uniqueSlotId();     } else {       slot = new slotClassOrSlot({         id: this.uniqueSlotId()       });     }     return this.insertOrAppend(subset.slots, index, slot);   };    SubsetsManager.prototype.deleteSlot = function(slotId) {     var slot, slotIndex, subset;     if (!(typeof slotId === 'number')) {       return;     }     slot = this.slotById(slotId);     slotIndex = this.slotIndex(slot);     subset = this.subsetBySlot(slot);     return subset.slots.splice(slotIndex, 1);   };    SubsetsManager.prototype.uniqueSlotId = function() {     var result;     result = this.slotId;     this.slotId++;     return result;   };    SubsetsManager.prototype.uniqueSubsetId = function() {     var result;     result = this.subsetId;     this.subsetId++;     return result;   };    SubsetsManager.prototype.insertOrAppend = function(arr, index, item) {     if (index) {       arr.splice(index, 0, item);     } else {       arr.push(item);     }     return item;   };    SubsetsManager.prototype.reset = function() {     var self;     this.subsets = [];     this.hotkeys = {};     self = this;     this.slotId = 0;     return this.subsetId = 0;   };    SubsetsManager.prototype.readEncodedSubsets = function(encodedSubsets) {     var j, len, results, subset;     results = [];     j = 0;     len = encodedSubsets.length;     while (j < len) {       subset = encodedSubsets[j];       results.push(this.readEncodedSubset(subset));       j++;     }     return results;   };    SubsetsManager.prototype.decodeSubset = function(encodedSubset) {     var slots;     slots = rast.PlainObjectParser.parseTokens(encodedSubset.symbols, this, this);     return {       slots: slots,       caption: encodedSubset.caption     };   };    SubsetsManager.prototype.readEncodedSubset = function(encodedSubset) {     var internalSubset, k, len1, ref, results1, slot, subset;     subset = this.decodeSubset(encodedSubset);     internalSubset = this.addSubset(subset.caption);     ref = subset.slots;     results1 = [];     for (k = 0, len1 = ref.length; k < len1; k++) {       slot = ref[k];       results1.push(this.addSlot(slot, internalSubset));     }     return results1;   };    SubsetsManager.prototype.toJSON = function() {     return this.subsets;   };    SubsetsManager.prototype.deserialize = function(subsets) {     return $.each(subsets, (function(_this) {       return function(i, subset) {         var cons, s, slot;         s = _this.addSubset(subset.caption);         cons = null;         slot = null;         return $.each(subset.slots, function(i, plainSlot) {           cons = eval(plainSlot['class']);           slot = new cons(plainSlot);           return _this.addSlot(slot, s);         });       };     })(this));   };    return SubsetsManager;  })();  rast.UIwindow = (function() {   function UIwindow() {}    UIwindow.show = function($content) {     var EditDialog, editDialog, windowManager;     EditDialog = function(config) {       return EditDialog["super"].call(this, config);     };     OO.inheritClass(EditDialog, OO.ui.Dialog);     EditDialog.static.title = 'Simple dialog';     EditDialog.static.name = 'Edit dialog';     EditDialog.prototype.initialize = function() {       EditDialog["super"].prototype.initialize.call(this);       this.$body.append($content);     };     EditDialog.prototype.getBodyHeight = function() {       return $content.outerHeight(true);     };     editDialog = new EditDialog({       size: 'large'     });     windowManager = new OO.ui.WindowManager;     $('body').append(windowManager.$element);     windowManager.addWindows([editDialog]);     windowManager.openWindow(editDialog);     return editDialog;   };    return UIwindow;  })();  rast.SlotAttributesEditor = (function() {   function SlotAttributesEditor(options) {     this.slot = options.slot;     this.slotsManager = options.slotsManager;     this.allInputs = [];   }    SlotAttributesEditor.prototype.fieldsetForAttrs = function(fieldsetName, attrs) {     var OOinput, attribute, fieldOptions, fields, fieldset, inputData, inputs, k, len1, type, value;     inputs = [];     for (k = 0, len1 = attrs.length; k < len1; k++) {       attribute = attrs[k];       value = this.slot[attribute.name];       type = attribute.type;       OOinput = type === 'string' || type === 'text' ? (fieldOptions = {         value: value,         multiline: type === 'text',         rows: 3,         autosize: true       }, {         getValue: 'getValue',         OOobject: new OO.ui.TextInputWidget(fieldOptions)       }) : type === 'boolean' ? {         getValue: 'getValue',         OOobject: new OO.ui.ToggleSwitchWidget({           value: value         })       } : void 0;       inputData = {         attribute: attribute.name,         label: attribute.caption,         input: OOinput.OOobject,         getValueFunc: OOinput.getValue,         labelAlignment: attribute.labelAlignment || 'left',         helpText: attribute.help       };       if (OOinput) {         this.allInputs.push(inputData);         inputs.push(inputData);       }     }     fieldset = new OO.ui.FieldsetLayout({       label: fieldsetName     });     fields = $.map(inputs, function(inputWrapper, index) {       return new OO.ui.FieldLayout(inputWrapper.input, {         label: inputWrapper.label,         align: inputWrapper.labelAlignment,         help: inputWrapper.helpText       });     });     fieldset.addItems(fields);     return fieldset;   };    SlotAttributesEditor.prototype.startEditing = function() {     var $content, attrs, bottomButtons, cancelButton, dialog, fieldset, panel, removeButton, saveButton, slotClass;     slotClass = this.slot.constructor;     attrs = slotClass.editableAttributes;     $content = $('<div class="rastEditWindow">');     if (attrs.view) {       fieldset = this.fieldsetForAttrs('Вигляд', attrs.view);       $content.append(fieldset.$element);     }     if (attrs.functionality) {       fieldset = this.fieldsetForAttrs('Функціонал', attrs.functionality);       $content.append(fieldset.$element);     }     saveButton = new OO.ui.ButtonWidget({       icon: 'check',       label: 'Зберегти'     });     saveButton.on('click', (function(_this) {       return function() {         var inputWrapper, k, len1, ref;         ref = _this.allInputs;         for (k = 0, len1 = ref.length; k < len1; k++) {           inputWrapper = ref[k];           _this.slot[inputWrapper.attribute] = inputWrapper.input[inputWrapper.getValueFunc]();         }         _this.slotsManager.onSlotSaved();         return dialog.close();       };     })(this));     cancelButton = new OO.ui.ButtonWidget({       icon: 'cancel',       label: 'Скасувати'     });     cancelButton.on('click', function() {       return dialog.close();     });     removeButton = new OO.ui.ButtonWidget({       icon: 'remove',       label: 'Вилучити комірку'     });     removeButton.on('click', (function(_this) {       return function() {         var base;         if (typeof (base = _this.slotsManager).onDeleteSlot === "function") {           base.onDeleteSlot(_this.slot.id);         }         return dialog.close();       };     })(this));     bottomButtons = new OO.ui.HorizontalLayout({       items: [saveButton, cancelButton, removeButton],       classes: ['bottomButtons']     });     $content.append(bottomButtons.$element);     panel = new OO.ui.PanelLayout({       $: $,       padded: true,       expanded: false     });     panel.$element.append($content);     return dialog = rast.UIwindow.show(panel.$element);   };    return SlotAttributesEditor;  })();  rast.SlotAttributes = (function() {   function SlotAttributes(attrsObj) {     $.extend(this, attrsObj);   }    SlotAttributes.prototype.toArray = function() {     var result;     result = [];     if (this.view) {       result = result.concat(this.view);     }     if (this.functionality) {       result = result.concat(this.functionality);     }     return result;   };    return SlotAttributes;  })();  rast.Slot = (function() {   Slot.editableAttributes = new rast.SlotAttributes({});    Slot.editorClass = rast.SlotAttributesEditor;    function Slot(options) {     var attribute, k, len1, ref;     if (options == null) {       options = {};     }     ref = this.constructor.editableAttributes.toArray();     for (k = 0, len1 = ref.length; k < len1; k++) {       attribute = ref[k];       this[attribute.name] = attribute["default"];     }     $.extend(this, options, {       'class': 'rast.' + this.constructor.name     });   }    Slot.prototype.generateEditHtml = function() {     var $element;     $element = this.generateHtml();     $($element).addClass('editedSlot');     return $element;   };    return Slot;  })();  rast.PlainTextSlot = (function(superClass) {   extend(PlainTextSlot, superClass);    function PlainTextSlot() {     return PlainTextSlot.__super__.constructor.apply(this, arguments);   }    PlainTextSlot.caption = 'Простий текст';    PlainTextSlot.editableAttributes = new rast.SlotAttributes({     view: [       {         name: 'css',         type: 'text',         "default": '',         caption: 'CSS-стилі'       }, {         name: 'text',         type: 'text',         "default": 'текст',         caption: 'Текст',         labelAlignment: 'top'       }     ]   });    PlainTextSlot.prototype.generateEditHtml = function() {     var $elem;     $elem = PlainTextSlot.__super__.generateEditHtml.call(this);     return $elem.attr('title', this.text);   };    PlainTextSlot.prototype.generateHtml = function(styles) {     var $elem;     $elem = $('<span>');     $elem.text(this.text);     $elem.attr('data-id', this.id);     $elem.attr('style', styles || this.css);     if (this.bold) {       $elem.css('font-weight', 'bold');     }     if (this.italic) {       $elem.css('font-style', 'italic');     }     return $elem;   };    return PlainTextSlot;  })(rast.Slot);  rast.InsertionSlot = (function(superClass) {   extend(InsertionSlot, superClass);    function InsertionSlot() {     return InsertionSlot.__super__.constructor.apply(this, arguments);   }    InsertionSlot.caption = 'Одна вставка';    InsertionSlot.editableAttributes = new rast.SlotAttributes({     view: [       {         name: 'css',         type: 'text',         "default": '',         caption: 'CSS-стилі'       }, {         name: 'caption',         caption: 'Напис',         type: 'text',         "default": 'Нова вставка'       }, {         name: 'captionAsHtml',         caption: 'Сприймати напис, як html-код?',         type: 'boolean',         "default": false       }     ],     functionality: [       {         name: 'insertion',         caption: 'Текст вставки',         type: 'text',         "default": '$',         labelAlignment: 'top',         help: 'Символ долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення. \nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'       }, {         name: 'useClickFunc',         caption: 'Замість вставляння виконати іншу дію?',         type: 'boolean',         "default": false       }, {         name: 'clickFunc',         caption: 'Інша дія (при клацанні)',         type: 'text',         "default": 'function(){  }',         labelAlignment: 'top'       }     ]   });    InsertionSlot.insertFunc = function(insertion) {     var tags;     rast.$getTextarea().focus();     tags = rast.PlainObjectParser.parseInsertion(insertion, '');     return rast.$getTextarea().insertTag(tags.tagOpen, tags.tagClose);   };    InsertionSlot.prototype.toJSON = function() {     var copy;     copy = rast.clone(this);     if (this.clickFunc) {       copy.clickFunc = this.clickFunc.toString();     }     return copy;   };    InsertionSlot.prototype.generateEditHtml = function() {     var $elem;     $elem = InsertionSlot.__super__.generateEditHtml.call(this);     $elem.attr('title', (this.useClickFunc && this.clickFunc.toString()) || this.insertion);     return $elem.append($('<div class="overlay">'));   };    InsertionSlot.prototype.generateCommonHtml = function(styles) {     var $a, $elem, caption;     if (this.captionAsHtml) {       $elem = $('<div>');       $elem.append(this.caption);       $elem.attr('data-id', this.id);       if (styles) {         $elem.attr('style', styles);       }       if (this.bold) {         $elem.css('font-weight', 'bold');       }       if (this.italic) {         $elem.css('font-style', 'italic');       }       return $elem;     } else {       $a = $('<a>');       $a.attr('data-id', this.id);       if (styles) {         $a.attr('style', styles);       }       caption = $('<div/>').text(this.caption).html();       $a.html(caption);       if (this.bold) {         $a.css('font-weight', 'bold');       }       if (this.italic) {         $a.css('font-style', 'italic');       }       return $a;     }   };    InsertionSlot.prototype.generateHtml = function(styles) {     var $elem;     $elem = this.generateCommonHtml(styles || this.css);     $elem.click((function(_this) {       return function(event) {         event.preventDefault();         if (_this.useClickFunc) {           return eval('(' + _this.clickFunc + ')()');         } else {           return rast.InsertionSlot.insertFunc(_this.insertion);         }       };     })(this));     return $elem;   };    return InsertionSlot;  })(rast.Slot);  rast.MultipleInsertionsSlot = (function(superClass) {   extend(MultipleInsertionsSlot, superClass);    function MultipleInsertionsSlot() {     return MultipleInsertionsSlot.__super__.constructor.apply(this, arguments);   }    MultipleInsertionsSlot.caption = 'Набір вставок';    MultipleInsertionsSlot.editableAttributes = new rast.SlotAttributes({     view: [       {         name: 'css',         type: 'text',         "default": '',         caption: 'CSS-стилі'       }     ],     functionality: [       {         name: 'insertion',         caption: 'Вставки',         type: 'text',         "default": 'вставка_1 ·п вставка_2',         labelAlignment: 'top',         help: 'Все, що розділене символами пробілу, вважається окремою коміркою. \nЯкщо комірка закірчується символом "п", вона вважатиметься не вставкою, а простим текстом. \nЯкщо хочете включити пробіл у вставку, пишіть нижнє підкреслення: "_". \nСимвол долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення. \nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'       }     ]   });    MultipleInsertionsSlot.insertFunc = function(insertion) {     var tags;     rast.$getTextarea().focus();     tags = rast.PlainObjectParser.parseInsertion(insertion, '');     return rast.$getTextarea().insertTag(tags.tagOpen, tags.tagClose);   };    MultipleInsertionsSlot.prototype.generateEditHtml = function() {     var $elem;     $elem = MultipleInsertionsSlot.__super__.generateEditHtml.call(this);     $elem.attr('title', this.insertion);     return $elem.prepend($('<div class="overlay">'));   };    MultipleInsertionsSlot.prototype.generateCommonHtml = function(styles) {     var $elem, $slot, k, len1, slot, slots;     slots = rast.PlainObjectParser.slotsFromStr(this.insertion);     $elem = $('<div>');     $elem.attr('data-id', this.id);     if (styles) {       $elem.attr('style', styles);     }     if (this.bold) {       $elem.css('font-weight', 'bold');     }     if (this.italic) {       $elem.css('font-style', 'italic');     }     for (k = 0, len1 = slots.length; k < len1; k++) {       slot = slots[k];       $slot = $(slot.generateHtml(styles));       $elem.append($slot);     }     return $elem;   };    MultipleInsertionsSlot.prototype.generateHtml = function(styles) {     var $elem;     $elem = this.generateCommonHtml(styles || this.css);     return $elem;   };    return MultipleInsertionsSlot;  })(rast.Slot);  rast.HtmlSlot = (function(superClass) {   extend(HtmlSlot, superClass);    HtmlSlot.caption = 'Довільний код';    HtmlSlot.editableAttributes = new rast.SlotAttributes({     view: [       {         name: 'html',         type: 'text',         "default": '<span>html</span>',         caption: 'HTML',         labelAlignment: 'top'       }     ],     functionality: [       {         name: 'onload',         type: 'text',         "default": 'function(){  }',         caption: 'JavaScript, що виконається при ініціалізації',         labelAlignment: 'top'       }     ]   });    HtmlSlot.prototype.toJSON = function() {     var copy;     copy = rast.clone(this);     if (this.onload) {       copy.onload = this.onload.toString();     }     return copy;   };    function HtmlSlot(options) {     HtmlSlot.__super__.constructor.call(this, options);     if (typeof this.onload === 'string') {       this.onload = eval('(' + this.onload + ')');     }     if (typeof this.onload === 'function') {       editTools.addOnloadFunc(this.onload);     }   }    HtmlSlot.prototype.generateEditHtml = function() {     var $elem;     $elem = HtmlSlot.__super__.generateEditHtml.call(this);     return $elem.attr('title', this.html);   };    HtmlSlot.prototype.generateHtml = function() {     var $elem, $overlay, $wrapper;     $elem = $(this.html);     $wrapper = $('<div>');     $wrapper.attr('data-id', this.id);     $wrapper.append($elem);     $overlay = $('<div class="overlay">');     $wrapper.append($overlay);     return $wrapper;   };    return HtmlSlot;  })(rast.Slot);  rast.PageStorage = (function() {   function PageStorage() {}    PageStorage.load = function(pagename, onLoaded, handler) {     return mw.loader.using('mediawiki.api', function() {       var api;       api = new mw.Api;       return api.get({         action: 'query',         prop: 'revisions',         rvprop: 'content',         titles: pagename       }).done(function(data) {         var pageId, results1;         results1 = [];         for (pageId in data.query.pages) {           if (data.query.pages[pageId].revisions) {             results1.push(typeof onLoaded === "function" ? onLoaded(data.query.pages[pageId].revisions[0]['*']) : void 0);           } else {             results1.push(typeof handler.onSubpageNotFound === "function" ? handler.onSubpageNotFound(pageId) : void 0);           }         }         return results1;       }).fail(function() {         return handler.onReadFromSubpageError();       }).always(function() {         return handler.onEndReadingSubpage();