diff --git a/app/assets/javascripts/app/lib/base/jquery.caret.js b/app/assets/javascripts/app/lib/base/jquery.caret.js deleted file mode 100644 index 651e037c6..000000000 --- a/app/assets/javascripts/app/lib/base/jquery.caret.js +++ /dev/null @@ -1,367 +0,0 @@ -//@ sourceMappingURL=jquery.caret.map -/* - Implement Github like autocomplete mentions - http://ichord.github.com/At.js - - Copyright (c) 2013 chord.luo@gmail.com - Licensed under the MIT license. -*/ - - -/* -本插件操作 textarea 或者 input 内的插入符 -只实现了获得插入符在文本框中的位置,我设置 -插入符的位置. -*/ - - -(function() { - (function(factory) { - if (typeof define === 'function' && define.amd) { - return define(['jquery'], factory); - } else { - return factory(window.jQuery); - } - })(function($) { - "use strict"; - var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy; - pluginName = 'caret'; - EditableCaret = (function() { - function EditableCaret($inputor) { - this.$inputor = $inputor; - this.domInputor = this.$inputor[0]; - } - - EditableCaret.prototype.setPos = function(pos) { - return this.domInputor; - }; - - EditableCaret.prototype.getIEPosition = function() { - return this.getPosition(); - }; - - EditableCaret.prototype.getPosition = function() { - var inputor_offset, offset; - offset = this.getOffset(); - inputor_offset = this.$inputor.offset(); - offset.left -= inputor_offset.left; - offset.top -= inputor_offset.top; - return offset; - }; - - EditableCaret.prototype.getOldIEPos = function() { - var preCaretTextRange, textRange; - textRange = oDocument.selection.createRange(); - preCaretTextRange = oDocument.body.createTextRange(); - preCaretTextRange.moveToElementText(this.domInputor); - preCaretTextRange.setEndPoint("EndToEnd", textRange); - return preCaretTextRange.text.length; - }; - - EditableCaret.prototype.getPos = function() { - var clonedRange, pos, range; - if (range = this.range()) { - clonedRange = range.cloneRange(); - clonedRange.selectNodeContents(this.domInputor); - clonedRange.setEnd(range.endContainer, range.endOffset); - pos = clonedRange.toString().length; - clonedRange.detach(); - return pos; - } else if (oDocument.selection) { - return this.getOldIEPos(); - } - }; - - EditableCaret.prototype.getOldIEOffset = function() { - var range, rect; - range = oDocument.selection.createRange().duplicate(); - range.moveStart("character", -1); - rect = range.getBoundingClientRect(); - return { - height: rect.bottom - rect.top, - left: rect.left, - top: rect.top - }; - }; - - EditableCaret.prototype.getOffset = function(pos) { - var clonedRange, offset, range, rect; - if (oWindow.getSelection && (range = this.range())) { - if (range.endOffset - 1 < 0) { - return null; - } - clonedRange = range.cloneRange(); - clonedRange.setStart(range.endContainer, range.endOffset - 1); - clonedRange.setEnd(range.endContainer, range.endOffset); - rect = clonedRange.getBoundingClientRect(); - offset = { - height: rect.height, - left: rect.left + rect.width, - top: rect.top - }; - clonedRange.detach(); - } else if (oDocument.selection) { - offset = this.getOldIEOffset(); - } - if (offset) { - offset.top += $(oWindow).scrollTop(); - offset.left += $(oWindow).scrollLeft(); - } - return offset; - }; - - EditableCaret.prototype.range = function() { - var sel; - if (!oWindow.getSelection) { - return; - } - sel = oWindow.getSelection(); - if (sel.rangeCount > 0) { - return sel.getRangeAt(0); - } else { - return null; - } - }; - - return EditableCaret; - - })(); - InputCaret = (function() { - function InputCaret($inputor) { - this.$inputor = $inputor; - this.domInputor = this.$inputor[0]; - } - - InputCaret.prototype.getIEPos = function() { - var endRange, inputor, len, normalizedValue, pos, range, textInputRange; - inputor = this.domInputor; - range = oDocument.selection.createRange(); - pos = 0; - if (range && range.parentElement() === inputor) { - normalizedValue = inputor.value.replace(/\r\n/g, "\n"); - len = normalizedValue.length; - textInputRange = inputor.createTextRange(); - textInputRange.moveToBookmark(range.getBookmark()); - endRange = inputor.createTextRange(); - endRange.collapse(false); - if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { - pos = len; - } else { - pos = -textInputRange.moveStart("character", -len); - } - } - return pos; - }; - - InputCaret.prototype.getPos = function() { - if (oDocument.selection) { - return this.getIEPos(); - } else { - return this.domInputor.selectionStart; - } - }; - - InputCaret.prototype.setPos = function(pos) { - var inputor, range; - inputor = this.domInputor; - if (oDocument.selection) { - range = inputor.createTextRange(); - range.move("character", pos); - range.select(); - } else if (inputor.setSelectionRange) { - inputor.setSelectionRange(pos, pos); - } - return inputor; - }; - - InputCaret.prototype.getIEOffset = function(pos) { - var h, textRange, x, y; - textRange = this.domInputor.createTextRange(); - pos || (pos = this.getPos()); - textRange.move('character', pos); - x = textRange.boundingLeft; - y = textRange.boundingTop; - h = textRange.boundingHeight; - return { - left: x, - top: y, - height: h - }; - }; - - InputCaret.prototype.getOffset = function(pos) { - var $inputor, offset, position; - $inputor = this.$inputor; - if (oDocument.selection) { - offset = this.getIEOffset(pos); - offset.top += $(oWindow).scrollTop() + $inputor.scrollTop(); - offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft(); - return offset; - } else { - offset = $inputor.offset(); - position = this.getPosition(pos); - return offset = { - left: offset.left + position.left - $inputor.scrollLeft(), - top: offset.top + position.top - $inputor.scrollTop(), - height: position.height - }; - } - }; - - InputCaret.prototype.getPosition = function(pos) { - var $inputor, at_rect, end_range, format, html, mirror, start_range; - $inputor = this.$inputor; - format = function(value) { - return $('
').text(value).html().replace(/\r\n|\r|\n/g, "
").replace(/\s/g, " "); - }; - if (pos === void 0) { - pos = this.getPos(); - } - start_range = $inputor.val().slice(0, pos); - end_range = $inputor.val().slice(pos); - html = "" + format(start_range) + ""; - html += "|"; - html += "" + format(end_range) + ""; - mirror = new Mirror($inputor); - return at_rect = mirror.create(html).rect(); - }; - - InputCaret.prototype.getIEPosition = function(pos) { - var h, inputorOffset, offset, x, y; - offset = this.getIEOffset(pos); - inputorOffset = this.$inputor.offset(); - x = offset.left - inputorOffset.left; - y = offset.top - inputorOffset.top; - h = offset.height; - return { - left: x, - top: y, - height: h - }; - }; - - return InputCaret; - - })(); - Mirror = (function() { - Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"]; - - function Mirror($inputor) { - this.$inputor = $inputor; - } - - Mirror.prototype.mirrorCss = function() { - var css, - _this = this; - css = { - position: 'absolute', - left: -9999, - top: 0, - zIndex: -20000 - }; - if (this.$inputor.prop('tagName') === 'TEXTAREA') { - this.css_attr.push('width'); - } - $.each(this.css_attr, function(i, p) { - return css[p] = _this.$inputor.css(p); - }); - return css; - }; - - Mirror.prototype.create = function(html) { - this.$mirror = $('
'); - this.$mirror.css(this.mirrorCss()); - this.$mirror.html(html); - this.$inputor.after(this.$mirror); - return this; - }; - - Mirror.prototype.rect = function() { - var $flag, pos, rect; - $flag = this.$mirror.find("#caret"); - pos = $flag.position(); - rect = { - left: pos.left, - top: pos.top, - height: $flag.height() - }; - this.$mirror.remove(); - return rect; - }; - - return Mirror; - - })(); - Utils = { - contentEditable: function($inputor) { - return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true'); - } - }; - methods = { - pos: function(pos) { - if (pos || pos === 0) { - return this.setPos(pos); - } else { - return this.getPos(); - } - }, - position: function(pos) { - if (oDocument.selection) { - return this.getIEPosition(pos); - } else { - return this.getPosition(pos); - } - }, - offset: function(pos) { - var offset; - offset = this.getOffset(pos); - return offset; - } - }; - oDocument = null; - oWindow = null; - oFrame = null; - setContextBy = function(settings) { - var iframe; - if (iframe = settings != null ? settings.iframe : void 0) { - oFrame = iframe; - oWindow = iframe.contentWindow; - return oDocument = iframe.contentDocument || oWindow.document; - } else { - oFrame = void 0; - oWindow = window; - return oDocument = document; - } - }; - discoveryIframeOf = function($dom) { - var error; - oDocument = $dom[0].ownerDocument; - oWindow = oDocument.defaultView || oDocument.parentWindow; - try { - return oFrame = oWindow.frameElement; - } catch (_error) { - error = _error; - } - }; - $.fn.caret = function(method, value, settings) { - var caret; - if (methods[method]) { - if ($.isPlainObject(value)) { - setContextBy(value); - value = void 0; - } else { - setContextBy(settings); - } - caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this); - return methods[method].apply(caret, [value]); - } else { - return $.error("Method " + method + " does not exist on jQuery.caret"); - } - }; - $.fn.caret.EditableCaret = EditableCaret; - $.fn.caret.InputCaret = InputCaret; - $.fn.caret.Utils = Utils; - return $.fn.caret.apis = methods; - }); - -}).call(this); diff --git a/app/assets/javascripts/app/lib/base/jquery.caretposition.js b/app/assets/javascripts/app/lib/base/jquery.caretposition.js deleted file mode 100644 index 7be8c68d1..000000000 --- a/app/assets/javascripts/app/lib/base/jquery.caretposition.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * jQuery plugin for getting position of cursor in textarea - - * @license under GNU license - * @author Bevis Zhao (i@bevis.me, http://bevis.me) - */ - -// workaround to work with jquery 1.9 -// http://api.jquery.com/jQuery.browser/ -(function(){ - if (!jQuery.browser) { - jQuery.browser={}; - jQuery.browser.msie = false; - jQuery.browser.version = 0; - if( navigator.userAgent.match(/MSIE ([0-9]+)\./) ){ - jQuery.browser.msie = true; - jQuery.browser.version = RegExp.$1; - } - else if ( !navigator.userAgent.match(/mozilla/i) && !navigator.userAgent.match(/webkit/i) ){ - jQuery.browser.mozilla = true; - } - } -})(); - -$(function() { - - var calculator = { - // key styles - primaryStyles: ['fontFamily', 'fontSize', 'fontWeight', 'fontVariant', 'fontStyle', - 'paddingLeft', 'paddingTop', 'paddingBottom', 'paddingRight', - 'marginLeft', 'marginTop', 'marginBottom', 'marginRight', - 'borderLeftColor', 'borderTopColor', 'borderBottomColor', 'borderRightColor', - 'borderLeftStyle', 'borderTopStyle', 'borderBottomStyle', 'borderRightStyle', - 'borderLeftWidth', 'borderTopWidth', 'borderBottomWidth', 'borderRightWidth', - 'line-height', 'outline'], - - specificStyle: { - 'word-wrap': 'break-word', - 'overflow-x': 'hidden', - 'overflow-y': 'auto' - }, - - simulator : $('
').css({ - position: 'absolute', - top: 0, - left: 0, - visibility: 'hidden' - }).appendTo(document.body), - - toHtml : function(text) { - return text.replace(//g,'>').replace(/\n/g, '
') - .split(' ').join(' '); - }, - // calculate position - getCaretPosition: function() { - var cal = calculator, self = this, element = self[0], elementOffset = self.offset(); - - // IE has easy way to get caret offset position - if ($.browser.msie && $.browser.version <= 9) { - // must get focus first - element.focus(); - var range = document.selection.createRange(); - $('#hskeywords').val(element.scrollTop); - return { - left: range.boundingLeft - elementOffset.left, - top: parseInt(range.boundingTop) - elementOffset.top + element.scrollTop - + document.documentElement.scrollTop + parseInt(self.getComputedStyle("fontSize")) - }; - } - - cal.simulator.empty(); - // clone primary styles to imitate textarea - $.each(cal.primaryStyles, function(index, styleName) { - self.cloneStyle(cal.simulator, styleName); - }); - - // caculate width and height - cal.simulator.css($.extend({ - 'width': self.width(), - 'height': self.height() - }, cal.specificStyle)); - - var value = (self.val() || self.text()), cursorPosition = self.getCursorPosition(); - var beforeText = value.substring(0, cursorPosition), - afterText = value.substring(cursorPosition); - - var before = $('').html(cal.toHtml(beforeText)), - focus = $(''), - after = $('').html(cal.toHtml(afterText)); - - cal.simulator.append(before).append(focus).append(after); - var focusOffset = focus.offset(), simulatorOffset = cal.simulator.offset(); - // alert(focusOffset.left + ',' + simulatorOffset.left + ',' + element.scrollLeft); - return { - top: focusOffset.top - simulatorOffset.top - element.scrollTop - // calculate and add the font height except Firefox - + ($.browser.mozilla ? 0 : parseInt(self.getComputedStyle("fontSize"))), - left: focus[0].offsetLeft - cal.simulator[0].offsetLeft - element.scrollLeft - }; - } - }; - - $.fn.extend({ - setCursorPosition : function(position){ - if(this.length == 0) return this; - return $(this).setSelection(position, position); - }, - setSelection: function(selectionStart, selectionEnd) { - if(this.length == 0) return this; - input = this[0]; - - if (input.createTextRange) { - var range = input.createTextRange(); - range.collapse(true); - range.moveEnd('character', selectionEnd); - range.moveStart('character', selectionStart); - range.select(); - } else if (input.setSelectionRange) { - input.focus(); - input.setSelectionRange(selectionStart, selectionEnd); - } else { - var el = this.get(0); - - var range = document.createRange(); - range.collapse(true); - range.setStart(el.childNodes[0], selectionStart); - range.setEnd(el.childNodes[0], selectionEnd); - - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } - - return this; - }, - getComputedStyle: function(styleName) { - if (this.length == 0) return; - var thiz = this[0]; - var result = this.css(styleName); - result = result || ($.browser.msie ? - thiz.currentStyle[styleName]: - document.defaultView.getComputedStyle(thiz, null)[styleName]); - return result; - }, - // easy clone method - cloneStyle: function(target, styleName) { - var styleVal = this.getComputedStyle(styleName); - if (!!styleVal) { - $(target).css(styleName, styleVal); - } - }, - cloneAllStyle: function(target, style) { - var thiz = this[0]; - for (var styleName in thiz.style) { - var val = thiz.style[styleName]; - typeof val == 'string' || typeof val == 'number' - ? this.cloneStyle(target, styleName) - : NaN; - } - }, - getCursorPosition : function() { - var element = input = this[0]; - var value = (input.value || input.innerText) - - if(!this.data("lastCursorPosition")){ - this.data("lastCursorPosition",0); - } - - var lastCursorPosition = this.data("lastCursorPosition"); - - if (document.selection) { - input.focus(); - var sel = document.selection.createRange(); - var selLen = document.selection.createRange().text.length; - sel.moveStart('character', -value.length); - lastCursorPosition = sel.text.length - selLen; - } else if (input.selectionStart || input.selectionStart == '0') { - return input.selectionStart; - } else if (typeof window.getSelection != "undefined" && window.getSelection().rangeCount>0) { - try{ - var selection = window.getSelection(); - var range = selection.getRangeAt(0); - var preCaretRange = range.cloneRange(); - preCaretRange.selectNodeContents(element); - preCaretRange.setEnd(range.endContainer, range.endOffset); - lastCursorPosition = preCaretRange.toString().length; - }catch(e){ - lastCursorPosition = this.data("lastCursorPosition"); - } - } else if (typeof document.selection != "undefined" && document.selection.type != "Control") { - var textRange = document.selection.createRange(); - var preCaretTextRange = document.body.createTextRange(); - preCaretTextRange.moveToElementText(element); - preCaretTextRange.setEndPoint("EndToEnd", textRange); - lastCursorPosition = preCaretTextRange.text.length; - } - - this.data("lastCursorPosition",lastCursorPosition); - return lastCursorPosition; - }, - getCaretPosition: calculator.getCaretPosition - }); -}); diff --git a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js index 41d1ce7fd..96d8440ea 100644 --- a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js +++ b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js @@ -1,44 +1,17 @@ (function ($) { /* -# mode: textonly/richtext / disable b/i/u/enter + strip on paste +# mode: textonly/richtext / disable b/i/u/enter + strip on paste # pasteOnlyText: true -# maxlength: 123 -# multiline: true / disable enter + strip on paste -# placeholder: 'some placeholder' -# +# maxlength: 123 +# multiline: true / disable enter + strip on paste +# placeholder: 'some placeholder' */ var pluginName = 'ce', defaults = { mode: 'richtext', multiline: true, - allowKey: { - 8: true, // backspace - 9: true, // tab - 16: true, // shift - 17: true, // ctrl - 18: true, // alt - 20: true, // cabslock - 37: true, // up - 38: true, // right - 39: true, // down - 40: true, // left - 91: true, // cmd left - 92: true, // cmd right - }, - extraAllowKey: { - 65: true, // a + ctrl - select all - 67: true, // c + ctrl - copy - 86: true, // v + ctrl - paste - 88: true, // x + ctrl - cut - 90: true, // z + ctrl - undo - }, - richTextFormatKey: { - 66: true, // b - 73: true, // i - 85: true, // u - }, }; function Plugin( element, options ) { @@ -47,232 +20,34 @@ this.options = $.extend( {}, defaults, options) ; + this._defaults = defaults; + this._name = pluginName; + // take placeholder from markup if ( !this.options.placeholder && this.$element.data('placeholder') ) { this.options.placeholder = this.$element.data('placeholder') } - this._defaults = defaults; - this._name = pluginName; - - this.preventInput = false - - this.init(); - } - - Plugin.prototype.init = function () { - var _this = this - - // set focus class - this.$element.on('focus', function (e) { - _this.$element.closest('.form-control').addClass('focus') - }).on('blur', function (e) { - _this.$element.closest('.form-control').removeClass('focus') - }) - - // process placeholder - if ( this.options.placeholder ) { - this.updatePlaceholder( 'add' ) - this.$element.on('focus', function (e) { - _this.updatePlaceholder( 'remove' ) - }).on('blur', function (e) { - _this.updatePlaceholder( 'add' ) - }) + // link input + if ( !this.options.multiline ) { + editorMode = Medium.inlineMode } - // maxlength check - //this.options.maxlength = 10 - if ( this.options.maxlength ) { - this.$element.on('keydown', function (e) { - console.log('maxlength', e.keyCode, _this.allowKey(e)) - // check control key - if ( _this.allowKey(e) ) { - _this.maxLengthOk() - } - // check type ahead key - else { - if ( !_this.maxLengthOk( true ) ) { - e.preventDefault() - } - } - }).on('keyup', function (e) { - // check control key - if ( _this.allowKey(e) ) { - _this.maxLengthOk() - } - // check type ahead key - else { - if ( !_this.maxLengthOk( true ) ) { - e.preventDefault() - } - } - }).on('focus', function (e) { - _this.maxLengthOk() - }).on('blur', function (e) { - _this.maxLengthOk() - }) + // link textarea + else if ( this.options.multiline && this.options.mode != 'richtext' ) { + editorMode = Medium.partialMode } - // handle enter - this.$element.on('keydown', function (e) { - console.log('keydown', e.keyCode) - if ( _this.preventInput ) { - console.log('preventInput', _this.preventInput) - return - } - - // strap the return key being pressed - if (e.keyCode === 13) { - // disbale multi line - if ( !_this.options.multiline ) { - e.preventDefault() - return - } - // limit check - if ( !_this.maxLengthOk( true ) ) { - e.preventDefault() - return - } - - //newLine = "

" - newLine = "\n
" - if (document.selection) { - var range = document.selection.createRange() - newLine = "
" // ie is not supporting \n :( - range.pasteHTML(newLine) - } - else { - - // workaround for chrome - insert
directly after
is ignored - - // insert
, if it hasn't change add it twice again - var oldValue = _this.$element.html().trim() - document.execCommand('insertHTML', false, newLine) - var newValue = _this.$element.html().trim() - console.log('ON', oldValue, '--', newValue) - //if ( oldValue == newValue || oldValue == newValue.substr( 0, newValue.length - newLine.length ) ) { - //if ( oldValue == newValue.substr( 0, newValue.length - newLine.length ) ) { - if ( oldValue == newValue ) { - var oldValue = _this.$element.html().trim() - document.execCommand('insertHTML', false, newLine) - console.log('Autoinsert 1th-br') - var newValue = _this.$element.html().trim() - if ( oldValue == newValue ) { - console.log('Autoinsert 2th-br') - document.execCommand('insertHTML', false, ' ' + newLine) // + newLine) - } - } - } - - // prevent the default behaviour of return key pressed - e.preventDefault() - return false - } - }) - - // just paste text - if ( this.options.mode === 'textonly' ) { - this.$element.on('paste', function (e) { - e.preventDefault() - var text - if (window.clipboardData) { // IE - text = window.clipboardData.getData('Text') - } - else { - text = (e.originalEvent || e).clipboardData.getData('text/plain') - } - var overlimit = false - if (text) { - - // replace new lines - if ( !_this.options.multiline ) { - text = text.replace(/\n/g, '') - text = text.replace(/\r/g, '') - text = text.replace(/\t/g, '') - } - - // limit length, limit paste string - if ( _this.options.maxlength ) { - var pasteLength = text.length - var currentLength = _this.$element.text().length - var overSize = ( currentLength + pasteLength ) - _this.options.maxlength - if ( overSize > 0 ) { - text = text.substr( 0, pasteLength - overSize ) - overlimit = true - } - } - - // insert new text - if (document.selection) { // IE - var range = document.selection.createRange() - range.pasteHTML(text) - } - else { - document.execCommand('inserttext', false, text) - } - _this.maxLengthOk( overlimit ) - } - }) - } - - // disable rich text b/u/i - if ( this.options.mode === 'textonly' ) { - this.$element.on('keydown', function (e) { - if ( _this.richTextKey(e) ) { - e.preventDefault() - } - }) - } - }; - - // add/remove placeholder - Plugin.prototype.updatePlaceholder = function(type) { - if ( !this.options.placeholder ) { - return - } - var holder = this.$element - var text = holder.text().trim() - var placeholder = '' + this.options.placeholder + '' - - // add placholder if no text exists - if ( type === 'add') { - if ( !text ) { - holder.html( placeholder ) - } - } - - // empty placeholder text + // rich text else { - if ( text === this.options.placeholder ) { - setTimeout(function(){ - document.execCommand('selectAll', false, ''); - document.execCommand('delete', false, ''); - document.execCommand('selectAll', false, ''); - document.execCommand('removeFormat', false, ''); - }, 100); - } + editorMode = Medium.richMode } - } - - // disable/enable input - Plugin.prototype.input = function(type) { - if ( type === 'off' ) { - this.preventInput = true - } - else { - this.preventInput = false - } - } - - // max length check - Plugin.prototype.maxLengthOk = function(typeAhead) { - var length = this.$element.text().length - if (typeAhead) { - length = length + 1 - } - if ( length > this.options.maxlength ) { + // max length validation + var validation = function(element) { + console.log('pp', element, $(element)) // try to set error on framework form - var parent = this.$element.parent().parent() + var parent = $(element).parent().parent() if ( parent.hasClass('controls') ) { parent.addClass('has-error') setTimeout($.proxy(function(){ @@ -284,42 +59,24 @@ // set validation on element else { - this.$element.addClass('invalid') + $(element).addClass('invalid') setTimeout($.proxy(function(){ - this.$element.removeClass('invalid') + $(element).removeClass('invalid') }, this), 1000) return false } } - return true - } - - // check if key is allowed, even if length limit is reached - Plugin.prototype.allowKey = function(e) { - if ( this.options.allowKey[ e.keyCode ] ) { - return true - } - if ( ( e.ctrlKey || e.metaKey ) && this.options.extraAllowKey[ e.keyCode ] ) { - return true - } - return false - } - - // check if rich text key is pressed - Plugin.prototype.richTextKey = function(e) { - // e.altKey - // e.ctrlKey - // e.metaKey - // on mac block e.metaKey + i/b/u - if ( !e.altKey && e.metaKey && this.options.richTextFormatKey[ e.keyCode ] ) { - return true - } - // on win block e.ctrlKey + i/b/u - if ( !e.altKey && e.ctrlKey && this.options.richTextFormatKey[ e.keyCode ] ) { - return true - } - return false + new Medium({ + element: element, + modifier: 'auto', + placeholder: this.options.placeholder || '', + autofocus: false, + autoHR: false, + mode: editorMode, + maxLength: this.options.maxlength || -1, + maxLengthReached: validation, + }); } // get value @@ -332,9 +89,17 @@ // strip html signes if multi line exists if ( this.options.multiline ) { var text = this.$element.html() + //console.log('h2a 1', text) + text = text.replace(/


<\/p>/g, "\n") // new line as /p + text = text.replace(/

