Відмінності між версіями «MediaWiki:Gadget-ImprovedEditTools.js»

Матеріал з Осмислено
Перейти до навігації Перейти до пошуку
 
(Не показано одну проміжну версію цього користувача)
Рядок 4: Рядок 4:
 
if (typeof unsafeWindow !== "undefined" && unsafeWindow !== null) {
 
if (typeof unsafeWindow !== "undefined" && unsafeWindow !== null) {
 
   window.$ = unsafeWindow.$;
 
   window.$ = unsafeWindow.$;
  window.etSubsets = unsafeWindow.etSubsets;
 
 
}
 
}
  
 
window.rast = {
 
window.rast = {
 +
  clone: function(object) {
 +
    return $.extend(true, {}, object);
 +
  },
 
   arrayMove: function(array, from, to) {
 
   arrayMove: function(array, from, to) {
 
     return array.splice(to, 0, array.splice(from, 1)[0]);
 
     return array.splice(to, 0, array.splice(from, 1)[0]);
Рядок 157: Рядок 159:
 
     return 'rast.' + constructor.name;
 
     return 'rast.' + constructor.name;
 
   },
 
   },
   clone: (function() {
+
   processSelection: function(txtFunc) {
     var clone;
+
     var $textarea, txt;
 
+
     $textarea = rast.$getTextarea();
    /**
+
     txt = $textarea.getSelection();
    * Clones (copies) an Object using deep copying.
+
     return $textarea.setSelection(txtFunc(txt));
    #
+
   },
    * This function supports circular references by default, but if you are certain
+
   perLineReplace: function(str, regex, to) {
    * there are no circular references in your object, you can save some CPU time
+
     var i, len;
    * by calling clone(obj, false).
+
     str = str.split('\n');
    #
+
     len = str.length;
    * Caution: if `circular` is false and `parent` contains circular references,
+
     i = 0;
    * your program may enter an infinite loop and crash.
+
     while (i < len) {
    #
+
       str[i] = str[i].replace(regex, to);
    * @param `parent` - the object to be cloned
+
       i += 1;
    * @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);
 
 
     }
 
     }
  },
+
     return str.join('\n');
  processSelection: function(txtFunc) {
+
   },
    var $textarea, txt;
+
   linkifyList: function(s) {
    $textarea = rast.$getTextarea();
+
     return rast.perLineReplace(s, /[^*;#—\s,][^*\.#—;,]+/g, '[[$&]]');
    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) {
 
   simpleList: function(s) {
Рядок 357: Рядок 185:
 
     return rast.perLineReplace(s, /(([\*#]*)\s*)(.+)/g, '#$2 $3');
 
     return rast.perLineReplace(s, /(([\*#]*)\s*)(.+)/g, '#$2 $3');
 
   },
 
   },
  dot: '·п',
 
 
   searchAndReplace: {
 
   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) {
 
     doSearchReplace: function(mode) {
 
       var $textarea, actualReplacement, context, e, end, flags, i, index, isRegex, match, matchCase, newText, offset, regex, replaceStr, searchStr, start, text, textRemainder;
 
       var $textarea, actualReplacement, context, e, end, flags, i, index, isRegex, match, matchCase, newText, offset, regex, replaceStr, searchStr, start, text, textRemainder;
Рядок 385: Рядок 195:
 
       start = void 0;
 
       start = void 0;
 
       end = void 0;
 
       end = void 0;
       $('#et-replace-nomatch, #et-replace-success,\u0009\u0009\u0009 #et-replace-emptysearch, #et-replace-invalidregex').hide();
+
       $('#et-replace-nomatch, #et-replace-success, #et-replace-emptysearch, #et-replace-invalidregex').hide();
 
       searchStr = $('#et-replace-search').val();
 
       searchStr = $('#et-replace-search').val();
 
       if (searchStr === '') {
 
       if (searchStr === '') {
Рядок 399: Рядок 209:
 
       isRegex = $('#et-replace-regex').is(':checked');
 
       isRegex = $('#et-replace-regex').is(':checked');
 
       if (!isRegex) {
 
       if (!isRegex) {
         searchStr = mw.RegExp.escape(searchStr);
+
         searchStr = mw.util.escapeRegExp(searchStr);
 
       }
 
       }
 
       if (mode === 'replaceAll') {
 
       if (mode === 'replaceAll') {
Рядок 487: Рядок 297:
 
         return $textarea[0].focus();
 
         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;
 
 
     }
 
     }
 
   }
 
   }
Рядок 572: Рядок 368:
  
 
   PanelDrawer.prototype.drawEditMode = function() {
 
   PanelDrawer.prototype.drawEditMode = function() {
     var $nameInput, $nameInputContainer, $nameLabel, $panelRemoveButton, $preview, $previewContent, $removeIcon, $slots, generateMethod;
+
     var $descLabel, $preview, $previewContent, $slots, generateMethod, layout, nameInput, nameLabel, removeButton;
     $nameLabel = $('<label class="panelNameLabel">Назва:</label>');
+
     nameLabel = new OO.ui.LabelWidget({
     $nameInputContainer = $('<div>');
+
      label: 'Назва панелі:'
     $nameInputContainer.append($nameLabel);
+
    });
     $nameInput = $('<input type="text">').val(this.subsetWrapper.caption);
+
     nameInput = new OO.ui.TextInputWidget({
     $nameInput.change({
+
      value: this.subsetWrapper.caption
       subsetWrapper: this.subsetWrapper
+
     });
 +
     nameInput.$element.on('keydown', function(event) {
 +
      if (event.keyCode === 13) {
 +
        event.preventDefault();
 +
        return false;
 +
      }
 +
    });
 +
     nameInput.$element.change({
 +
       subsetWrapper: this.subsetWrapper
 
     }, this.eventsHandler.onTabNameChanged);
 
     }, this.eventsHandler.onTabNameChanged);
     $nameInputContainer.append($nameInput);
+
     removeButton = new OO.ui.ButtonWidget({
    $nameInputContainer.appendTo(this.$panel);
+
      label: 'Вилучити цю панель',
    $panelRemoveButton = $('<span class="panelRemoveButton">Вилучити панель</span>');
+
      flags: 'destructive'
     $panelRemoveButton.click((function(_this) {
+
    });
 +
     removeButton.on('click', (function(_this) {
 
       return function() {
 
       return function() {
 
         return _this.eventsHandler.onRemoveSubsetClick(_this.subsetWrapper);
 
         return _this.eventsHandler.onRemoveSubsetClick(_this.subsetWrapper);
 
       };
 
       };
 
     })(this));
 
     })(this));
     $removeIcon = $('<span class="removeIcon">');
+
    layout = new OO.ui.HorizontalLayout({
     $nameInputContainer.append($panelRemoveButton);
+
      items: [nameLabel, nameInput, removeButton]
     $panelRemoveButton.append($removeIcon);
+
    });
 +
    this.$panel.append(layout.$element);
 +
     $descLabel = $('<span>');
 +
     $descLabel.text('Ви можете перетягувати комірки, щоб змінити їхній порядок. Клацніть по комірці, щоб редагувати її. Щоб додати комірку, перетягніть нижче один з цих видів:');
 +
    this.$panel.append($descLabel);
 +
     this.drawSlotClasses(this.$panel);
 
     $slots = $('<div class="slots">');
 
     $slots = $('<div class="slots">');
 
     this.$panel.append($slots);
 
     this.$panel.append($slots);
Рядок 596: Рядок 406:
 
     this.sortableSlots($slots);
 
     this.sortableSlots($slots);
 
     if (!this.subsetWrapper.slots.length) {
 
     if (!this.subsetWrapper.slots.length) {
       $slots.append('<span>Щоб додати комірку, сюди перетягніть потрібний вид з бічної панелі.</span>');
+
       $slots.append('<span>Щоб додати комірку, сюди перетягніть потрібний вид.</span>');
 
     } else {
 
     } else {
 
       this.generateHtml($slots, this.subsetWrapper.slots, generateMethod);
 
       this.generateHtml($slots, this.subsetWrapper.slots, generateMethod);
Рядок 629: Рядок 439:
 
     slotIndex = this.subsets.slotIndex(slot);
 
     slotIndex = this.subsets.slotIndex(slot);
 
     return rast.arrayMove(this.subsetWrapper.slots, slotIndex, newSlotIndex);
 
     return rast.arrayMove(this.subsetWrapper.slots, slotIndex, newSlotIndex);
 +
  };
 +
 +
  PanelDrawer.prototype.drawSlotClasses = function($container) {
 +
    var $slot, $slots, k, len1, ref, slotClass;
 +
    $slots = $('<div class="slotClasses">');
 +
    ref = this.slotClasses();
 +
    for (k = 0, len1 = ref.length; k < len1; k++) {
 +
      slotClass = ref[k];
 +
      $slot = $('<span class="slotClass">');
 +
      $slot.attr('data-slot-class', rast.name(slotClass));
 +
      $slot.text(slotClass.caption);
 +
      $slot.attr('title', 'Перетягніть на панель, щоб вставити цей вид комірки');
 +
      $slots.append($slot);
 +
    }
 +
    $container.append($slots);
 +
    return $slots.find('.slotClass').draggable({
 +
      connectToSortable: '.etPanel .slots',
 +
      helper: 'clone'
 +
    });
 +
  };
 +
 +
  PanelDrawer.prototype.slotClasses = function() {
 +
    return [rast.PlainTextSlot, rast.InsertionSlot, rast.MultipleInsertionsSlot, rast.HtmlSlot];
 
   };
 
   };
  
Рядок 638: Рядок 471:
 
   function Drawer() {}
 
   function Drawer() {}
  
  Drawer.prototype.$editButtonIcon = function() {
+
   Drawer.prototype.$editButton = function() {
    return $('<div class="gear">');
+
     var icon;
  };
+
     icon = new OO.ui.IconWidget({
 
+
      icon: 'settings',
   Drawer.prototype.$editButton = function() {
+
      title: 'Редагувати символи',
     var $editButton;
+
      classes: ['gear']
     $editButton = this.$editButtonIcon();
+
    });
    $editButton.addClass('menuButton edit');
+
    return icon.$element;
    return $editButton.attr('title', 'Редагувати символи.');
 
 
   };
 
   };
  
 
   Drawer.prototype.drawMenu = function() {
 
   Drawer.prototype.drawMenu = function() {
     var $aboutLink, $cancelButton, $dot, $editButton, $menu, $persistButton, $resetButton, $saveButton;
+
     var $aboutLink, $editButton, $menu, cancelButton, persistButton, resetButton, saveButton;
 
     $menu = $('<div class="rastMenu">');
 
     $menu = $('<div class="rastMenu">');
 
     $menu.addClass(this.mode);
 
     $menu.addClass(this.mode);
Рядок 658: Рядок 490:
 
       $menu.append($editButton);
 
       $menu.append($editButton);
 
     } else if (this.mode === 'edit') {
 
     } else if (this.mode === 'edit') {
       $dot = function() {
+
       persistButton = new OO.ui.ButtonWidget({
         return $('<span> · </span>');
+
         label: ' Зберегти на постійно',
      };
+
        title: 'Символи буде збережено на підсторінку у Вашому просторі користувача.',
      $persistButton = $('<span class="menuButton">').attr('title', 'Символи буде збережено у Вашому особистому просторі. Для цього виконається редагування підсторінки від Вашого імени.');
+
        icon: 'checkAll'
       $persistButton.text('зберегти на підсторінку').click(this.eventsHandler.onPersistClick);
+
      });
       $saveButton = $('<span class="menuButton">').attr('title', 'Зміни збережуться тільки на час редагування сторінки і втратяться після закриття або перевантаження сторінки.');
+
       persistButton.on('click', this.eventsHandler.onPersistClick);
       $saveButton.text('зберегти').click(this.eventsHandler.onSaveClick);
+
       saveButton = new OO.ui.ButtonWidget({
       $cancelButton = $('<span class="menuButton">');
+
        label: ' Зберегти тимчасово',
      $cancelButton.text('скасувати').click(this.eventsHandler.onCancelClick).attr('title', 'Всі зміни цієї сесії редагування будуть відкинуті.');
+
        title: 'Зміни збережуться тільки на час редагування сторінки і втратяться після закриття або перевантаження сторінки.',
      $resetButton = $('<span class="menuButton">');
+
        icon: 'check'
       $resetButton.text('відновити звичаєві').click(this.eventsHandler.onResetClick).attr('title', 'Буде відновлено набір символів за промовчанням.');
+
      });
 +
       saveButton.on('click', this.eventsHandler.onSaveClick);
 +
       cancelButton = new OO.ui.ButtonWidget({
 +
        label: ' Скасувати',
 +
        title: 'Всі зміни цієї сесії редагування будуть відкинуті.',
 +
        icon: 'cancel'
 +
      });
 +
       cancelButton.on('click', this.eventsHandler.onCancelClick);
 +
      resetButton = new OO.ui.ButtonWidget({
 +
        label: ' Відновити звичаєві',
 +
        title: 'Буде відновлено набір символів за промовчанням.',
 +
        icon: 'reload'
 +
      });
 +
      resetButton.on('click', this.eventsHandler.onResetClick);
 
       $aboutLink = $("<a class=\"aboutLink\" target=\"_blank\" href=\"" + this.docLink + "\">про додаток</a>");
 
       $aboutLink = $("<a class=\"aboutLink\" target=\"_blank\" href=\"" + this.docLink + "\">про додаток</a>");
       $menu.append($persistButton, $dot(), $saveButton, $dot(), $cancelButton, $dot(), $resetButton, $aboutLink);
+
       $menu.append(persistButton.$element, saveButton.$element, cancelButton.$element, resetButton.$element, $aboutLink);
 
     }
 
     }
 
     return this.$container.append($menu);
 
     return this.$container.append($menu);
Рядок 713: Рядок 558:
 
       $addNewdiv.click(this.eventsHandler.onAddSubsetClick);
 
       $addNewdiv.click(this.eventsHandler.onAddSubsetClick);
 
     }
 
     }
     this.$container.append($outline);
+
     return this.$container.append($outline);
    if (this.mode === 'edit' && this.subsets.subsets.length) {
 
      return this.drawSlotClasses($outline);
 
    }
 
 
   };
 
   };
  
   Drawer.prototype.drawSlotClasses = function($outline) {
+
   Drawer.prototype.draw = function() {
     var $hint, $slot, $slots, k, len1, ref, slotClass;
+
     this.$container.empty();
    $slots = $('<div class="slotClasses">');
+
     this.drawMenu();
    $hint = $('<div title="Щоб створити комірку, перетягніть потрібний вид в область редагування (область редагування обведена штриховим обідком).">Види комірок:</div>');
+
    this.drawMessage();
    $slots.append($hint);
+
     this.drawNavigation();
     ref = this.slotClasses;
+
     return this.drawPanels();
    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() {
+
   Drawer.prototype.drawMessage = function() {
    this.$container.find('[original-title]').each(function(i, elem) {
+
     return this.$container.append(this.message);
      var base;
+
   };
      return typeof (base = $(elem)).tipsy === "function" ? base.tipsy('hide') : void 0;
 
    });
 
    return mw.loader.using(['jquery.ui.sortable', 'jquery.ui.droppable', 'jquery.ui.draggable', '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() {
 
   Drawer.prototype.drawPanels = function() {
Рядок 815: Рядок 605:
 
   PlainObjectParser.charinsertDivider = ' ';
 
   PlainObjectParser.charinsertDivider = ' ';
  
   PlainObjectParser.parseTokens = function(arr, hotkeysHandler) {
+
   PlainObjectParser.parseTokens = function(arr) {
 
     var k, len1, slot, slots, token;
 
     var k, len1, slot, slots, token;
 
     slots = [];
 
     slots = [];
Рядок 825: Рядок 615:
 
         slots.push(this.slotFromArr(token));
 
         slots.push(this.slotFromArr(token));
 
       } else if (typeof token === 'object') {
 
       } else if (typeof token === 'object') {
         slot = this.slotFromPlainObj(token, hotkeysHandler);
+
         slot = this.slotFromPlainObj(token);
 
         if (slot) {
 
         if (slot) {
 
           slots.push(slot);
 
           slots.push(slot);
Рядок 974: Рядок 764:
 
   };
 
   };
  
   PlainObjectParser.slotFromPlainObj = function(obj, hotkeysHandler) {
+
   PlainObjectParser.slotFromPlainObj = function(obj) {
 
     var slot;
 
     var slot;
 
     slot = void 0;
 
     slot = void 0;
Рядок 993: Рядок 783:
 
         return;
 
         return;
 
       }
 
       }
      hotkeysHandler.processShortcut(slot, obj);
 
 
     }
 
     }
 
     return slot;
 
     return slot;
Рядок 1074: Рядок 863:
 
     }
 
     }
 
     return null;
 
     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);
 
        }
 
      }
 
    }
 
 
   };
 
   };
  
Рядок 1160: Рядок 931:
 
     var self;
 
     var self;
 
     this.subsets = [];
 
     this.subsets = [];
    this.hotkeys = {};
 
 
     self = this;
 
     self = this;
 
     this.slotId = 0;
 
     this.slotId = 0;
Рядок 1234: Рядок 1004:
 
     };
 
     };
 
     OO.inheritClass(EditDialog, OO.ui.Dialog);
 
     OO.inheritClass(EditDialog, OO.ui.Dialog);
     EditDialog.static.title = 'Simple dialog';
+
     EditDialog["static"].title = 'Simple dialog';
     EditDialog.static.name = 'Edit dialog';
+
     EditDialog["static"].name = 'Edit dialog';
 
     EditDialog.prototype.initialize = function() {
 
     EditDialog.prototype.initialize = function() {
 
       EditDialog["super"].prototype.initialize.call(this);
 
       EditDialog["super"].prototype.initialize.call(this);
Рядок 1271: Рядок 1041:
 
       value = this.slot[attribute.name];
 
       value = this.slot[attribute.name];
 
       type = attribute.type;
 
       type = attribute.type;
       OOinput = type === 'string' || type === 'text' ? (fieldOptions = {
+
       OOinput = type === 'string' ? (fieldOptions = {
 +
        value: value
 +
      }, {
 +
        getValue: 'getValue',
 +
        OOobject: new OO.ui.TextInputWidget(fieldOptions)
 +
      }) : type === 'text' ? (fieldOptions = {
 
         value: value,
 
         value: value,
        multiline: type === 'text',
 
 
         rows: 3,
 
         rows: 3,
 
         autosize: true
 
         autosize: true
 
       }, {
 
       }, {
 
         getValue: 'getValue',
 
         getValue: 'getValue',
         OOobject: new OO.ui.TextInputWidget(fieldOptions)
+
         OOobject: new OO.ui.MultilineTextInputWidget(fieldOptions)
 
       }) : type === 'boolean' ? {
 
       }) : type === 'boolean' ? {
 
         getValue: 'getValue',
 
         getValue: 'getValue',
Рядок 1425: Рядок 1199:
 
     $($element).addClass('editedSlot');
 
     $($element).addClass('editedSlot');
 
     return $element;
 
     return $element;
 +
  };
 +
 +
  Slot.prototype.toJSON = function() {
 +
    var defaults, res, sanitized;
 +
    defaults = new this.constructor;
 +
    defaults = defaults.sanitizedAttributes();
 +
    sanitized = this.sanitizedAttributes();
 +
    res = {};
 +
    Object.keys(sanitized).forEach((function(_this) {
 +
      return function(key) {
 +
        if ((sanitized[key] !== defaults[key]) && defaults.hasOwnProperty(key)) {
 +
          return res[key] = sanitized[key];
 +
        }
 +
      };
 +
    })(this));
 +
    res['class'] = this['class'];
 +
    delete res['id'];
 +
    return res;
 +
  };
 +
 +
  Slot.prototype.sanitizedAttributes = function() {
 +
    return rast.clone(this);
 
   };
 
   };
  
Рядок 1469: Рядок 1265:
 
     $elem.attr('data-id', this.id);
 
     $elem.attr('data-id', this.id);
 
     $elem.attr('style', styles || this.css);
 
     $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 $elem;
 
   };
 
   };
Рядок 1517: Рядок 1307:
 
         "default": '$',
 
         "default": '$',
 
         labelAlignment: 'top',
 
         labelAlignment: 'top',
         help: 'Символ долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення. \nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'
+
         help: 'Символ долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення.\nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'
 
       }, {
 
       }, {
 
         name: 'useClickFunc',
 
         name: 'useClickFunc',
Рядок 1527: Рядок 1317:
 
         caption: 'Інша дія (при клацанні)',
 
         caption: 'Інша дія (при клацанні)',
 
         type: 'text',
 
         type: 'text',
         "default": 'function(){  }',
+
         "default": '',
 
         labelAlignment: 'top'
 
         labelAlignment: 'top'
 
       }
 
       }
Рядок 1540: Рядок 1330:
 
   };
 
   };
  
   InsertionSlot.prototype.toJSON = function() {
+
   InsertionSlot.prototype.sanitizedAttributes = function() {
 
     var copy;
 
     var copy;
 
     copy = rast.clone(this);
 
     copy = rast.clone(this);
     if (this.clickFunc) {
+
     copy.clickFunc = $.trim(this.clickFunc);
      copy.clickFunc = this.clickFunc.toString();
 
    }
 
 
     return copy;
 
     return copy;
 
   };
 
   };
Рядок 1552: Рядок 1340:
 
     var $elem;
 
     var $elem;
 
     $elem = InsertionSlot.__super__.generateEditHtml.call(this);
 
     $elem = InsertionSlot.__super__.generateEditHtml.call(this);
     $elem.attr('title', (this.useClickFunc && this.clickFunc.toString()) || this.insertion);
+
     $elem.attr('title', (this.useClickFunc && this.clickFunc) || this.insertion);
 
     return $elem.append($('<div class="overlay">'));
 
     return $elem.append($('<div class="overlay">'));
 
   };
 
   };
Рядок 1562: Рядок 1350:
 
       $elem.append(this.caption);
 
       $elem.append(this.caption);
 
       $elem.attr('data-id', this.id);
 
       $elem.attr('data-id', this.id);
       if (styles) {
+
       if (styles && styles.length) {
 
         $elem.attr('style', styles);
 
         $elem.attr('style', styles);
      }
 
      if (this.bold) {
 
        $elem.css('font-weight', 'bold');
 
      }
 
      if (this.italic) {
 
        $elem.css('font-style', 'italic');
 
 
       }
 
       }
 
       return $elem;
 
       return $elem;
Рядок 1575: Рядок 1357:
 
       $a = $('<a>');
 
       $a = $('<a>');
 
       $a.attr('data-id', this.id);
 
       $a.attr('data-id', this.id);
       if (styles) {
+
       if (styles && styles.length) {
 
         $a.attr('style', styles);
 
         $a.attr('style', styles);
 
       }
 
       }
 
       caption = $('<div/>').text(this.caption).html();
 
       caption = $('<div/>').text(this.caption).html();
 
       $a.html(caption);
 
       $a.html(caption);
      if (this.bold) {
 
        $a.css('font-weight', 'bold');
 
      }
 
      if (this.italic) {
 
        $a.css('font-style', 'italic');
 
      }
 
 
       return $a;
 
       return $a;
 
     }
 
     }
Рядок 1597: Рядок 1373:
 
         event.preventDefault();
 
         event.preventDefault();
 
         if (_this.useClickFunc) {
 
         if (_this.useClickFunc) {
           return eval('(' + _this.clickFunc + ')()');
+
           return eval(_this.clickFunc);
 
         } else {
 
         } else {
 
           return rast.InsertionSlot.insertFunc(_this.insertion);
 
           return rast.InsertionSlot.insertFunc(_this.insertion);
Рядок 1635: Рядок 1411:
 
         "default": 'вставка_1 ·п вставка_2',
 
         "default": 'вставка_1 ·п вставка_2',
 
         labelAlignment: 'top',
 
         labelAlignment: 'top',
         help: 'Все, що розділене символами пробілу, вважається окремою коміркою. \nЯкщо комірка закірчується символом "п", вона вважатиметься не вставкою, а простим текстом. \nЯкщо хочете включити пробіл у вставку, пишіть нижнє підкреслення: "_". \nСимвол долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення. \nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'
+
         help: 'Все, що розділене символами пробілу, вважається окремою коміркою.\nЯкщо комірка закірчується символом "п", вона вважатиметься не вставкою, а простим текстом.\nЯкщо хочете включити пробіл у вставку, пишіть нижнє підкреслення: "_".\nСимвол долара "$" буде замінено на виділений текст. Перший символ додавання "+" позначає місце каретки після вставлення.\nЯкщо хочете екранувати ці символи, поставте "\\" перед потрібним символом; наприклад "\\$" вставлятиме знак долара.'
 
       }
 
       }
 
     ]
 
     ]
Рядок 1659: Рядок 1435:
 
     $elem = $('<div>');
 
     $elem = $('<div>');
 
     $elem.attr('data-id', this.id);
 
     $elem.attr('data-id', this.id);
     if (styles) {
+
     if (styles && styles.length) {
 
       $elem.attr('style', 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++) {
 
     for (k = 0, len1 = slots.length; k < len1; k++) {
Рядок 1677: Рядок 1447:
  
 
   MultipleInsertionsSlot.prototype.generateHtml = function(styles) {
 
   MultipleInsertionsSlot.prototype.generateHtml = function(styles) {
     var $elem;
+
     return this.generateCommonHtml(styles || this.css);
    $elem = this.generateCommonHtml(styles || this.css);
 
    return $elem;
 
 
   };
 
   };
  
Рядок 1705: Рядок 1473:
 
         name: 'onload',
 
         name: 'onload',
 
         type: 'text',
 
         type: 'text',
         "default": 'function(){  }',
+
         "default": '',
 
         caption: 'JavaScript, що виконається при ініціалізації',
 
         caption: 'JavaScript, що виконається при ініціалізації',
 
         labelAlignment: 'top'
 
         labelAlignment: 'top'
Рядок 1712: Рядок 1480:
 
   });
 
   });
  
   HtmlSlot.prototype.toJSON = function() {
+
   HtmlSlot.prototype.sanitizedAttributes = function() {
 
     var copy;
 
     var copy;
 
     copy = rast.clone(this);
 
     copy = rast.clone(this);
     if (this.onload) {
+
     copy.onload = $.trim(this.onload);
      copy.onload = this.onload.toString();
 
    }
 
 
     return copy;
 
     return copy;
 
   };
 
   };
Рядок 1723: Рядок 1489:
 
   function HtmlSlot(options) {
 
   function HtmlSlot(options) {
 
     HtmlSlot.__super__.constructor.call(this, options);
 
     HtmlSlot.__super__.constructor.call(this, options);
     if (typeof this.onload === 'string') {
+
     this.onload = $.trim(this.onload);
      this.onload = eval('(' + this.onload + ')');
+
     if (this.onload.length) {
    }
+
       editTools.addOnloadFunc((function(_this) {
     if (typeof this.onload === 'function') {
+
        return function() {
       editTools.addOnloadFunc(this.onload);
+
          try {
 +
            return eval(_this.onload);
 +
          } catch (_error) {}
 +
        };
 +
      })(this));
 
     }
 
     }
 
   }
 
   }
Рядок 1756: Рядок 1526:
  
 
   PageStorage.load = function(pagename, onLoaded, handler) {
 
   PageStorage.load = function(pagename, onLoaded, handler) {
     return mw.loader.using('mediawiki.api.edit', function() {
+
     var api;
      var api;
+
    api = new mw.Api;
      api = new mw.Api;
+
    return api.get({
      return api.get({
+
      action: 'query',
        action: 'query',
+
      prop: 'revisions',
        prop: 'revisions',
+
      rvprop: 'content',
        rvprop: 'content',
+
      titles: pagename
        titles: pagename
+
    }).done(function(data) {
      }).done(function(data) {
+
      var pageId, results1;
        var pageId, results1;
+
      results1 = [];
        results1 = [];
+
      for (pageId in data.query.pages) {
        for (pageId in data.query.pages) {
+
        if (data.query.pages[pageId].revisions) {
          if (data.query.pages[pageId].revisions) {
+
          results1.push(typeof onLoaded === "function" ? onLoaded(data.query.pages[pageId].revisions[0]['*']) : void 0);
            results1.push(typeof onLoaded === "function" ? onLoaded(data.query.pages[pageId].revisions[0]['*']) : void 0);
+
        } else {
          } else {
+
          results1.push(typeof handler.onSubpageNotFound === "function" ? handler.onSubpageNotFound(pageId) : void 0);
            results1.push(typeof handler.onSubpageNotFound === "function" ? handler.onSubpageNotFound(pageId) : void 0);
 
          }
 
 
         }
 
         }
        return results1;
+
      }
      }).fail(function() {
+
      return results1;
        return handler.onReadFromSubpageError();
+
    }).fail(function() {
      }).always(function() {
+
      return handler.onReadFromSubpageError();
        return handler.onEndReadingSubpage();
+
    }).always(function() {
      });
+
      return handler.onEndReadingSubpage();
 
     });
 
     });
 
   };
 
   };
  
   PageStorage.save = function(pagename, string, handler) {
+
   PageStorage.save = function(pagename, string, handler, summary) {
     return mw.loader.using('mediawiki.api.edit', function() {
+
     var api;
      var api;
+
    api = new mw.Api;
      api = new mw.Api;
+
    return api.postWithEditToken({
      return api.postWithEditToken({
+
      action: 'edit',
        action: 'edit',
+
      title: pagename,
        title: pagename,
+
      summary: summary,
        summary: 'serialize tools',
+
      text: string
        text: string
+
    }).done(function() {
      }).done(function() {
+
      return handler.onSavedToSubpage(pagename);
        return handler.onSavedToSubpage();
+
    }).fail(function() {
      }).fail(function() {
+
      return handler.onSaveToSubpageError(pagename);
        return handler.onSaveToSubpageError();
+
    }).always(function() {
      }).always(function() {
+
      return handler.onEndSavingToSubpage();
        return handler.onEndSavingToSubpage();
 
      });
 
 
     });
 
     });
 
   };
 
   };
Рядок 1808: Рядок 1574:
 
$(function() {
 
$(function() {
 
   window.editTools = {
 
   window.editTools = {
    hotkeys: [],
 
 
     onloadFuncs: [],
 
     onloadFuncs: [],
 
     mode: 'view',
 
     mode: 'view',
Рядок 1826: Рядок 1591:
 
       return results1;
 
       return results1;
 
     },
 
     },
     checkHotkey: function(e) {
+
     extraCSS: '#edittools .etPanel { margin-top: 5px; }\n#edittools .etPanel .slots [data-id] { margin: -1px -1px 0px 0px; }\n#edittools .etPanel .slots [data-id]:hover { z-index: 1; text-decoration: none; }\n#edittools .etPanel > [data-id], #edittools .etPanel .preview [data-id] { display: inline; padding: 0px 2px; }\n#edittools .etPanel > a[data-id], #edittools .etPanel .preview a[data-id] { cursor: pointer; }\n#edittools { min-height: 20px; }\n#edittools .rastMenu.view { position: absolute; left: -5px; }\n#edittools .rastMenu.edit { border-bottom: solid #aaaaaa 1px; padding: 2px 6px; }\n#edittools .slots.ui-sortable { min-height: 4em; border-width: 1px; border-style: dashed; margin: 5px 0px; background-color: white; }\n#edittools .slots.ui-sortable .emptyHint {  }\n#edittools .editedSlot {\n  cursor: pointer;\n  min-width: 1em;\n  min-height: 1em;\n  border: 1px solid grey;\n  margin-left: -1px;\n  position: relative;\n  display: block;\n}\n#edittools .editedSlot .overlay { width: 100%; height: 100%; position: absolute; top: 0px; left: 0px; }\n#edittools .slotClasses { text-align: center; }\n#edittools .slotClass { cursor: copy; padding: 3px 5px; border: 1px solid grey; margin-left: 5px; }\n#edittools .panelRemoveButton, #edittools .menuButton { cursor: pointer; }\n#edittools .gear {\n  min-height: 15px;\n  min-width: 15px;\n  cursor: pointer;\n}\n#edittools .ui-state-highlight {\n  min-width: 1em;\n  min-height: 1em;\n  display: inline-block;\n}\n#edittools .ui-sortable-helper { min-width: 1em; min-height: 1em; }\n.specialchars-tabs {float: left; background: #E0E0E0; margin-right: 7px; }\n.specialchars-tabs a{ display: block; padding-left: 3px; }\n#edittools { border: solid #aaaaaa 1px; }\n.mw-editTools a{ cursor: pointer; }\n.overflowHidden { overflow: hidden; }\n.specialchars-tabs .asnav-selectedtab { background: #eaecf0; border-left: 2px solid #aaaaaa; }\n#edittools .highlighted { opacity: 0.5; }\n#edittools [data-id]:hover { border-color: red; }\n#edittools .notFoundWarning { padding: 4px; }\n#edittools .newPanelButton { padding: 4px; border-bottom: solid #aaaaaa 1px; }\n#edittools .removeIcon {\n  background-image: url(\'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Ambox_delete_soft.svg/15px-Ambox_delete_soft.svg.png?uselang=uk\');\n  display: inline-block;\n  width: 15px;\n  height: 15px;\n  margin: 0px 0px 2px 4px;\n  vertical-align: middle;\n}\n#edittools .panelNameLabel { margin-right: 5px; }\n#edittools .panelRemoveButton { margin-left: 20px; }\n#edittools .etPanel > .slots {\n  padding: 6px 1px;\n  border: 1px black dashed;\n  overflow: auto;\n  max-height: 240px;\n}\n.rastEditWindow .bottomButtons { margin-top: 10px; }\n\n#edittools .aboutLink { float: right; }\n}',
      var obj;
 
      if (e && e.ctrlKey) {
 
        obj = editTools.hotkeys[String.fromCharCode(e.which).toUpperCase()];
 
        if (obj) {
 
          if (typeof obj === 'object') {
 
            obj.trigger('click');
 
          } else {
 
            obj();
 
          }
 
          return false;
 
        }
 
      }
 
      return true;
 
    },
 
    extraCSS: '#edittools .etPanel .slots [data-id] { margin: -1px -1px 0px 0px; }\n#edittools .etPanel .slots [data-id]:hover { z-index: 1; text-decoration: none; }\n#edittools .etPanel .preview [data-id] { display: inline; padding: 0px 2px; cursor: pointer; }\n#edittools .etPanel > [data-id] { display: inline; padding: 0px 2px; cursor: pointer; }\n#edittools { min-height: 20px; } \n#edittools .rastMenu.view { position: absolute; left: 0px; } \n#edittools .rastMenu.edit { border-bottom: solid #aaaaaa 1px; padding: 2px 6px; } \n#edittools .slots.ui-sortable { min-height: 4em; border-width: 1px; border-style: dashed; margin: 5px 0px; } \n#edittools .slots.ui-sortable .emptyHint {  } \n#edittools .editedSlot { \n  cursor: pointer; \n  min-width: 1em;\n  min-height: 1em;\n  border: 1px solid grey;\n  margin-left: -1px;\n  position: relative;\n  display: block;\n}\n#edittools .editedSlot .overlay { width: 100%; height: 100%; position: absolute; top: 0px; left: 0px; } \n#edittools .slotClass { cursor: copy; } \n#edittools .panelRemoveButton, #edittools .menuButton { cursor: pointer; }\n#edittools .gear { \n  background-image: url(\'https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Simpleicons_Interface_gear-wheel-in-black.svg/15px-Simpleicons_Interface_gear-wheel-in-black.svg.png\'); \n  height: 15px;;\n  width: 15px; \n  background-repeat: no-repeat;\n background-size: cover;\n  display: inline-block; } \n#edittools .ui-state-highlight { \n  min-width: 1em; \n  min-height: 1em; \n  display: inline-block;\n} \n#edittools .ui-sortable-helper { min-width: 1em; min-height: 1em; } \n.specialchars-tabs {float: left; background: #E0E0E0; margin-right: 7px; } \n.specialchars-tabs a{ display: block; } \n#edittools { border: solid #aaaaaa 1px; } \n.mw-editTools a{ cursor: pointer; } \n.overflowHidden { overflow: hidden; } \n.specialchars-tabs .asnav-selectedtab{ background: #F0F0F0; } \n#edittools .highlighted { opacity: 0.5; }\n#edittools [data-id]:hover { border-color: red; }\n#edittools .notFoundWarning { padding: 4px; }\n#edittools .newPanelButton { padding: 4px; border-bottom: solid #aaaaaa 1px; }\n#edittools .removeIcon { \n  background-image: url(\'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Ambox_delete_soft.svg/15px-Ambox_delete_soft.svg.png?uselang=uk\');\n  display: inline-block;\n  width: 15px;\n  height: 15px;\n  margin: 0px 0px 2px 4px;\n  vertical-align: middle;\n}\n#edittools .panelNameLabel { margin-right: 5px; }\n#edittools .panelRemoveButton { margin-left: 20px; }\n#edittools .etPanel > .slots { \n  padding: 6px 1px;\n  border: 1px black dashed; \n  overflow: auto;\n  max-height: 240px;\n}\n.rastEditWindow .bottomButtons { margin-top: 10px; }\n\n#edittools .aboutLink { float: right; }\n}',
 
 
     appendExtraCSS: function() {
 
     appendExtraCSS: function() {
 
       mw.util.addCSS(this.extraCSS);
 
       mw.util.addCSS(this.extraCSS);
Рядок 1849: Рядок 1599:
 
     cookieName: 'edittool',
 
     cookieName: 'edittool',
 
     createEditTools: function() {
 
     createEditTools: function() {
       var $tabs, event, self;
+
       var $tabs, self;
 
       $tabs = $('<div></div>').attr('id', this.id);
 
       $tabs = $('<div></div>').attr('id', this.id);
      event = rast.ieVersion() < 9 ? 'mousedown' : 'click';
 
 
       self = this;
 
       self = this;
       $tabs.on(event, '.asnav-content .etPanel .slots [data-id]', function($e) {
+
       $tabs.on('click', '.asnav-content .etPanel .slots [data-id]', function($e) {
 
         var id, slot;
 
         var id, slot;
 
         if (editTools.mode === 'edit') {
 
         if (editTools.mode === 'edit') {
Рядок 1896: Рядок 1645:
 
       this.reset();
 
       this.reset();
 
       this.subsets.readEncodedSubsets(obj);
 
       this.subsets.readEncodedSubsets(obj);
       this.subsetsUpdated();
+
       this.resetTemporarySubsets();
 
       this.refresh();
 
       this.refresh();
 
       return true;
 
       return true;
 
     },
 
     },
     subsetsUpdated: function() {
+
     resetTemporarySubsets: function() {
      return this.temporarySubsets = rast.clone(this.subsets, false);
 
    },
 
    undoChanges: function() {
 
 
       return this.temporarySubsets = rast.clone(this.subsets);
 
       return this.temporarySubsets = rast.clone(this.subsets);
 
     },
 
     },
Рядок 1931: Рядок 1677:
 
     save: function() {
 
     save: function() {
 
       this.subsets = this.temporarySubsets;
 
       this.subsets = this.temporarySubsets;
       this.subsetsUpdated();
+
       this.resetTemporarySubsets();
 
       return this.view();
 
       return this.view();
 
     },
 
     },
 
     restoreDefaults: function() {
 
     restoreDefaults: function() {
       return this.readFromSubpage('User:Адмін/defaults.js');
+
       return this.readFromSubpage('User:AS/defaults.js');
 
     },
 
     },
 
     init: function() {
 
     init: function() {
 
       var $placeholder, $tabs, etActiveTab;
 
       var $placeholder, $tabs, etActiveTab;
      this.subsets = new rast.SubsetsManager;
 
      this.temporarySubsets = new rast.SubsetsManager;
 
 
       $tabs = $('#' + this.id);
 
       $tabs = $('#' + this.id);
 
       etActiveTab = $tabs.find('.existingTabs .asnav-selectedtab').attr('data-contentid') || mw.cookie.get(editTools.cookieName + 'Selected') || 'etTabContent0';
 
       etActiveTab = $tabs.find('.existingTabs .asnav-selectedtab').attr('data-contentid') || mw.cookie.get(editTools.cookieName + 'Selected') || 'etTabContent0';
Рядок 1950: Рядок 1694:
 
       this.onCancelClick = (function(_this) {
 
       this.onCancelClick = (function(_this) {
 
         return function() {
 
         return function() {
           _this.undoChanges();
+
           _this.resetTemporarySubsets();
 
           return _this.view();
 
           return _this.view();
 
         };
 
         };
Рядок 2007: Рядок 1751:
 
         docLink: this.docLink,
 
         docLink: this.docLink,
 
         onTabClick: null,
 
         onTabClick: null,
         eventsHandler: this,
+
         eventsHandler: this
        slotClasses: [rast.PlainTextSlot, rast.InsertionSlot, rast.MultipleInsertionsSlot, rast.HtmlSlot]
 
 
       });
 
       });
 
       $placeholder = $(this.parentId);
 
       $placeholder = $(this.parentId);
Рядок 2016: Рядок 1759:
 
       this.appendExtraCSS();
 
       this.appendExtraCSS();
 
       $placeholder.empty().append(this.createEditTools());
 
       $placeholder.empty().append(this.createEditTools());
       $('input#wpSummary').attr('style', 'margin-bottom:3px;');
+
       $('input#wpSummary').attr('style', 'margin-bottom: 3px;');
 
       this.created = true;
 
       this.created = true;
 +
      this.temporarySubsets = new rast.SubsetsManager;
 
       return this.reload();
 
       return this.reload();
 
     },
 
     },
Рядок 2024: Рядок 1768:
 
       $tabs = $('#' + this.id);
 
       $tabs = $('#' + this.id);
 
       $tabs.throbber(true, 'prepend');
 
       $tabs.throbber(true, 'prepend');
       return this.readFromSubpage();
+
       return this.readFromSubpage(this.subpage(), (function(_this) {
 +
        return function() {
 +
          _this.subsets = rast.clone(_this.temporarySubsets);
 +
          return _this.refresh();
 +
        };
 +
      })(this));
 
     },
 
     },
 
     docLink: 'https://uk.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D0%B8%D1%81%D1%82%D1%83%D0%B2%D0%B0%D1%87:AS/%D0%9F%D0%9F%D0%A1-2',
 
     docLink: 'https://uk.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D0%B8%D1%81%D1%82%D1%83%D0%B2%D0%B0%D1%87:AS/%D0%9F%D0%9F%D0%A1-2',
Рядок 2035: Рядок 1784:
 
     },
 
     },
 
     onSubpageNotFound: function() {
 
     onSubpageNotFound: function() {
       if (!this.readFromSpecialSyntaxObject(window.etSubsets)) {
+
       return this.showMessage("<div class=\"notFoundWarning\">Це повідомлення від додатка <a href=\"" + this.docLink + "\">Покращеної панелі спецсимволів</a> (Налаштування -> Додатки -> Редагування). Підсторінку із символами не знайдено або не вдалося завантажити. Це нормально, якщо ви ще не зберегли жодну версію. Натисніть зліва від панелі на " + (this.editButtonHtml()) + ", щоб редагувати символи.</div>");
        return this.showMessage("<div class=\"notFoundWarning\">Це повідомлення від додатка <a href=\"" + this.docLink + "\">Покращеної панелі спецсимволів</a> (Налаштування -> Додатки -> Редагування). Підсторінку із символами не знайдено або не вдалося завантажити. Це нормально, якщо ви ще не зберегли жодну версію. Натисніть зліва від панелі на " + (this.editButtonHtml()) + ", щоб редагувати символи.</div>");
 
      }
 
 
     },
 
     },
 
     serialize: function() {
 
     serialize: function() {
Рядок 2044: Рядок 1791:
 
     subpageStorageName: 'AStools.js',
 
     subpageStorageName: 'AStools.js',
 
     saveToSubpage: function() {
 
     saveToSubpage: function() {
       return this.serializeToPage('User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName);
+
       return this.serializeToPage(this.subpage(), '[[Обговорення користувача:AS/rast.js|serialize]]');
 +
    },
 +
    subpage: function() {
 +
      return 'User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName;
 
     },
 
     },
     trackingPage: 'User:Адмін/ToolsTrack',
+
     trackingPage: 'User:AS/track',
 
     serializeToPage: function(pagename) {
 
     serializeToPage: function(pagename) {
 
       var serializedTools;
 
       var serializedTools;
Рядок 2055: Рядок 1805:
 
       return 'User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName;
 
       return 'User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName;
 
     },
 
     },
     readFromSubpage: function(pagename) {
+
     readFromSubpage: function(pagename, doneFunc) {
 
       var json;
 
       var json;
      this.reset();
 
 
       return json = rast.PageStorage.load(pagename || this.subpageName(), (function(_this) {
 
       return json = rast.PageStorage.load(pagename || this.subpageName(), (function(_this) {
 
         return function(pagetext) {
 
         return function(pagetext) {
Рядок 2063: Рядок 1812:
 
           pagetextWithoutNowiki = pagetext.replace(/^(\[\[[^\]]+\]\])?<nowiki>/, '').replace(/<\/nowiki>$/, '');
 
           pagetextWithoutNowiki = pagetext.replace(/^(\[\[[^\]]+\]\])?<nowiki>/, '').replace(/<\/nowiki>$/, '');
 
           serializedTools = JSON.parse(pagetextWithoutNowiki);
 
           serializedTools = JSON.parse(pagetextWithoutNowiki);
           _this.subsets.deserialize(serializedTools);
+
           _this.temporarySubsets.reset();
           _this.subsetsUpdated();
+
          _this.temporarySubsets.deserialize(serializedTools);
           return _this.refresh();
+
           _this.refresh();
 +
           if (doneFunc != null) {
 +
            return doneFunc();
 +
          }
 
         };
 
         };
 
       })(this), this);
 
       })(this), this);
Рядок 2077: Рядок 1829:
 
       return $tabs.throbber(false);
 
       return $tabs.throbber(false);
 
     },
 
     },
     onSavedToSubpage: function() {
+
     onSavedToSubpage: function(pagename) {
      var pagename;
 
      pagename = this.subpageName();
 
 
       return mw.notify($("<span>Збережено на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
 
       return mw.notify($("<span>Збережено на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
 
     },
 
     },
     onSaveToSubpageError: function() {
+
     onSaveToSubpageError: function(pagename) {
      var pagename;
 
      pagename = this.subpageName();
 
 
       return mw.notify($("<span>Не вдалося зберегти на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
 
       return mw.notify($("<span>Не вдалося зберегти на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
 
     },
 
     },
Рядок 2094: Рядок 1842:
 
     setupOnEditPage: function() {
 
     setupOnEditPage: function() {
 
       if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit') {
 
       if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit') {
         return mw.loader.using(['mediawiki.cookie', 'oojs-ui', 'jquery.colorUtil'], function() {
+
         rast.installJQueryPlugins();
          rast.installJQueryPlugins();
+
        return editTools.init();
          return editTools.init();
 
        });
 
 
       }
 
       }
 
     },
 
     },
Рядок 2104: Рядок 1850:
 
     }
 
     }
 
   };
 
   };
  rast.PlainObjectParser.processShortcut = editTools.processShortcut;
 
 
   rast.PlainObjectParser.addOnloadFunc = editTools.addOnloadFunc;
 
   rast.PlainObjectParser.addOnloadFunc = editTools.addOnloadFunc;
   return editTools.setupOnEditPage();
+
   return $(function() {
 +
    return mw.loader.using(['mediawiki.cookie', 'oojs-ui', 'mediawiki.api', 'jquery.ui.sortable', 'jquery.ui.droppable', 'jquery.ui.draggable'], function() {
 +
      return editTools.setupOnEditPage();
 +
    });
 +
  });
 
});
 
});

Поточна версія на 14:44, 13 грудня 2019

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.rast = {
  clone: function(object) {
    return $.extend(true, {}, object);
  },
  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;
  },
  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');
  },
  searchAndReplace: {
    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, #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.util.escapeRegExp(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();
      }
    }
  }
};

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 $descLabel, $preview, $previewContent, $slots, generateMethod, layout, nameInput, nameLabel, removeButton;
    nameLabel = new OO.ui.LabelWidget({
      label: 'Назва панелі:'
    });
    nameInput = new OO.ui.TextInputWidget({
      value: this.subsetWrapper.caption
    });
    nameInput.$element.on('keydown', function(event) {
      if (event.keyCode === 13) {
        event.preventDefault();
        return false;
      }
    });
    nameInput.$element.change({
      subsetWrapper: this.subsetWrapper
    }, this.eventsHandler.onTabNameChanged);
    removeButton = new OO.ui.ButtonWidget({
      label: 'Вилучити цю панель',
      flags: 'destructive'
    });
    removeButton.on('click', (function(_this) {
      return function() {
        return _this.eventsHandler.onRemoveSubsetClick(_this.subsetWrapper);
      };
    })(this));
    layout = new OO.ui.HorizontalLayout({
      items: [nameLabel, nameInput, removeButton]
    });
    this.$panel.append(layout.$element);
    $descLabel = $('<span>');
    $descLabel.text('Ви можете перетягувати комірки, щоб змінити їхній порядок. Клацніть по комірці, щоб редагувати її. Щоб додати комірку, перетягніть нижче один з цих видів:');
    this.$panel.append($descLabel);
    this.drawSlotClasses(this.$panel);
    $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);
  };

  PanelDrawer.prototype.drawSlotClasses = function($container) {
    var $slot, $slots, k, len1, ref, slotClass;
    $slots = $('<div class="slotClasses">');
    ref = this.slotClasses();
    for (k = 0, len1 = ref.length; k < len1; k++) {
      slotClass = ref[k];
      $slot = $('<span class="slotClass">');
      $slot.attr('data-slot-class', rast.name(slotClass));
      $slot.text(slotClass.caption);
      $slot.attr('title', 'Перетягніть на панель, щоб вставити цей вид комірки');
      $slots.append($slot);
    }
    $container.append($slots);
    return $slots.find('.slotClass').draggable({
      connectToSortable: '.etPanel .slots',
      helper: 'clone'
    });
  };

  PanelDrawer.prototype.slotClasses = function() {
    return [rast.PlainTextSlot, rast.InsertionSlot, rast.MultipleInsertionsSlot, rast.HtmlSlot];
  };

  return PanelDrawer;

})();

rast.Drawer = (function() {
  function Drawer() {}

  Drawer.prototype.$editButton = function() {
    var icon;
    icon = new OO.ui.IconWidget({
      icon: 'settings',
      title: 'Редагувати символи',
      classes: ['gear']
    });
    return icon.$element;
  };

  Drawer.prototype.drawMenu = function() {
    var $aboutLink, $editButton, $menu, cancelButton, 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') {
      persistButton = new OO.ui.ButtonWidget({
        label: ' Зберегти на постійно',
        title: 'Символи буде збережено на підсторінку у Вашому просторі користувача.',
        icon: 'checkAll'
      });
      persistButton.on('click', this.eventsHandler.onPersistClick);
      saveButton = new OO.ui.ButtonWidget({
        label: ' Зберегти тимчасово',
        title: 'Зміни збережуться тільки на час редагування сторінки і втратяться після закриття або перевантаження сторінки.',
        icon: 'check'
      });
      saveButton.on('click', this.eventsHandler.onSaveClick);
      cancelButton = new OO.ui.ButtonWidget({
        label: ' Скасувати',
        title: 'Всі зміни цієї сесії редагування будуть відкинуті.',
        icon: 'cancel'
      });
      cancelButton.on('click', this.eventsHandler.onCancelClick);
      resetButton = new OO.ui.ButtonWidget({
        label: ' Відновити звичаєві',
        title: 'Буде відновлено набір символів за промовчанням.',
        icon: 'reload'
      });
      resetButton.on('click', this.eventsHandler.onResetClick);
      $aboutLink = $("<a class=\"aboutLink\" target=\"_blank\" href=\"" + this.docLink + "\">про додаток</a>");
      $menu.append(persistButton.$element, saveButton.$element, cancelButton.$element, resetButton.$element, $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);
    }
    return this.$container.append($outline);
  };

  Drawer.prototype.draw = function() {
    this.$container.empty();
    this.drawMenu();
    this.drawMessage();
    this.drawNavigation();
    return this.drawPanels();
  };

  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) {
    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);
        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) {
    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;
      }
    }
    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.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 = [];
    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' ? (fieldOptions = {
        value: value
      }, {
        getValue: 'getValue',
        OOobject: new OO.ui.TextInputWidget(fieldOptions)
      }) : type === 'text' ? (fieldOptions = {
        value: value,
        rows: 3,
        autosize: true
      }, {
        getValue: 'getValue',
        OOobject: new OO.ui.MultilineTextInputWidget(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;
  };

  Slot.prototype.toJSON = function() {
    var defaults, res, sanitized;
    defaults = new this.constructor;
    defaults = defaults.sanitizedAttributes();
    sanitized = this.sanitizedAttributes();
    res = {};
    Object.keys(sanitized).forEach((function(_this) {
      return function(key) {
        if ((sanitized[key] !== defaults[key]) && defaults.hasOwnProperty(key)) {
          return res[key] = sanitized[key];
        }
      };
    })(this));
    res['class'] = this['class'];
    delete res['id'];
    return res;
  };

  Slot.prototype.sanitizedAttributes = function() {
    return rast.clone(this);
  };

  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);
    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": '',
        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.sanitizedAttributes = function() {
    var copy;
    copy = rast.clone(this);
    copy.clickFunc = $.trim(this.clickFunc);
    return copy;
  };

  InsertionSlot.prototype.generateEditHtml = function() {
    var $elem;
    $elem = InsertionSlot.__super__.generateEditHtml.call(this);
    $elem.attr('title', (this.useClickFunc && this.clickFunc) || 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 && styles.length) {
        $elem.attr('style', styles);
      }
      return $elem;
    } else {
      $a = $('<a>');
      $a.attr('data-id', this.id);
      if (styles && styles.length) {
        $a.attr('style', styles);
      }
      caption = $('<div/>').text(this.caption).html();
      $a.html(caption);
      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 && styles.length) {
      $elem.attr('style', styles);
    }
    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) {
    return this.generateCommonHtml(styles || this.css);
  };

  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": '',
        caption: 'JavaScript, що виконається при ініціалізації',
        labelAlignment: 'top'
      }
    ]
  });

  HtmlSlot.prototype.sanitizedAttributes = function() {
    var copy;
    copy = rast.clone(this);
    copy.onload = $.trim(this.onload);
    return copy;
  };

  function HtmlSlot(options) {
    HtmlSlot.__super__.constructor.call(this, options);
    this.onload = $.trim(this.onload);
    if (this.onload.length) {
      editTools.addOnloadFunc((function(_this) {
        return function() {
          try {
            return eval(_this.onload);
          } catch (_error) {}
        };
      })(this));
    }
  }

  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) {
    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();
    });
  };

  PageStorage.save = function(pagename, string, handler, summary) {
    var api;
    api = new mw.Api;
    return api.postWithEditToken({
      action: 'edit',
      title: pagename,
      summary: summary,
      text: string
    }).done(function() {
      return handler.onSavedToSubpage(pagename);
    }).fail(function() {
      return handler.onSaveToSubpageError(pagename);
    }).always(function() {
      return handler.onEndSavingToSubpage();
    });
  };

  return PageStorage;

})();

$(function() {
  window.editTools = {
    onloadFuncs: [],
    mode: 'view',
    addOnloadFunc: (function(_this) {
      return function(func) {
        return editTools.onloadFuncs.push(func);
      };
    })(this),
    fireOnloadFuncs: function() {
      var func, k, len1, ref, results1;
      ref = editTools.onloadFuncs;
      results1 = [];
      for (k = 0, len1 = ref.length; k < len1; k++) {
        func = ref[k];
        results1.push(func());
      }
      return results1;
    },
    extraCSS: '#edittools .etPanel { margin-top: 5px; }\n#edittools .etPanel .slots [data-id] { margin: -1px -1px 0px 0px; }\n#edittools .etPanel .slots [data-id]:hover { z-index: 1; text-decoration: none; }\n#edittools .etPanel > [data-id], #edittools .etPanel .preview [data-id] { display: inline; padding: 0px 2px; }\n#edittools .etPanel > a[data-id], #edittools .etPanel .preview a[data-id] { cursor: pointer; }\n#edittools { min-height: 20px; }\n#edittools .rastMenu.view { position: absolute; left: -5px; }\n#edittools .rastMenu.edit { border-bottom: solid #aaaaaa 1px; padding: 2px 6px; }\n#edittools .slots.ui-sortable { min-height: 4em; border-width: 1px; border-style: dashed; margin: 5px 0px; background-color: white; }\n#edittools .slots.ui-sortable .emptyHint {  }\n#edittools .editedSlot {\n  cursor: pointer;\n  min-width: 1em;\n  min-height: 1em;\n  border: 1px solid grey;\n  margin-left: -1px;\n  position: relative;\n  display: block;\n}\n#edittools .editedSlot .overlay { width: 100%; height: 100%; position: absolute; top: 0px; left: 0px; }\n#edittools .slotClasses { text-align: center; }\n#edittools .slotClass { cursor: copy; padding: 3px 5px; border: 1px solid grey; margin-left: 5px; }\n#edittools .panelRemoveButton, #edittools .menuButton { cursor: pointer; }\n#edittools .gear {\n  min-height: 15px;\n  min-width: 15px;\n  cursor: pointer;\n}\n#edittools .ui-state-highlight {\n  min-width: 1em;\n  min-height: 1em;\n  display: inline-block;\n}\n#edittools .ui-sortable-helper { min-width: 1em; min-height: 1em; }\n.specialchars-tabs {float: left; background: #E0E0E0; margin-right: 7px; }\n.specialchars-tabs a{ display: block; padding-left: 3px; }\n#edittools { border: solid #aaaaaa 1px; }\n.mw-editTools a{ cursor: pointer; }\n.overflowHidden { overflow: hidden; }\n.specialchars-tabs .asnav-selectedtab { background: #eaecf0; border-left: 2px solid #aaaaaa; }\n#edittools .highlighted { opacity: 0.5; }\n#edittools [data-id]:hover { border-color: red; }\n#edittools .notFoundWarning { padding: 4px; }\n#edittools .newPanelButton { padding: 4px; border-bottom: solid #aaaaaa 1px; }\n#edittools .removeIcon {\n  background-image: url(\'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Ambox_delete_soft.svg/15px-Ambox_delete_soft.svg.png?uselang=uk\');\n  display: inline-block;\n  width: 15px;\n  height: 15px;\n  margin: 0px 0px 2px 4px;\n  vertical-align: middle;\n}\n#edittools .panelNameLabel { margin-right: 5px; }\n#edittools .panelRemoveButton { margin-left: 20px; }\n#edittools .etPanel > .slots {\n  padding: 6px 1px;\n  border: 1px black dashed;\n  overflow: auto;\n  max-height: 240px;\n}\n.rastEditWindow .bottomButtons { margin-top: 10px; }\n\n#edittools .aboutLink { float: right; }\n}',
    appendExtraCSS: function() {
      mw.util.addCSS(this.extraCSS);
    },
    parentId: '.mw-editTools',
    id: 'edittools',
    cookieName: 'edittool',
    createEditTools: function() {
      var $tabs, self;
      $tabs = $('<div></div>').attr('id', this.id);
      self = this;
      $tabs.on('click', '.asnav-content .etPanel .slots [data-id]', function($e) {
        var id, slot;
        if (editTools.mode === 'edit') {
          id = parseInt($(this).closest('.editedSlot').attr('data-id'));
          slot = editTools.temporarySubsets.slotById(id);
          return editTools.editWindow(slot);
        }
      });
      return $tabs;
    },
    editWindow: function(slot) {
      var editor;
      editor = new slot.constructor.editorClass({
        slot: slot,
        slotsManager: this
      });
      return editor.startEditing();
    },
    onDeleteSlot: function(slotId) {
      var id;
      id = parseInt(slotId);
      this.temporarySubsets.deleteSlot(id);
      return this.refresh();
    },
    edit: function() {
      this.mode = 'edit';
      $('#' + this.id).find('.notFoundWarning').remove();
      return this.refresh();
    },
    view: function() {
      this.mode = 'view';
      return this.refresh();
    },
    reset: function() {
      this.onloadFuncs = [];
      return this.subsets.reset();
    },
    readFromSpecialSyntaxObject: function(obj) {
      if (!obj) {
        return false;
      }
      this.reset();
      this.subsets.readEncodedSubsets(obj);
      this.resetTemporarySubsets();
      this.refresh();
      return true;
    },
    resetTemporarySubsets: function() {
      return this.temporarySubsets = rast.clone(this.subsets);
    },
    refresh: function() {
      var $tabs, etActiveTab;
      if (!this.created) {
        return;
      }
      $tabs = $('#' + this.id);
      etActiveTab = $tabs.find('.existingTabs .asnav-selectedtab').attr('data-contentid') || mw.cookie.get(editTools.cookieName + 'Selected') || 'etTabContent0';
      this.drawer.$container = $tabs;
      this.drawer.mode = this.mode;
      this.drawer.subsets = this.temporarySubsets;
      this.drawer.message = this.message;
      this.message = null;
      this.drawer.activeTab = etActiveTab;
      this.drawer.draw();
      setTimeout((function(_this) {
        return function() {
          return _this.fireOnloadFuncs();
        };
      })(this), 0);
      return $tabs.on('asNav:select', function(ev, selectedId) {
        return mw.cookie.set(editTools.cookieName + 'Selected', selectedId);
      });
    },
    save: function() {
      this.subsets = this.temporarySubsets;
      this.resetTemporarySubsets();
      return this.view();
    },
    restoreDefaults: function() {
      return this.readFromSubpage('User:AS/defaults.js');
    },
    init: function() {
      var $placeholder, $tabs, etActiveTab;
      $tabs = $('#' + this.id);
      etActiveTab = $tabs.find('.existingTabs .asnav-selectedtab').attr('data-contentid') || mw.cookie.get(editTools.cookieName + 'Selected') || 'etTabContent0';
      this.onSaveClick = (function(_this) {
        return function() {
          return _this.save();
        };
      })(this);
      this.onCancelClick = (function(_this) {
        return function() {
          _this.resetTemporarySubsets();
          return _this.view();
        };
      })(this);
      this.onResetClick = (function(_this) {
        return function() {
          return _this.restoreDefaults();
        };
      })(this);
      this.onEditClick = (function(_this) {
        return function() {
          return _this.edit();
        };
      })(this);
      this.onTabNameChanged = (function(_this) {
        return function(event) {
          event.data.subsetWrapper.caption = $(event.target).val();
          return _this.refresh();
        };
      })(this);
      this.onAddSubsetClick = (function(_this) {
        return function() {
          var subset;
          subset = _this.temporarySubsets.addSubset('Нова панель', _this.temporarySubsets.subsets.length);
          _this.refresh();
          $tabs = $('#' + _this.id);
          return $tabs.asnavSelect('etTabContent' + subset.id);
        };
      })(this);
      this.onRemoveSubsetClick = (function(_this) {
        return function(subsetWrapper) {
          _this.temporarySubsets.deleteSubset(subsetWrapper);
          return _this.refresh();
        };
      })(this);
      this.onSlotAdded = (function(_this) {
        return function() {
          return _this.refresh();
        };
      })(this);
      this.onPersistClick = (function(_this) {
        return function() {
          _this.save();
          $tabs = $('#' + _this.id);
          $tabs.throbber(true, 'prepend');
          return _this.saveToSubpage();
        };
      })(this);
      this.onSlotRemoved = (function(_this) {
        return function() {
          return _this.refresh();
        };
      })(this);
      this.drawer = new rast.Drawer();
      $.extend(this.drawer, {
        docLink: this.docLink,
        onTabClick: null,
        eventsHandler: this
      });
      $placeholder = $(this.parentId);
      if (!$placeholder.length) {
        return;
      }
      this.appendExtraCSS();
      $placeholder.empty().append(this.createEditTools());
      $('input#wpSummary').attr('style', 'margin-bottom: 3px;');
      this.created = true;
      this.temporarySubsets = new rast.SubsetsManager;
      return this.reload();
    },
    reload: function() {
      var $tabs;
      $tabs = $('#' + this.id);
      $tabs.throbber(true, 'prepend');
      return this.readFromSubpage(this.subpage(), (function(_this) {
        return function() {
          _this.subsets = rast.clone(_this.temporarySubsets);
          return _this.refresh();
        };
      })(this));
    },
    docLink: 'https://uk.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D0%B8%D1%81%D1%82%D1%83%D0%B2%D0%B0%D1%87:AS/%D0%9F%D0%9F%D0%A1-2',
    editButtonHtml: function() {
      return this.drawer.$editButtonIcon().prop('outerHTML');
    },
    showMessage: function(html) {
      this.message = html;
      return this.refresh();
    },
    onSubpageNotFound: function() {
      return this.showMessage("<div class=\"notFoundWarning\">Це повідомлення від додатка <a href=\"" + this.docLink + "\">Покращеної панелі спецсимволів</a> (Налаштування -> Додатки -> Редагування). Підсторінку із символами не знайдено або не вдалося завантажити. Це нормально, якщо ви ще не зберегли жодну версію. Натисніть зліва від панелі на " + (this.editButtonHtml()) + ", щоб редагувати символи.</div>");
    },
    serialize: function() {
      return JSON.stringify(this.subsets, null, 2);
    },
    subpageStorageName: 'AStools.js',
    saveToSubpage: function() {
      return this.serializeToPage(this.subpage(), '[[Обговорення користувача:AS/rast.js|serialize]]');
    },
    subpage: function() {
      return 'User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName;
    },
    trackingPage: 'User:AS/track',
    serializeToPage: function(pagename) {
      var serializedTools;
      serializedTools = "[[" + this.trackingPage + "]]<nowiki>" + (this.serialize()) + "</nowiki>";
      return rast.PageStorage.save(pagename, serializedTools, this);
    },
    subpageName: function() {
      return 'User:' + mw.config.get('wgUserName') + '/' + this.subpageStorageName;
    },
    readFromSubpage: function(pagename, doneFunc) {
      var json;
      return json = rast.PageStorage.load(pagename || this.subpageName(), (function(_this) {
        return function(pagetext) {
          var pagetextWithoutNowiki, serializedTools;
          pagetextWithoutNowiki = pagetext.replace(/^(\[\[[^\]]+\]\])?<nowiki>/, '').replace(/<\/nowiki>$/, '');
          serializedTools = JSON.parse(pagetextWithoutNowiki);
          _this.temporarySubsets.reset();
          _this.temporarySubsets.deserialize(serializedTools);
          _this.refresh();
          if (doneFunc != null) {
            return doneFunc();
          }
        };
      })(this), this);
    },
    onReadFromSubpageError: function() {
      return this.showMessage("<div class=\"readingSubpageError\">Не вдалося завантажити підсторінку з символами.</div>");
    },
    onEndReadingSubpage: function() {
      var $tabs;
      $tabs = $('#' + this.id);
      return $tabs.throbber(false);
    },
    onSavedToSubpage: function(pagename) {
      return mw.notify($("<span>Збережено на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
    },
    onSaveToSubpageError: function(pagename) {
      return mw.notify($("<span>Не вдалося зберегти на <a href='" + (mw.util.getUrl(pagename)) + "'>" + pagename + "</a></span>"));
    },
    onEndSavingToSubpage: function() {
      var $tabs;
      $tabs = $('#' + this.id);
      return $tabs.throbber(false);
    },
    setupOnEditPage: function() {
      if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit') {
        rast.installJQueryPlugins();
        return editTools.init();
      }
    },
    onSlotSaved: function() {
      return this.refresh();
    }
  };
  rast.PlainObjectParser.addOnloadFunc = editTools.addOnloadFunc;
  return $(function() {
    return mw.loader.using(['mediawiki.cookie', 'oojs-ui', 'mediawiki.api', 'jquery.ui.sortable', 'jquery.ui.droppable', 'jquery.ui.draggable'], function() {
      return editTools.setupOnEditPage();
    });
  });
});