<\/p>/g, "\n") // new line as /p + text = text.replace(/<\/p>/g, "\n") // new line as /p text = text.replace(/
/g, "\n") // new line as br - text = text.replace(/

/g, "\n") // in some caes, new line als div - text = $("
" + text + "
").text().trim() + text = text.replace(/<\/div>/g, "\n") // in some caes, new line als div + text = text.replace(/<.+?>/g, "") // new line as /p + text = $("
" + text + "
").text() + text = text.replace(/ /g, " ") + text = text.replace(/\s+$/g, ''); + //console.log('h2a 2', text) return text } return this.$element.text().trim() @@ -357,19 +122,4 @@ return plugin.value() } -}(jQuery)); - -/* TODO: paste - @$('.ticket-title-update').bind( - 'drop' - (e) => - e.preventDefault() - - t2 = e.originalEvent.dataTransfer.getData("text/plain")# : window.event.dataTransfer.getData("Text"); - - @log('drop', t2, e.keyCode, e.clipboardData, e, $(e.target).text().length) - - document.execCommand('inserttext', false, '123123'); - #document.execCommand('inserttext', false, prompt('Paste something.')); - ) -*/ +}(jQuery)); \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/base/jquery.textmodule.js b/app/assets/javascripts/app/lib/base/jquery.textmodule.js index 465f50de8..b73a6f89a 100644 --- a/app/assets/javascripts/app/lib/base/jquery.textmodule.js +++ b/app/assets/javascripts/app/lib/base/jquery.textmodule.js @@ -174,25 +174,29 @@ // update widget position Plugin.prototype.updatePosition = function() { - console.log('uP') this.$widget.find('.dropdown-menu').scrollTop( 300 ); - //if ( !this.$element.is(':visible') ) return - var position = this.$element.caret('position'); - console.log('PP', position) + if ( !this.$element.is(':visible') ) return + + // get cursor position + var marker = '' + Medium.Injector.prototype.inject( marker ) + position = $('#js-cursor-position').position() + $('#js-cursor-position').remove() if (!position) return - var widgetHeight = this.$widget.find('ul').height() + 85 - this.$widget.css('top', position.top - widgetHeight) - if ( !this.isActive() ) { - this.$widget.css('left', position.left) - } + + // set position of widget + var height = this.$element.height() + var widgetHeight = this.$widget.find('ul').height() //+ 60 // + height + var top = -( widgetHeight + height ) + position.top + this.$widget.css('top', top) + this.$widget.css('left', position.left) } // open widget Plugin.prototype.open = function() { this.active = true - if (this.ce) { - this.ce.input('off') - } + this.updatePosition() + b = $.proxy(function() { this.$widget.addClass('open') }, this) @@ -203,9 +207,6 @@ Plugin.prototype.close = function() { this.active = false this.cutInput() - if (this.ce) { - this.ce.input('on') - } this.$widget.removeClass('open') } @@ -214,6 +215,36 @@ return this.active } + // paste some content + Plugin.prototype.paste = function(string) { + if (document.selection) { // IE + var range = document.selection.createRange() + range.pasteHTML(string) + } + else { + document.execCommand('insertHTML', false, string) + } + } + + // cut some content + Plugin.prototype.cut = function(string) { + var sel = window.getSelection() + if ( !sel || sel.rangeCount < 1) { + return + } + var range = sel.getRangeAt(0) + var clone = range.cloneRange() + + // improve error handling + start = range.startOffset - string.length + if (start < 0) { + start = 0 + } + clone.setStart(range.startContainer, start) + clone.setEnd(range.startContainer, range.startOffset) + clone.deleteContents() + } + // select text module and insert into text Plugin.prototype.take = function(id) { if (!id) { @@ -223,15 +254,9 @@ for (var i = 0; i < this.collection.length; i++) { var item = this.collection[i] if ( item.id == id ) { - var content = item.content + "\n" + var content = item.content + "
\n" this.cutInput() - if (document.selection) { // IE - var range = document.selection.createRange() - range.pasteHTML(content) - } - else { - document.execCommand('insertHTML', false, content) - } + this.paste(content) this.close() return } @@ -248,17 +273,7 @@ this.buffer = '' return } - var range = sel.getRangeAt(0) - var clone = range.cloneRange() - - // improve error handling - start = range.startOffset - this.buffer.length - if (start < 0) { - start = 0 - } - clone.setStart(range.startContainer, start) - clone.setEnd(range.startContainer, range.startOffset) - clone.deleteContents() + this.cut(this.buffer) this.buffer = '' } @@ -311,4 +326,4 @@ }); } -}(jQuery, window)); +}(jQuery, window)); \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/base/medium.js b/app/assets/javascripts/app/lib/base/medium.js new file mode 100644 index 000000000..dd3db427b --- /dev/null +++ b/app/assets/javascripts/app/lib/base/medium.js @@ -0,0 +1,1951 @@ +/* + * Medium.js + * + * Copyright 2013-2014, Jacob Kelley - http://jakiestfu.com/ + * Released under the MIT Licence + * http://opensource.org/licenses/MIT + * + * Github: http://github.com/jakiestfu/Medium.js/ + * Version: master + */ + +(function (w, d) { + + 'use strict'; + + var Medium = (function () { + + var trim = function (string) { + return string.replace(/^[\s]+|\s+$/g, ''); + }, + arrayContains = function(array, variable) { + var i = array.length; + while (i--) { + if (array[i] === variable) { + return true; + } + } + return false; + }, + //two modes, wild (native) or domesticated (rangy + undo.js) + rangy = w['rangy'] || null, + undo = w['Undo'] || null, + wild = (!rangy || !undo), + domesticated = (!wild), + key = w.Key = { + 'backspace': 8, + 'tab': 9, + 'enter': 13, + 'shift': 16, + 'ctrl': 17, + 'alt': 18, + 'pause': 19, + 'capsLock': 20, + 'escape': 27, + 'pageUp': 33, + 'pageDown': 34, + 'end': 35, + 'home': 36, + 'leftArrow': 37, + 'upArrow': 38, + 'rightArrow': 39, + 'downArrow': 40, + 'insert': 45, + 'delete': 46, + '0': 48, + '1': 49, + '2': 50, + '3': 51, + '4': 52, + '5': 53, + '6': 54, + '7': 55, + '8': 56, + '9': 57, + 'a': 65, + 'b': 66, + 'c': 67, + 'd': 68, + 'e': 69, + 'f': 70, + 'g': 71, + 'h': 72, + 'i': 73, + 'j': 74, + 'k': 75, + 'l': 76, + 'm': 77, + 'n': 78, + 'o': 79, + 'p': 80, + 'q': 81, + 'r': 82, + 's': 83, + 't': 84, + 'u': 85, + 'v': 86, + 'w': 87, + 'x': 88, + 'y': 89, + 'z': 90, + 'leftWindow': 91, + 'rightWindowKey': 92, + 'select': 93, + 'numpad0': 96, + 'numpad1': 97, + 'numpad2': 98, + 'numpad3': 99, + 'numpad4': 100, + 'numpad5': 101, + 'numpad6': 102, + 'numpad7': 103, + 'numpad8': 104, + 'numpad9': 105, + 'multiply': 106, + 'add': 107, + 'subtract': 109, + 'decimalPoint': 110, + 'divide': 111, + 'f1': 112, + 'f2': 113, + 'f3': 114, + 'f4': 115, + 'f5': 116, + 'f6': 117, + 'f7': 118, + 'f8': 119, + 'f9': 120, + 'f10': 121, + 'f11': 122, + 'f12': 123, + 'numLock': 144, + 'scrollLock': 145, + 'semiColon': 186, + 'equalSign': 187, + 'comma': 188, + 'dash': 189, + 'period': 190, + 'forwardSlash': 191, + 'graveAccent': 192, + 'openBracket': 219, + 'backSlash': 220, + 'closeBraket': 221, + 'singleQuote': 222 + }, + + /** + * Medium.js - Taking control of content editable + * @constructor + * @param {Object} [userSettings] user options + */ + Medium = function (userSettings) { + var medium = this, + action = new Medium.Action(), + cache = new Medium.Cache(), + cursor = new Medium.Cursor(), + html = new Medium.HtmlAssistant(), + utils = new Medium.Utilities(), + selection = new Medium.Selection(), + intercept = { + focus: function (e) { + e = e || w.event; + Medium.activeElement = el; + html.placeholders(); + }, + blur: function (e) { + e = e || w.event; + if (Medium.activeElement === el) { + Medium.activeElement = null; + } + + html.placeholders(); + }, + down: function (e) { + e = e || w.event; + + var keepEvent = true; + + //in Chrome it sends out this event before every regular event, not sure why + if (e.keyCode === 229) return; + + utils.isCommand(e, function () { + cache.cmd = true; + }, function () { + cache.cmd = false; + }); + + utils.isShift(e, function () { + cache.shift = true; + }, function () { + cache.shift = false; + }); + + utils.isModifier(e, function (cmd) { + if (cache.cmd) { + + if (( (settings.mode === Medium.inlineMode) || (settings.mode === Medium.partialMode) ) && cmd !== "paste") { + utils.preventDefaultEvent(e); + return; + } + + var cmdType = typeof cmd; + var fn = null; + if (cmdType === "function") { + fn = cmd; + } else { + fn = intercept.command[cmd]; + } + + keepEvent = fn.call(medium, e); + + if (keepEvent === false) { + utils.preventDefaultEvent(e); + utils.stopPropagation(e); + } + } + }); + + if (settings.maxLength !== -1) { + var len = html.text().length, + hasSelection = false, + selection = w.getSelection(); + + if (selection) { + hasSelection = !selection.isCollapsed; + } + + if (len >= settings.maxLength && !utils.isSpecial(e) && !utils.isNavigational(e) && !hasSelection) { + settings.maxLengthReached(settings.element) + return utils.preventDefaultEvent(e); + } + } + + switch (e.keyCode) { + case key['enter']: + intercept.enterKey(e); + break; + case key['backspace']: + case key['delete']: + intercept.backspaceOrDeleteKey(e); + break; + } + + return keepEvent; + }, + up: function (e) { + e = e || w.event; + utils.isCommand(e, function () { + cache.cmd = false; + }, function () { + cache.cmd = true; + }); + html.clean(); + html.placeholders(); + + //here we have a key context, so if you need to create your own object within a specific context it is doable + var keyContext; + if ( + settings.keyContext !== null + && ( keyContext = settings.keyContext[e.keyCode] ) + ) { + var el = cursor.parent(); + + if (el) { + keyContext.call(medium, e, el); + } + } + + action.preserveElementFocus(); + }, + command: { + bold: function (e) { + utils.preventDefaultEvent(e); + (new Medium.Element(medium, 'bold')) + .setClean(false) + .invoke(settings.beforeInvokeElement); + }, + underline: function (e) { + utils.preventDefaultEvent(e); + (new Medium.Element(medium, 'underline')) + .setClean(false) + .invoke(settings.beforeInvokeElement); + }, + italicize: function (e) { + utils.preventDefaultEvent(e); + (new Medium.Element(medium, 'italic')) + .setClean(false) + .invoke(settings.beforeInvokeElement); + }, + quote: function (e) { + }, + paste: function (e) { + medium.makeUndoable(); + if (settings.pasteAsText) { + var sel = utils.selection.saveSelection(); + utils.pasteHook(function (text) { + utils.selection.restoreSelection(sel); + + text = text.replace(/\n/g, '
'); + + (new Medium.Html(medium, text)) + .setClean(false) + .insert(settings.beforeInsertHtml, true); + + html.clean(); + html.placeholders(); + }); + } else { + html.clean(); + html.placeholders(); + } + } + }, + enterKey: function (e) { + if( settings.mode === Medium.inlineMode || settings.mode === Medium.inlineRichMode ){ + return utils.preventDefaultEvent(e); + } + + if (cache.shift) { + if (settings.tags['break']) { + utils.preventDefaultEvent(e); + html.addTag(settings.tags['break'], true); + return false; + } + + } else { + + var focusedElement = html.atCaret() || {}, + children = el.children, + lastChild = focusedElement === el.lastChild ? el.lastChild : null, + makeHR, + secondToLast, + paragraph; + + if ( + lastChild + && lastChild !== el.firstChild + && settings.autoHR + && settings.mode !== 'partial' + && settings.tags.horizontalRule + ) { + + utils.preventDefaultEvent(e); + + makeHR = + html.text(lastChild) === "" + && lastChild.nodeName.toLowerCase() === settings.tags.paragraph; + + if (makeHR && children.length >= 2) { + secondToLast = children[ children.length - 2 ]; + + if (secondToLast.nodeName.toLowerCase() === settings.tags.horizontalRule) { + makeHR = false; + } + } + + if (makeHR) { + html.addTag(settings.tags.horizontalRule, false, true, focusedElement); + focusedElement = focusedElement.nextSibling; + } + + if ((paragraph = html.addTag(settings.tags.paragraph, true, null, focusedElement)) !== null) { + paragraph.innerHTML = ''; + cursor.set(0, paragraph); + } + } + } + + return true; + }, + backspaceOrDeleteKey: function (e) { + if (settings.onBackspaceOrDelete !== undefined) { + var result = settings.onBackspaceOrDelete.call(medium, e, el); + + if (result) { + return; + } + } + + if (el.lastChild === null) return; + + var lastChild = el.lastChild, + beforeLastChild = lastChild.previousSibling; + + if ( + lastChild + && settings.tags.horizontalRule + && lastChild.nodeName.toLocaleLowerCase() === settings.tags.horizontalRule + ) { + el.removeChild(lastChild); + } else if ( + lastChild + && beforeLastChild + && utils.html.text(lastChild).length < 1 + + && beforeLastChild.nodeName.toLowerCase() === settings.tags.horizontalRule + && lastChild.nodeName.toLowerCase() === settings.tags.paragraph + ) { + el.removeChild(lastChild); + el.removeChild(beforeLastChild); + } + } + }, + defaultSettings = { + element: null, + modifier: 'auto', + placeholder: "", + autofocus: false, + autoHR: true, + mode: Medium.richMode, + maxLength: -1, + modifiers: { + 'b': 'bold', + 'i': 'italicize', + 'u': 'underline', + 'v': 'paste' + }, + tags: { + 'break': 'br', + 'horizontalRule': 'hr', + 'paragraph': 'p', + 'outerLevel': ['pre', 'blockquote', 'figure'], + 'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong'] + }, + cssClasses: { + editor: 'Medium', + pasteHook: 'Medium-paste-hook', + placeholder: 'Medium-placeholder', + clear: 'Medium-clear' + }, + attributes: { + remove: ['style', 'class'] + }, + pasteAsText: true, + beforeInvokeElement: function () { + //this = Medium.Element + }, + beforeInsertHtml: function () { + //this = Medium.Html + }, + maxLengthReached: function (element) { + //element + }, + beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) { + }, + keyContext: null, + pasteEventHandler: function(e) { + e = e || w.event; + medium.makeUndoable(); + var length = medium.value().length, + totalLength; + + if (settings.pasteAsText) { + utils.preventDefaultEvent(e); + var + sel = utils.selection.saveSelection(), + text = prompt(Medium.Messages.pastHere) || ''; + + if (text.length > 0) { + el.focus(); + Medium.activeElement = el; + utils.selection.restoreSelection(sel); + + //encode the text first + text = html.encodeHtml(text); + + //cut down it's length + totalLength = text.length + length; + if (settings.maxLength > 0 && totalLength > settings.maxLength) { + text = text.substring(0, settings.maxLength - length); + } + + if (settings.mode !== Medium.inlineMode) { + text = text.replace(/\n/g, '
'); + } + + (new Medium.Html(medium, text)) + .setClean(false) + .insert(settings.beforeInsertHtml, true); + + html.clean(); + html.placeholders(); + + return false; + } + } else { + setTimeout(function() { + html.clean(); + html.placeholders(); + }, 20); + } + } + }, + settings = utils.deepExtend(defaultSettings, userSettings), + el, + newVal, + i, + bridge = {}; + + for (i in defaultSettings) { + // Override defaults with data-attributes + if ( + typeof defaultSettings[i] !== 'object' + && defaultSettings.hasOwnProperty(i) + && settings.element.getAttribute('data-medium-' + key) + ) { + newVal = settings.element.getAttribute('data-medium-' + key); + + if (newVal.toLowerCase() === "false" || newVal.toLowerCase() === "true") { + newVal = newVal.toLowerCase() === "true"; + } + settings[i] = newVal; + } + } + + if (settings.modifiers) { + for (i in settings.modifiers) { + if (typeof(key[i]) !== 'undefined') { + settings.modifiers[key[i]] = settings.modifiers[i]; + } + } + } + + if (settings.keyContext) { + for (i in settings.keyContext) { + if (typeof(key[i]) !== 'undefined') { + settings.keyContext[key[i]] = settings.keyContext[i]; + } + } + } + + // Extend Settings + el = settings.element; + + // Editable + el.contentEditable = true; + el.className + += (' ' + settings.cssClasses.editor) + + (' ' + settings.cssClasses.editor + '-' + settings.mode); + + settings.tags = (settings.tags || {}); + if (settings.tags.outerLevel) { + settings.tags.outerLevel = settings.tags.outerLevel.concat([settings.tags.paragraph, settings.tags.horizontalRule]); + } + + this.settings = settings; + this.element = el; + this.intercept = intercept; + + this.action = action; + this.cache = cache; + this.cursor = cursor; + this.html = html; + this.utils = utils; + this.selection = selection; + + bridge.element = el; + bridge.medium = this; + bridge.settings = settings; + + bridge.action = action; + bridge.cache = cache; + bridge.cursor = cursor; + bridge.html = html; + bridge.intercept = intercept; + bridge.utils = utils; + bridge.selection = selection; + + action.setBridge(bridge); + cache.setBridge(bridge); + cursor.setBridge(bridge); + html.setBridge(bridge); + utils.setBridge(bridge); + selection.setBridge(bridge); + + // Initialize editor + html.clean(); + html.placeholders(); + action.preserveElementFocus(); + + // Capture Events + action.listen(); + + if (wild) { + this.makeUndoable = function () { + }; + } else { + this.dirty = false; + this.undoable = new Medium.Undoable(this); + this.undo = this.undoable.undo; + this.redo = this.undoable.redo; + this.makeUndoable = this.undoable.makeUndoable; + } + + el.medium = this; + + // Set as initialized + cache.initialized = true; + }; + + Medium.prototype = { + /** + * + * @param {String|Object} html + * @param {Function} [callback] + * @param {Boolean} [skipChangeEvent] + * @returns {Medium} + */ + insertHtml: function (html, callback, skipChangeEvent) { + var result = (new Medium.Html(this, html)) + .insert(this.settings.beforeInsertHtml); + + if (skipChangeEvent === true) { + this.utils.triggerEvent(this.element, "change"); + } + + if (callback) { + callback.apply(result); + } + + return this; + }, + + /** + * + * @param {String} tagName + * @param {Object} [attributes] + * @param {Boolean} [skipChangeEvent] + * @returns {Medium} + */ + invokeElement: function (tagName, attributes, skipChangeEvent) { + var settings = this.settings, + attributes = attributes || {}, + remove = attributes.remove || []; + + switch (settings.mode) { + case Medium.inlineMode: + case Medium.partialMode: + return this; + default: + } + + //invoke works off class, so if it isn't there, we just add it + if (remove.length > 0) { + if (!arrayContains(settings, 'class')) { + remove.push('class'); + } + } + + (new Medium.Element(this, tagName, attributes)) + .invoke(this.settings.beforeInvokeElement); + + if (skipChangeEvent === true) { + this.utils.triggerEvent(this.element, "change"); + } + + return this; + }, + + /** + * @returns {string} + */ + behavior: function () { + return (wild ? Medium.wildBehavior : Medium.domesticatedBehavior); + }, + + /** + * + * @param value + * @returns {Medium} + */ + value: function (value) { + if (typeof value !== 'undefined') { + this.element.innerHTML = value; + + this.html.clean(); + this.html.placeholders(); + } else { + return this.element.innerHTML; + } + + return this; + }, + + /** + * Focus on element + * @returns {Medium} + */ + focus: function () { + var el = this.element; + el.focus(); + return this; + }, + + /** + * Select all text + * @returns {Medium} + */ + select: function () { + var el = this.element, + range, + selection; + + el.focus(); + + if (d.body.createTextRange) { + range = d.body.createTextRange(); + range.moveToElementText(el); + range.select(); + } else if (w.getSelection) { + selection = w.getSelection(); + range = d.createRange(); + range.selectNodeContents(el); + selection.removeAllRanges(); + selection.addRange(range); + } + + return this; + }, + + isActive: function () { + return (Medium.activeElement === this.element); + }, + + destroy: function () { + var el = this.element, + intercept = this.intercept, + settings = this.settings, + placeholder = this.placeholder || null; + + if (placeholder !== null && placeholder.setup) { + //remove placeholder + placeholder.parentNode.removeChild(placeholder); + delete el.placeHolderActive; + } + + //remove contenteditable + el.removeAttribute('contenteditable'); + + //remove classes + el.className = trim(el.className + .replace(settings.cssClasses.editor, '') + .replace(settings.cssClasses.clear, '') + .replace(settings.cssClasses.editor + '-' + settings.mode, '')); + + //remove events + this.utils + .removeEvent(el, 'keyup', intercept.up) + .removeEvent(el, 'keydown', intercept.down) + .removeEvent(el, 'focus', intercept.focus) + .removeEvent(el, 'blur', intercept.focus) + .removeEvent(el, 'paste', settings.pasteEventHandler); + }, + + // Clears the element and restores the placeholder + clear: function () { + this.element.innerHTML = ''; + this.html.placeholders(); + }, + + /** + * Splits content in medium element at cursor + * @returns {DocumentFragment|null} + */ + splitAtCaret: function() { + if (!this.isActive()) return null; + + var selector = (w.getSelection || d.selection), + sel = selector(), + offset = sel.focusOffset, + node = sel.focusNode, + el = this.element, + range = d.createRange(), + endRange = d.createRange(), + contents; + + range.setStart(node, offset); + endRange.selectNodeContents(el); + range.setEnd(endRange.endContainer, endRange.endOffset); + + contents = range.extractContents(); + + return contents; + }, + + /** + * Deletes selection + */ + deleteSelection: function() { + if (!this.isActive()) return; + + var sel = rangy.getSelection(), + range; + + if (sel.rangeCount > 0) { + range = sel.getRangeAt(0); + range.deleteContents(); + } + } + }; + + /** + * @param {Medium} medium + * @param {String} tagName + * @param {Object} [attributes] + * @constructor + */ + Medium.Element = function (medium, tagName, attributes) { + this.medium = medium; + this.element = medium.settings.element; + if (wild) { + this.tagName = tagName; + } else { + switch (tagName.toLowerCase()) { + case 'bold': + this.tagName = 'b'; + break; + case 'italic': + this.tagName = 'i'; + break; + case 'underline': + this.tagName = 'u'; + break; + default: + this.tagName = tagName; + } + } + this.attributes = attributes || {}; + this.clean = true; + }; + + + /** + * @constructor + * @param {Medium} medium + * @param {String|HtmlElement} html + */ + Medium.Html = function (medium, html) { + this.medium = medium; + this.element = medium.settings.element; + this.html = html; + this.clean = true; + }; + + /** + * + * @constructor + */ + Medium.Injector = function () { + }; + + if (wild) { + Medium.Element.prototype = { + /** + * @methodOf Medium.Element + * @param {Function} [fn] + */ + invoke: function (fn) { + if (Medium.activeElement === this.element) { + if (fn) { + fn.apply(this); + } + d.execCommand(this.tagName, false); + } + }, + setClean: function () { + return this; + } + }; + + Medium.Injector.prototype = { + /** + * @methodOf Medium.Injector + * @param {String|HtmlElement} htmlRaw + * @param {Boolean} [selectInserted] + * @returns {null} + */ + inject: function (htmlRaw, selectInserted) { + this.insertHTML(htmlRaw, selectInserted); + return null; + } + }; + + /** + * + * @constructor + */ + Medium.Undoable = function () { + }; + } + + //if medium is domesticated (ie, not wild) + else { + rangy.rangePrototype.insertNodeAtEnd = function (node) { + var range = this.cloneRange(); + range.collapse(false); + range.insertNode(node); + range.detach(); + this.setEndAfter(node); + }; + + Medium.Element.prototype = { + /** + * @methodOf Medium.Element + * @param {Function} [fn] + */ + invoke: function (fn) { + if (Medium.activeElement === this.element) { + if (fn) { + fn.apply(this); + } + + var + attr = this.attributes, + tagName = this.tagName.toLowerCase(), + applier, + cl; + + if (attr.className !== undefined) { + cl = (attr.className.split[' '] || [attr.className]).shift(); + delete attr.className; + } else { + cl = 'medium-' + tagName; + } + + applier = rangy.createClassApplier(cl, { + elementTagName: tagName, + elementAttributes: this.attributes + }); + + this.medium.makeUndoable(); + + applier.toggleSelection(w); + + if (this.clean) { + //cleanup + this.medium.html.clean(); + this.medium.html.placeholders(); + } + + + } + }, + + /** + * + * @param {Boolean} clean + * @returns {Medium.Element} + */ + setClean: function (clean) { + this.clean = clean; + return this; + } + }; + + Medium.Injector.prototype = { + /** + * @methodOf Medium.Injector + * @param {String|HtmlElement} htmlRaw + * @returns {HtmlElement} + */ + inject: function (htmlRaw) { + var html, isConverted = false; + if (typeof htmlRaw === 'string') { + var htmlConverter = d.createElement('div'); + htmlConverter.innerHTML = htmlRaw; + html = htmlConverter.childNodes; + isConverted = true; + } else { + html = htmlRaw; + } + + this.insertHTML(''); + + var wedge = d.getElementById('wedge'), + parent = wedge.parentNode, + i = 0; + wedge.removeAttribute('id'); + + if (isConverted) { + while (i < html.length) { + parent.insertBefore(html[i], wedge); + } + } else { + parent.insertBefore(html, wedge); + } + parent.removeChild(wedge); + wedge = null; + + return html; + } + }; + + /** + * @param {Medium} medium + * @constructor + */ + Medium.Undoable = function (medium) { + var me = this, + element = medium.settings.element, + utils = medium.utils, + addEvent = utils.addEvent, + startValue = element.innerHTML, + timer, + stack = new Undo.Stack(), + EditCommand = Undo.Command.extend({ + constructor: function (oldValue, newValue) { + this.oldValue = oldValue; + this.newValue = newValue; + }, + execute: function () { + }, + undo: function () { + element.innerHTML = this.oldValue; + medium.canUndo = stack.canUndo(); + medium.canRedo = stack.canRedo(); + medium.dirty = stack.dirty(); + }, + redo: function () { + element.innerHTML = this.newValue; + medium.canUndo = stack.canUndo(); + medium.canRedo = stack.canRedo(); + medium.dirty = stack.dirty(); + } + }), + makeUndoable = function () { + var newValue = element.innerHTML; + // ignore meta key presses + if (newValue != startValue) { + + if (!me.movingThroughStack) { + // this could try and make a diff instead of storing snapshots + stack.execute(new EditCommand(startValue, newValue)); + startValue = newValue; + medium.dirty = stack.dirty(); + } + + utils.triggerEvent(medium.settings.element, "change"); + } + }; + + this.medium = medium; + this.timer = timer; + this.stack = stack; + this.makeUndoable = makeUndoable; + this.EditCommand = EditCommand; + this.movingThroughStack = false; + + addEvent(element, 'keyup', function (e) { + if (e.ctrlKey || e.keyCode === key.z) { + utils.preventDefaultEvent(e); + return; + } + + // a way too simple algorithm in place of single-character undo + clearTimeout(timer); + timer = setTimeout(function () { + makeUndoable(); + }, 250); + }); + + addEvent(element, 'keydown', function (e) { + if (!e.ctrlKey || e.keyCode !== key.z) { + me.movingThroughStack = false; + return true; + } + + utils.preventDefaultEvent(e); + + me.movingThroughStack = true; + + if (e.shiftKey) { + stack.canRedo() && stack.redo() + } else { + stack.canUndo() && stack.undo(); + } + }); + }; + } + + //Thank you Tim Down (super uber genius): http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div/6691294#6691294 + Medium.Injector.prototype.insertHTML = function (html, selectPastedContent) { + var sel, range; + if (w.getSelection) { + // IE9 and non-IE + sel = w.getSelection(); + if (sel.getRangeAt && sel.rangeCount) { + range = sel.getRangeAt(0); + range.deleteContents(); + + // Range.createContextualFragment() would be useful here but is + // only relatively recently standardized and is not supported in + // some browsers (IE9, for one) + var el = d.createElement("div"); + el.innerHTML = html; + var frag = d.createDocumentFragment(), node, lastNode; + while ((node = el.firstChild)) { + lastNode = frag.appendChild(node); + } + var firstNode = frag.firstChild; + range.insertNode(frag); + + // Preserve the selection + if (lastNode) { + range = range.cloneRange(); + range.setStartAfter(lastNode); + if (selectPastedContent) { + range.setStartBefore(firstNode); + } else { + range.collapse(true); + } + sel.removeAllRanges(); + sel.addRange(range); + } + } + } else if ((sel = d.selection) && sel.type != "Control") { + // IE < 9 + var originalRange = sel.createRange(); + originalRange.collapse(true); + sel.createRange().pasteHTML(html); + if (selectPastedContent) { + range = sel.createRange(); + range.setEndPoint("StartToStart", originalRange); + range.select(); + } + } + }; + + Medium.Html.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + /** + * @methodOf Medium.Html + * @param {Function} [fn] + * @param {Boolean} [selectInserted] + * @returns {HtmlElement} + */ + insert: function (fn, selectInserted) { + if (Medium.activeElement === this.element) { + if (fn) { + fn.apply(this); + } + + var inserted = this.injector.inject(this.html, selectInserted); + + if (this.clean) { + //cleanup + this.medium.html.clean(); + this.medium.html.placeholders(); + } + + this.medium.makeUndoable(); + + return inserted; + } else { + return null; + } + }, + + /** + * @attributeOf {Medium.Injector} Medium.Html + */ + injector: new Medium.Injector(), + + /** + * @methodOf Medium.Html + * @param clean + * @returns {Medium.Html} + */ + setClean: function (clean) { + this.clean = clean; + return this; + } + }; + + Medium.Utilities = function () { + }; + Medium.Utilities.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + /* + * Keyboard Interface events + */ + isCommand: function (e, fnTrue, fnFalse) { + var s = this.settings; + if ((s.modifier === 'ctrl' && e.ctrlKey ) || + (s.modifier === 'cmd' && e.metaKey ) || + (s.modifier === 'auto' && (e.ctrlKey || e.metaKey) ) + ) { + return fnTrue.call(); + } else { + return fnFalse.call(); + } + }, + isShift: function (e, fnTrue, fnFalse) { + if (e.shiftKey) { + return fnTrue.call(); + } else { + return fnFalse.call(); + } + }, + isModifier: function (e, fn) { + var cmd = this.settings.modifiers[e.keyCode]; + if (cmd) { + return fn.call(null, cmd); + } + return false; + }, + special: (function () { + var special = {}; + + special[key['backspace']] = true; + special[key['shift']] = true; + special[key['ctrl']] = true; + special[key['alt']] = true; + special[key['delete']] = true; + special[key['cmd']] = true; + + return special; + })(), + isSpecial: function (e) { + + if (this.cache.cmd) { + return true; + } + + return typeof this.special[e.keyCode] !== 'undefined'; + }, + navigational: (function () { + var navigational = {}; + + navigational[key['upArrow']] = true; + navigational[key['downArrow']] = true; + navigational[key['leftArrow']] = true; + navigational[key['rightArrow']] = true; + + return navigational; + })(), + isNavigational: function (e) { + return typeof this.navigational[e.keyCode] !== 'undefined'; + }, + + /* + * Handle Events + */ + addEvent: function addEvent(element, eventName, func) { + if (element.addEventListener) { + element.addEventListener(eventName, func, false); + } else if (element.attachEvent) { + element.attachEvent("on" + eventName, func); + } else { + element['on' + eventName] = func; + } + + return this; + }, + removeEvent: function removeEvent(element, eventName, func) { + if (element.removeEventListener) { + element.removeEventListener(eventName, func, false); + } else if (element.detachEvent) { + element.detachEvent("on" + eventName, func); + } else { + element['on' + eventName] = null; + } + + return this; + }, + preventDefaultEvent: function (e) { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + + return this; + }, + stopPropagation: function(e) { + e = e || window.event; + e.cancelBubble = true; + + if (e.stopPropagation !== undefined) { + e.stopPropagation(); + } + }, + isEventSupported: function (eventName) { + eventName = 'on' + eventName; + var el = d.createElement(this.element.tagName), + isSupported = (eventName in el); + + if (!isSupported) { + el.setAttribute(eventName, 'return;'); + isSupported = typeof el[eventName] == 'function'; + } + el = null; + return isSupported; + }, + triggerEvent: function (element, eventName) { + var e; + if (d.createEvent) { + e = d.createEvent("HTMLEvents"); + e.initEvent(eventName, true, true); + e.eventName = eventName; + element.dispatchEvent(e); + } else { + e = d.createEventObject(); + element.fireEvent("on" + eventName, e); + } + + return this; + }, + + deepExtend: function (destination, source) { + for (var property in source) { + if ( + source[property] + && source[property].constructor + && source[property].constructor === Object + ) { + destination[property] = destination[property] || {}; + this.deepExtend(destination[property], source[property]); + } else { + destination[property] = source[property]; + } + } + return destination; + }, + /* + * This is a Paste Hook. When the user pastes + * content, this ultimately converts it into + * plain text before inserting the data. + */ + pasteHook: function (fn) { + var textarea = d.createElement('textarea'), + el = this.element, + existingValue, + existingLength, + overallLength, + s = this.settings, + medium = this.medium, + html = this.html; + + textarea.className = s.cssClasses.pasteHook; + + el.parentNode.appendChild(textarea); + + textarea.focus(); + + if (!wild) { + medium.makeUndoable(); + } + setTimeout(function () { + el.focus(); + if (s.maxLength > 0) { + existingValue = html.text(el); + existingLength = existingValue.length; + overallLength = existingLength + textarea.value.length; + if (overallLength > existingLength) { + textarea.value = textarea.value.substring(0, s.maxLength - existingLength); + } + } + fn(textarea.value); + html.deleteNode( textarea ); + }, 2); + }, + setupContents: function () { + var el = this.element, + children = el.children, + childNodes = el.childNodes, + initialParagraph; + + if ( + !this.settings.tags.paragraph + || children.length > 0 + || this.settings.mode === Medium.inlineMode + || this.settings.mode === Medium.inlineRichMode + ) { + return; + } + + //has content, but no children + if (childNodes.length > 0) { + initialParagraph = d.createElement(this.settings.tags.paragraph); + if (el.innerHTML.match('^[&]nbsp[;]')) { + el.innerHTML = el.innerHTML.substring(6, el.innerHTML.length - 1); + } + initialParagraph.innerHTML = el.innerHTML; + el.innerHTML = ''; + el.appendChild(initialParagraph); + this.cursor.set(initialParagraph.innerHTML.length, initialParagraph); + } else { + initialParagraph = d.createElement(this.settings.tags.paragraph); + initialParagraph.innerHTML = ' '; + el.appendChild(initialParagraph); + } + }, + traverseAll: function(element, options, depth) { + var children = element.childNodes, + length = children.length, + i = 0, + node, + depth = depth || 1; + + options = options || {}; + + if (length > 0) { + for(;i < length;i++) { + node = children[i]; + switch (node.nodeType) { + case 1: + this.traverseAll(node, options, depth + 1); + if (options.element !== undefined) options.element(node, i, depth, element); + break; + case 3: + if (options.fragment !== undefined) options.fragment(node, i, depth, element); + } + + //length may change + length = children.length; + //if length did change, and we are at the last item, this causes infinite recursion, so if we are at the last item, then stop to prevent this + if (node === element.lastChild) { + i = length; + } + } + } + + } + }; + + /* + * Handle Selection Logic + */ + Medium.Selection = function () { + }; + Medium.Selection.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + saveSelection: function () { + if (w.getSelection) { + var sel = w.getSelection(); + if (sel.rangeCount > 0) { + return sel.getRangeAt(0); + } + } else if (d.selection && d.selection.createRange) { // IE + return d.selection.createRange(); + } + return null; + }, + + restoreSelection: function (range) { + if (range) { + if (w.getSelection) { + var sel = w.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } else if (d.selection && range.select) { // IE + range.select(); + } + } + } + }; + + /* + * Handle Cursor Logic + */ + Medium.Cursor = function () { + }; + Medium.Cursor.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + set: function (pos, el) { + var range, + html = this.html; + + if (d.createRange) { + var selection = w.getSelection() + var lastChild = html.lastChild() + var length = html.text(lastChild).length - 1 + var toModify = el ? el : lastChild + var theLength = ((typeof pos !== 'undefined') && (pos !== null) ? pos : length) + range = d.createRange(); + console.log('theLength', toModify, theLength, range) + range.setStart(toModify, theLength); + range.collapse(true); + selection.removeAllRanges(); + selection.addRange(range); + } else { + range = d.body.createTextRange(); + range.moveToElementText(el); + range.collapse(false); + range.select(); + } + }, + parent: function () { + var target = null, range; + + if (w.getSelection) { + range = w.getSelection().getRangeAt(0); + target = range.commonAncestorContainer; + + target = (target.nodeType === 1 + ? target + : target.parentNode + ); + } + + else if (d.selection) { + target = d.selection.createRange().parentElement(); + } + + if (target.tagName == 'SPAN') { + target = target.parentNode; + } + + return target; + }, + caretToBeginning: function (el) { + this.set(0, el); + }, + caretToEnd: function (el) { + this.set(this.html.text(el).length, el); + } + }; + + /* + * HTML Abstractions + */ + Medium.HtmlAssistant = function () { + }; + Medium.HtmlAssistant.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + encodeHtml: function ( html ) { + return d.createElement( 'a' ).appendChild( + d.createTextNode( html ) ).parentNode.innerHTML; + }, + text: function (node, val) { + node = node || this.settings.element; + if (val) { + if ((node.textContent) && (typeof (node.textContent) != "undefined")) { + node.textContent = val; + } else { + node.innerText = val; + } + } + + else if (node.innerText) { + return trim(node.innerText); + } + + else if (node.textContent) { + return trim(node.textContent); + } + //document fragment + else if (node.data) { + return trim(node.data); + } + + //for good measure + return ''; + }, + changeTag: function (oldNode, newTag) { + var newNode = d.createElement(newTag), + node, + nextNode; + + node = oldNode.firstChild; + while (node) { + nextNode = node.nextSibling; + newNode.appendChild(node); + node = nextNode; + } + + oldNode.parentNode.insertBefore(newNode, oldNode); + oldNode.parentNode.removeChild(oldNode); + + return newNode; + }, + deleteNode: function (el) { + el.parentNode.removeChild(el); + }, + placeholders: function () { + //in IE8, just gracefully degrade to no placeholders + if (!w.getComputedStyle) return; + + var that = this, + s = this.settings, + placeholder = this.medium.placeholder || (this.medium.placeholder = d.createElement('div')), + el = s.element, + style = placeholder.style, + elStyle = w.getComputedStyle(el, null), + qStyle = function (prop) { + return elStyle.getPropertyValue(prop) + }, + utils = this.utils, + text = utils.html.text(el), + cursor = this.cursor, + childCount = el.children.length, + hasFocus = Medium.activeElement === el; + + el.placeholder = placeholder; + + // Empty Editor + if ( + !hasFocus + && text.length < 1 + && childCount < 2 + ) { + if (el.placeHolderActive) return; + + if (!el.innerHTML.match('<' + s.tags.paragraph)) { + el.innerHTML = ''; + } + + // We need to add placeholders + if (s.placeholder.length > 0) { + if (!placeholder.setup) { + placeholder.setup = true; + + //background & background color + style.background = qStyle('background'); + style.backgroundColor = qStyle('background-color'); + + //text size & text color + style.fontSize = qStyle('font-size'); + style.color = elStyle.color; + + //begin box-model + //margin + style.marginTop = qStyle('margin-top'); + style.marginBottom = qStyle('margin-bottom'); + style.marginLeft = qStyle('margin-left'); + style.marginRight = qStyle('margin-right'); + + //padding + style.paddingTop = qStyle('padding-top'); + style.paddingBottom = qStyle('padding-bottom'); + style.paddingLeft = qStyle('padding-left'); + style.paddingRight = qStyle('padding-right'); + + //border + style.borderTopWidth = qStyle('border-top-width'); + style.borderTopColor = qStyle('border-top-color'); + style.borderTopStyle = qStyle('border-top-style'); + style.borderBottomWidth = qStyle('border-bottom-width'); + style.borderBottomColor = qStyle('border-bottom-color'); + style.borderBottomStyle = qStyle('border-bottom-style'); + style.borderLeftWidth = qStyle('border-left-width'); + style.borderLeftColor = qStyle('border-left-color'); + style.borderLeftStyle = qStyle('border-left-style'); + style.borderRightWidth = qStyle('border-right-width'); + style.borderRightColor = qStyle('border-right-color'); + style.borderRightStyle = qStyle('border-right-style'); + //end box model + + //element setup + placeholder.className = s.cssClasses.placeholder + ' ' + s.cssClasses.placeholder + '-' + s.mode; + placeholder.innerHTML = '
' + s.placeholder + '
'; + el.parentNode.insertBefore(placeholder, el); + } + + el.className += ' ' + s.cssClasses.clear; + + style.display = ''; + // Add base P tag and do auto focus, give it a min height if el has one + style.minHeight = el.clientHeight + 'px'; + style.minWidth = el.clientWidth + 'px'; + + if ( s.mode !== Medium.inlineMode && s.mode !== Medium.inlineRichMode ) { + utils.setupContents(); + + if (childCount === 0 && el.firstChild) { + cursor.set(0, el.firstChild); + } + } + } + el.placeHolderActive = true; + } else if (el.placeHolderActive) { + el.placeHolderActive = false; + style.display = 'none'; + el.className = trim(el.className.replace(s.cssClasses.clear, '')); + utils.setupContents(); + } + }, + + /** + * Cleans element + * @param {HtmlElement} [el] default is settings.element + */ + clean: function (el) { + + /* + * Deletes invalid nodes + * Removes Attributes + */ + var s = this.settings, + placeholderClass = s.cssClasses.placeholder, + attributesToRemove = (s.attributes || {}).remove || [], + tags = s.tags || {}, + onlyOuter = tags.outerLevel || null, + onlyInner = tags.innerLevel || null, + outerSwitch = {}, + innerSwitch = {}, + paragraphTag = (tags.paragraph || '').toUpperCase(), + html = this.html, + attr, + text, + j; + + el = el || s.element; + + if (s.mode === Medium.inlineRichMode) { + onlyOuter = s.tags.innerLevel; + } + + if (onlyOuter !== null) { + for (j = 0; j < onlyOuter.length; j++) { + outerSwitch[onlyOuter[j].toUpperCase()] = true; + } + } + + if (onlyInner !== null) { + for (j = 0; j < onlyInner.length; j++) { + innerSwitch[onlyInner[j].toUpperCase()] = true; + } + } + + this.utils.traverseAll(el, { + element: function(child, i, depth, parent) { + var nodeName = child.nodeName, + shouldDelete = true; + + // Remove attributes + for (j = 0; j < attributesToRemove.length; j++) { + attr = attributesToRemove[j]; + if (child.hasAttribute(attr)) { + if (child.getAttribute(attr) !== placeholderClass) { + child.removeAttribute(attr); + } + } + } + + if ( onlyOuter === null && onlyInner === null ) { + return; + } + + if (depth === 1 && outerSwitch[nodeName] !== undefined) { + shouldDelete = false; + } else if (depth > 1 && innerSwitch[nodeName] !== undefined) { + shouldDelete = false; + } + + // Convert tags or delete + if (shouldDelete) { + if (w.getComputedStyle(child, null).getPropertyValue('display') === 'block') { + if (paragraphTag.length > 0 && paragraphTag !== nodeName) { + html.changeTag(child, paragraphTag); + } + + if (depth > 1) { + while (parent.childNodes.length > i) { + parent.parentNode.insertBefore(parent.lastChild, parent.nextSibling); + } + } + } else { + switch (nodeName) { + case 'BR': + if (child === child.parentNode.lastChild) { + if (child === child.parentNode.firstChild) { + break; + } + text = document.createTextNode(""); + text.innerHTML = ' '; + child.parentNode.insertBefore(text, child); + break; + } + default: + while (child.firstChild !== null) { + child.parentNode.insertBefore(child.firstChild, child); + } + html.deleteNode(child); + break; + } + } + } + } + }); + }, + lastChild: function () { + return this.element.lastChild; + }, + addTag: function (tag, shouldFocus, isEditable, afterElement) { + if (!this.settings.beforeAddTag(tag, shouldFocus, isEditable, afterElement)) { + var newEl = d.createElement(tag), + toFocus; + + if (typeof isEditable !== "undefined" && isEditable === false) { + newEl.contentEditable = false; + } + if (newEl.innerHTML.length == 0) { + newEl.innerHTML = ' '; + } + if (afterElement && afterElement.nextSibling) { + afterElement.parentNode.insertBefore(newEl, afterElement.nextSibling); + toFocus = afterElement.nextSibling; + + } else { + this.settings.element.appendChild(newEl); + toFocus = this.html.lastChild(); + } + + if (shouldFocus) { + this.cache.focusedElement = toFocus; + this.cursor.set(0, toFocus); + } + return newEl; + } + return null; + }, + baseAtCaret: function () { + if (!this.medium.isActive()) return null; + + var sel = w.getSelection ? w.getSelection() : document.selection; + + if (sel.rangeCount) { + var selRange = sel.getRangeAt(0), + container = selRange.endContainer; + + switch (container.nodeType) { + case 3: + if (container.data && container.data.length != selRange.endOffset) return false; + break; + } + + return container; + } + + return null; + }, + atCaret: function () { + var container = this.baseAtCaret() || {}, + el = this.element; + + if (container === false) return null; + + while (container && container.parentNode !== el) { + container = container.parentNode; + } + + if (container && container.nodeType == 1) { + return container; + } + + return null; + } + }; + + Medium.Action = function () { + }; + Medium.Action.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + }, + listen: function () { + var el = this.element, + intercept = this.intercept; + + this.utils + .addEvent(el, 'keyup', intercept.up) + .addEvent(el, 'keydown', intercept.down) + .addEvent(el, 'focus', intercept.focus) + .addEvent(el, 'blur', intercept.blur) + .addEvent(el, 'paste', this.settings.pasteEventHandler); + }, + preserveElementFocus: function () { + // Fetch node that has focus + var anchorNode = w.getSelection ? w.getSelection().anchorNode : d.activeElement; + if (anchorNode) { + var cache = this.medium.cache, + s = this.settings, + cur = anchorNode.parentNode, + children = s.element.children, + diff = cur !== cache.focusedElement, + elementIndex = 0, + i; + + // anchorNode is our target if element is empty + if (cur === s.element) { + cur = anchorNode; + } + + // Find our child index + for (i = 0; i < children.length; i++) { + if (cur === children[i]) { + elementIndex = i; + break; + } + } + + // Focused element is different + if (diff) { + cache.focusedElement = cur; + cache.focusedElementIndex = elementIndex; + } + } + } + }; + + Medium.Cache = function () { + this.initialized = false; + this.cmd = false; + this.focusedElement = null + }; + Medium.Cache.prototype = { + setBridge: function (bridge) { + for (var i in bridge) { + this[i] = bridge[i]; + } + } + }; + + //Modes; + Medium.inlineMode = 'inline'; + Medium.partialMode = 'partial'; + Medium.richMode = 'rich'; + Medium.inlineRichMode = 'inlineRich'; + Medium.Messages = { + pastHere: 'Paste Here' + }; + + //Behaviours + Medium.domesticatedBehavior = 'domesticated'; + Medium.wildBehavior = 'wild'; + + return Medium; + }()); + + if (typeof define === 'function' && define['amd']) { + define(function () { return Medium; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = Medium; + } else if (typeof this !== 'undefined') { + this.Medium = Medium; + } + +}).call(this, window, document); diff --git a/app/assets/javascripts/app/lib/core/jquery-ui-1.11.2.js b/app/assets/javascripts/app/lib/core/jquery-ui-1.11.2.js new file mode 100755 index 000000000..ce8731b07 --- /dev/null +++ b/app/assets/javascripts/app/lib/core/jquery-ui-1.11.2.js @@ -0,0 +1,16582 @@ +/*! jQuery UI - v1.11.2 - 2014-10-16 +* http://jqueryui.com +* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { +/*! + * jQuery UI Core 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/ui-core/ + */ + + +// $.ui might exist from components with no dependencies, e.g., $.ui.position +$.ui = $.ui || {}; + +$.extend( $.ui, { + version: "1.11.2", + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + } +}); + +// plugins +$.fn.extend({ + scrollParent: function( includeHidden ) { + var position = this.css( "position" ), + excludeStaticParent = position === "absolute", + overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + scrollParent = this.parents().filter( function() { + var parent = $( this ); + if ( excludeStaticParent && parent.css( "position" ) === "static" ) { + return false; + } + return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) ); + }).eq( 0 ); + + return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent; + }, + + uniqueId: (function() { + var uuid = 0; + + return function() { + return this.each(function() { + if ( !this.id ) { + this.id = "ui-id-" + ( ++uuid ); + } + }); + }; + })(), + + removeUniqueId: function() { + return this.each(function() { + if ( /^ui-id-\d+$/.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + }); + } +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var map, mapName, img, + nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap='#" + mapName + "']" )[ 0 ]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && + // the element and all of its ancestors must be visible + visible( element ); +} + +function visible( element ) { + return $.expr.filters.visible( element ) && + !$( element ).parents().addBack().filter(function() { + return $.css( this, "visibility" ) === "hidden"; + }).length; +} + +$.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo(function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + }) : + // support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.fn.extend({ + focus: (function( orig ) { + return function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + orig.apply( this, arguments ); + }; + })( $.fn.focus ), + + disableSelection: (function() { + var eventType = "onselectstart" in document.createElement( "div" ) ? + "selectstart" : + "mousedown"; + + return function() { + return this.bind( eventType + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }; + })(), + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
+ value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + } +}); + +// $.ui.plugin is deprecated. Use $.widget() extensions instead. +$.ui.plugin = { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args, allowDisconnected ) { + var i, + set = instance.plugins[ name ]; + + if ( !set ) { + return; + } + + if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } +}; + + +/*! + * jQuery UI Widget 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/jQuery.widget/ + */ + + +var widget_uuid = 0, + widget_slice = Array.prototype.slice; + +$.cleanData = (function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; (elem = elems[i]) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; +})( $.cleanData ); + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; +}; + +$.widget.extend = function( target ) { + var input = widget_slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = widget_slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.widget.extend.apply( null, [ options ].concat(args) ) : + options; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( options === "instance" ) { + returnValue = instance; + return false; + } + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widget_uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled", !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + } + + return this; + }, + + enable: function() { + return this._setOptions({ disabled: false }); + }, + disable: function() { + return this._setOptions({ disabled: true }); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +var widget = $.widget; + + +/*! + * jQuery UI Mouse 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/mouse/ + */ + + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +var mouse = $.widget("ui.mouse", { + version: "1.11.2", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown." + this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click." + this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("." + this.widgetName); + if ( this._mouseMoveDelegate ) { + this.document + .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup." + this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if ( mouseHandled ) { + return; + } + + this._mouseMoved = false; + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + + this.document + .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .bind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if ( this._mouseMoved ) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + + // Iframe mouseup check - mouseup occurred in another document + } else if ( !event.which ) { + return this._mouseUp( event ); + } + } + + if ( event.which || event.button ) { + this._mouseMoved = true; + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + this.document + .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + mouseHandled = false; + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + + +/*! + * jQuery UI Position 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + +(function() { + +$.ui = $.ui || {}; + +var cachedScrollbarWidth, supportsOffsetFractions, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[0]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "
" ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return (cachedScrollbarWidth = w1 - w2); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[0] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: withinElement.offset() || { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + + // support: jQuery 1.6.x + // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows + width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(), + height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[0].preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + // clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each(function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // if the browser doesn't support fractions, then round for consistent results + if ( !supportsOffsetFractions ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + }); + } + }); + + if ( options.using ) { + // adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // element is wider than within + if ( data.collisionWidth > outerWidth ) { + // element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; + position.left += overLeft - newOverRight; + // element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + // element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + // too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + // too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + // adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // element is taller than within + if ( data.collisionHeight > outerHeight ) { + // element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; + position.top += overTop - newOverBottom; + // element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + // element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + // too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + // too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + // adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; + if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; + if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +// fraction support test +(function() { + var testElement, testElementParent, testElementStyle, offsetLeft, i, + body = document.getElementsByTagName( "body" )[ 0 ], + div = document.createElement( "div" ); + + //Create a "fake body" for testing based on method used in jQuery.support + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || document.documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + div.style.cssText = "position: absolute; left: 10.7432222px;"; + + offsetLeft = $( div ).offset().left; + supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11; + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); +})(); + +})(); + +var position = $.ui.position; + + +/*! + * jQuery UI Accordion 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/accordion/ + */ + + +var accordion = $.widget( "ui.accordion", { + version: "1.11.2", + options: { + active: 0, + animate: {}, + collapsible: false, + event: "click", + header: "> li > :first-child,> :not(li):even", + heightStyle: "auto", + icons: { + activeHeader: "ui-icon-triangle-1-s", + header: "ui-icon-triangle-1-e" + }, + + // callbacks + activate: null, + beforeActivate: null + }, + + hideProps: { + borderTopWidth: "hide", + borderBottomWidth: "hide", + paddingTop: "hide", + paddingBottom: "hide", + height: "hide" + }, + + showProps: { + borderTopWidth: "show", + borderBottomWidth: "show", + paddingTop: "show", + paddingBottom: "show", + height: "show" + }, + + _create: function() { + var options = this.options; + this.prevShow = this.prevHide = $(); + this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) + // ARIA + .attr( "role", "tablist" ); + + // don't allow collapsible: false and active: false / null + if ( !options.collapsible && (options.active === false || options.active == null) ) { + options.active = 0; + } + + this._processPanels(); + // handle negative values + if ( options.active < 0 ) { + options.active += this.headers.length; + } + this._refresh(); + }, + + _getCreateEventData: function() { + return { + header: this.active, + panel: !this.active.length ? $() : this.active.next() + }; + }, + + _createIcons: function() { + var icons = this.options.icons; + if ( icons ) { + $( "" ) + .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-accordion-header-icon" ) + .removeClass( icons.header ) + .addClass( icons.activeHeader ); + this.headers.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers + .removeClass( "ui-accordion-icons" ) + .children( ".ui-accordion-header-icon" ) + .remove(); + }, + + _destroy: function() { + var contents; + + // clean up main element + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + // clean up headers + this.headers + .removeClass( "ui-accordion-header ui-accordion-header-active ui-state-default " + + "ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-selected" ) + .removeAttr( "aria-controls" ) + .removeAttr( "tabIndex" ) + .removeUniqueId(); + + this._destroyIcons(); + + // clean up content panels + contents = this.headers.next() + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom " + + "ui-accordion-content ui-accordion-content-active ui-state-disabled" ) + .css( "display", "" ) + .removeAttr( "role" ) + .removeAttr( "aria-hidden" ) + .removeAttr( "aria-labelledby" ) + .removeUniqueId(); + + if ( this.options.heightStyle !== "content" ) { + contents.css( "height", "" ); + } + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + if ( key === "event" ) { + if ( this.options.event ) { + this._off( this.headers, this.options.event ); + } + this._setupEvents( value ); + } + + this._super( key, value ); + + // setting collapsible: false while collapsed; open first panel + if ( key === "collapsible" && !value && this.options.active === false ) { + this._activate( 0 ); + } + + if ( key === "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.headers.add( this.headers.next() ) + .toggleClass( "ui-state-disabled", !!value ); + } + }, + + _keydown: function( event ) { + if ( event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._eventHandler( event ); + break; + case keyCode.HOME: + toFocus = this.headers[ 0 ]; + break; + case keyCode.END: + toFocus = this.headers[ length - 1 ]; + break; + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + event.preventDefault(); + } + }, + + _panelKeyDown: function( event ) { + if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { + $( event.currentTarget ).prev().focus(); + } + }, + + refresh: function() { + var options = this.options; + this._processPanels(); + + // was collapsed or no panel + if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { + options.active = false; + this.active = $(); + // active false only when collapsible is true + } else if ( options.active === false ) { + this._activate( 0 ); + // was active, but active panel is gone + } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + // all remaining panel are disabled + if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { + options.active = false; + this.active = $(); + // activate previous panel + } else { + this._activate( Math.max( 0, options.active - 1 ) ); + } + // was active, active panel still exists + } else { + // make sure active index is correct + options.active = this.headers.index( this.active ); + } + + this._destroyIcons(); + + this._refresh(); + }, + + _processPanels: function() { + var prevHeaders = this.headers, + prevPanels = this.panels; + + this.headers = this.element.find( this.options.header ) + .addClass( "ui-accordion-header ui-state-default ui-corner-all" ); + + this.panels = this.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) + .filter( ":not(.ui-accordion-content-active)" ) + .hide(); + + // Avoid memory leaks (#10056) + if ( prevPanels ) { + this._off( prevHeaders.not( this.headers ) ); + this._off( prevPanels.not( this.panels ) ); + } + }, + + _refresh: function() { + var maxHeight, + options = this.options, + heightStyle = options.heightStyle, + parent = this.element.parent(); + + this.active = this._findActive( options.active ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) + .removeClass( "ui-corner-all" ); + this.active.next() + .addClass( "ui-accordion-content-active" ) + .show(); + + this.headers + .attr( "role", "tab" ) + .each(function() { + var header = $( this ), + headerId = header.uniqueId().attr( "id" ), + panel = header.next(), + panelId = panel.uniqueId().attr( "id" ); + header.attr( "aria-controls", panelId ); + panel.attr( "aria-labelledby", headerId ); + }) + .next() + .attr( "role", "tabpanel" ); + + this.headers + .not( this.active ) + .attr({ + "aria-selected": "false", + "aria-expanded": "false", + tabIndex: -1 + }) + .next() + .attr({ + "aria-hidden": "true" + }) + .hide(); + + // make sure at least one header is in the tab order + if ( !this.active.length ) { + this.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active.attr({ + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + }) + .next() + .attr({ + "aria-hidden": "false" + }); + } + + this._createIcons(); + + this._setupEvents( options.event ); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + this.element.siblings( ":visible" ).each(function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + }); + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); + }) + .height( maxHeight ); + } + }, + + _activate: function( index ) { + var active = this._findActive( index )[ 0 ]; + + // trying to activate the already active panel + if ( active === this.active[ 0 ] ) { + return; + } + + // trying to collapse, simulate a click on the currently active header + active = active || this.active[ 0 ]; + + this._eventHandler({ + target: active, + currentTarget: active, + preventDefault: $.noop + }); + }, + + _findActive: function( selector ) { + return typeof selector === "number" ? this.headers.eq( selector ) : $(); + }, + + _setupEvents: function( event ) { + var events = { + keydown: "_keydown" + }; + if ( event ) { + $.each( event.split( " " ), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + }); + } + + this._off( this.headers.add( this.headers.next() ) ); + this._on( this.headers, events ); + this._on( this.headers.next(), { keydown: "_panelKeyDown" }); + this._hoverable( this.headers ); + this._focusable( this.headers ); + }, + + _eventHandler: function( event ) { + var options = this.options, + active = this.active, + clicked = $( event.currentTarget ), + clickedIsActive = clicked[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : clicked.next(), + toHide = active.next(), + eventData = { + oldHeader: active, + oldPanel: toHide, + newHeader: collapsing ? $() : clicked, + newPanel: toShow + }; + + event.preventDefault(); + + if ( + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.headers.index( clicked ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $() : clicked; + this._toggle( eventData ); + + // switch classes + // corner classes on the previously active header stay after the animation + active.removeClass( "ui-accordion-header-active ui-state-active" ); + if ( options.icons ) { + active.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.activeHeader ) + .addClass( options.icons.header ); + } + + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-corner-all" ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); + if ( options.icons ) { + clicked.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.activeHeader ); + } + + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + }, + + _toggle: function( data ) { + var toShow = data.newPanel, + toHide = this.prevShow.length ? this.prevShow : data.oldPanel; + + // handle activating a panel during the animation for another activation + this.prevShow.add( this.prevHide ).stop( true, true ); + this.prevShow = toShow; + this.prevHide = toHide; + + if ( this.options.animate ) { + this._animate( toShow, toHide, data ); + } else { + toHide.hide(); + toShow.show(); + this._toggleComplete( data ); + } + + toHide.attr({ + "aria-hidden": "true" + }); + toHide.prev().attr( "aria-selected", "false" ); + // if we're switching panels, remove the old header from the tab order + // if we're opening from collapsed state, remove the previous header from the tab order + // if we're collapsing, then keep the collapsing header in the tab order + if ( toShow.length && toHide.length ) { + toHide.prev().attr({ + "tabIndex": -1, + "aria-expanded": "false" + }); + } else if ( toShow.length ) { + this.headers.filter(function() { + return $( this ).attr( "tabIndex" ) === 0; + }) + .attr( "tabIndex", -1 ); + } + + toShow + .attr( "aria-hidden", "false" ) + .prev() + .attr({ + "aria-selected": "true", + tabIndex: 0, + "aria-expanded": "true" + }); + }, + + _animate: function( toShow, toHide, data ) { + var total, easing, duration, + that = this, + adjust = 0, + down = toShow.length && + ( !toHide.length || ( toShow.index() < toHide.index() ) ), + animate = this.options.animate || {}, + options = down && animate.down || animate, + complete = function() { + that._toggleComplete( data ); + }; + + if ( typeof options === "number" ) { + duration = options; + } + if ( typeof options === "string" ) { + easing = options; + } + // fall back from options to animation in case of partial down settings + easing = easing || options.easing || animate.easing; + duration = duration || options.duration || animate.duration; + + if ( !toHide.length ) { + return toShow.animate( this.showProps, duration, easing, complete ); + } + if ( !toShow.length ) { + return toHide.animate( this.hideProps, duration, easing, complete ); + } + + total = toShow.show().outerHeight(); + toHide.animate( this.hideProps, { + duration: duration, + easing: easing, + step: function( now, fx ) { + fx.now = Math.round( now ); + } + }); + toShow + .hide() + .animate( this.showProps, { + duration: duration, + easing: easing, + complete: complete, + step: function( now, fx ) { + fx.now = Math.round( now ); + if ( fx.prop !== "height" ) { + adjust += fx.now; + } else if ( that.options.heightStyle !== "content" ) { + fx.now = Math.round( total - toHide.outerHeight() - adjust ); + adjust = 0; + } + } + }); + }, + + _toggleComplete: function( data ) { + var toHide = data.oldPanel; + + toHide + .removeClass( "ui-accordion-content-active" ) + .prev() + .removeClass( "ui-corner-top" ) + .addClass( "ui-corner-all" ); + + // Work around for rendering bug in IE (#5421) + if ( toHide.length ) { + toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className; + } + this._trigger( "activate", null, data ); + } +}); + + +/*! + * jQuery UI Menu 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/menu/ + */ + + +var menu = $.widget( "ui.menu", { + version: "1.11.2", + defaultElement: "