(function($) { $.fn.redraw = function() { return this.map(function(){ this.offsetTop; return this; }); }; $.fn.prepareSlideX = function(callback) { return this.map(function(){ $(this).css({width: this.scrollWidth, overflow: 'hidden'}); return this; }).one('transitionend', function(){ $(this).css({width: '', overflow: ''}); callback && callback.call(this); }).redraw(); }; $.fn.prepareSlideY = function(callback) { return this.map(function(){ $(this).css({height: this.scrollHeight, overflow: 'hidden'}); return this; }).one('transitionend', function(){ $(this).css({height: '', overflow: ''}); callback && callback.call(this); }).redraw(); }; $.fn.animOff = function(this_el) { if (this_el) { return this.css('transition', 'none').redraw(); } return this.addClass('no-transition').redraw(); }; $.fn.animOn = function(this_el) { if (this_el) { return this.redraw().css('transition', ''); } return this.redraw().removeClass('no-transition'); }; $.fn.fadeShow = function(callback) { return this.fadeToggle(true, callback); }; $.fn.fadeHide = function(callback) { return this.fadeToggle(false, callback); }; $.fn.isFadeHidden = function() { return this.hasClass('ohide'); }; $.fn.isFixed = function() { return this.parents().map(function(){ return $(this).css('position'); }).get().indexOf('fixed') != -1; }; $.fn.focusField = function() { var field = this.get(0); if (field && field instanceof RadioNodeList) { if (field[0]) { field[0].focus(); } } else { field.focus(); } return this; }; $.fn.focusAndSelect = function(select_all) { var field = this.get(0), len = this.value().length; if (field) { this.focusField(); if (len > 0) { if (this.is('[contenteditable]')) { var range = document.createRange(), sel; range.selectNodeContents(field); if (!select_all) { range.collapse(); } sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (field.setSelectionRange) { if (select_all) { field.setSelectionRange(0, len); } else { field.setSelectionRange(len, len); } } } } return this; }; $.fn.focusAndSelectAll = function() { return this.focusAndSelect(true); }; $.fn.fadeToggle = function(state, callback) { if (state === true || state === false) { state = !state; } if (callback == 'remove') { callback = function(){ $(this).remove(); }; } if (callback) { this.one('transitionend', callback); } return this.toggleClass('ohide', state); }; $.fn.slideShow = function(callback) { return this.prepareSlideY(callback).removeClass('shide'); }; $.fn.slideHide = function(callback) { if (callback == 'remove') { callback = function(){ $(this).remove(); }; } return this.prepareSlideY(callback).addClass('shide'); }; $.fn.slideXShow = function(callback) { return this.prepareSlideX(callback).removeClass('sxhide'); }; $.fn.slideXHide = function(callback) { if (callback == 'remove') { callback = function(){ $(this).remove(); }; } return this.prepareSlideX(callback).addClass('sxhide'); }; $.fn.isSlideHidden = function() { return this.hasClass('shide'); }; $.fn.slideToggle = function(state, callback) { if (state === true || state === false) { state = !state; } return this.prepareSlideY(callback).toggleClass('shide', state); }; $.fn.fitText = function() { return this.map(function(){ var init_size = $(this).data('init-size'); if (!init_size) { init_size = parseInt($(this).css('font-size')); $(this).data('init-size', init_size); } var size = parseInt($(this).css('font-size')); size = parseInt(size); while (this.scrollWidth > this.offsetWidth) { size -= 0.5; if (size >= init_size * 0.75) { $(this).css('font-size', size + 'px'); } else { break; } } return this; }); }; $.fn.highlight = function(delay) { var $this = this; $this.addClass('highlight'); setTimeout(function() { $this.removeClass('highlight'); }, delay); return $this; }; $.fn.scrollIntoView = function(options) { options = options || {}; return this.first().each(function() { var position = options.position || 'auto', padding = options.padding || 0, duration = options.duration || 0; var $item = $(this), $cont = $item.scrollParent(), scrollTop = $cont.scrollTop(), positionTop = 0, paddingTop = 0, itemHeight = $item.outerHeight(), isBody = false; if ($cont.get(0) === document) { isBody = true; $cont = $(window); positionTop = $item.offset().top; paddingTop = $('header').height() + 1; } else { positionTop = $item.offset().top - $cont.offset().top + scrollTop; } if (options.slidedEl) { if (options.slidedEl === 'this') { options.slidedEl = this; } $(options.slidedEl, this).each(function() { itemHeight += (this.scrollHeight - this.clientHeight); }); } var itemTop = positionTop, itemBottom = itemTop + itemHeight, contHeight = $cont.height(), contTop = scrollTop + padding + paddingTop, contBottom = scrollTop + contHeight - padding, scrollTo = null; if (position == 'auto') { if (itemTop < contTop) { scrollTo = itemTop - padding - paddingTop; } else if (itemBottom > contBottom) { if (itemHeight > contHeight - padding - padding) { scrollTo = itemTop - padding - paddingTop; } else { scrollTo = itemBottom - contHeight + padding; } } } else if (position == 'top' || position == 'center') { if (position == 'center' && contHeight > itemHeight) { padding = (contHeight - paddingTop - itemHeight) / 2; } scrollTo = itemTop - padding - paddingTop; } else if (position == 'bottom') { if (itemHeight > contHeight - padding - padding) { scrollTo = itemTop - padding - paddingTop; } else { scrollTo = itemBottom - contHeight + padding; } } if (scrollTo) { if (duration) { if (isBody) { $cont = $('html'); } $cont.stop().animate({scrollTop: scrollTo}, duration); } else { $cont.scrollTop(scrollTo); } } }); }; $.fn.initSearch = function(options) { return this.map(function(){ var $field = $(this); var curValue = $field.value(); var curSelectedIndex = false; var curResult = []; var curRenderedIndex = 0; var dataWaiting = false; var keyUpTimeout = null; var blurTimeout = null; var isFocused = false; options = options || {}; if (!options.searchEnabled) { options.searchEnabled = function(){ return true; }; } if (!options.enterEnabled) { options.enterEnabled = function(){ return true; }; } if (!options.prepareQuery) { options.prepareQuery = function(str){ return str.toLowerCase(); }; } $field.data('searchOptions', options); function onKeyDown(e) { switch (e.which) { case Keys.ESC: $field.blur(); break; case Keys.RETURN: select(curSelectedIndex); break; case Keys.UP: var index; if (!curSelectedIndex) { if (options.$enter && options.enterEnabled()) { index = false; } else { break; } } else { index = curSelectedIndex - 1; } hover(index, true); break; case Keys.DOWN: var index; if (curSelectedIndex === false) { index = 0; } else { index = curSelectedIndex + 1; } if (index > curResult.length - 1) { break; } hover(index, true); break; default: return; } e.stopImmediatePropagation(); e.preventDefault(); } function onKeyUp(e) { clearTimeout(blurTimeout); var value = $field.value(); clearTimeout(keyUpTimeout); if (curValue !== value) { // if (e.type == 'keyup') { // keyUpTimeout = setTimeout(function() { // valueChange(); // }, 50); // } else { options.onInputBeforeChange && options.onInputBeforeChange(value); valueChange(); options.onInput && options.onInput(value); open(); // } } } function onClick(e) { open(); } function check(item, queryLower) { if (options.checkItem) { return options.checkItem(item, queryLower); } if (!queryLower.length) { return 0; } for (var j = 0; j < item._values.length; j++) { var valueLower = item._values[j]; if (valueLower == queryLower) { item._fullmatch = true; return valueLower.length; } } for (var j = 0; j < item._values.length; j++) { var valueLower = item._values[j]; var index = valueLower.indexOf(queryLower); var found = options.prefixOnly ? index === 0 : index !== -1; if (found) { return valueLower.length; } } return false; } function search(data, query) { var result = []; result.fullMatchIndex = null; if (!options.emptyQueryEnabled && !query.length) { return result; } var time = +(new Date); var queryLower = options.prepareQuery(query); for (var i = 0; i < data.length; i++) { var item = data[i]; var valueScore = check(item, queryLower); if (valueScore !== false) { item._score = valueScore; if (item._top) item._score -= 10000000; else if (item._bottom) item._score += 10000000; item._i = i; result.push(item); } } result.sort(function(item1, item2) { return (item1._score - item2._score) || (item1._i - item2._i); }); for (i = 0; i < result.length; i++) { var item = result[i]; if (item._fullmatch) { delete item._fullmatch; if (result.fullMatchIndex === null) { result.fullMatchIndex = i; } } delete item._score; delete item._i; } console.log('search: ' + (((new Date) - time) / 1000) + 's'); return result; } function render(result, query, from_index) { if (from_index && from_index >= result.length) { return; } var time = +(new Date); from_index = from_index || 0; var html = ''; var render_limit = options.renderLimit || 50; if (result.length > 0) { for (var i = from_index, j = 0; i < result.length && j < render_limit; i++, j++) { var item = result[i]; var tagName = options.itemTagName || 'div'; var className = 'search-item' + (options.itemClass ? ' ' + options.itemClass : '') + (item.className ? ' ' + item.className : ''); var item_html = '<' + tagName + ' class="' + className + '" data-i="' + i + '">' + options.renderItem(item, query) + ''; html += item_html; } curRenderedIndex = i; } else { html = options.renderNoItems ? options.renderNoItems(query) : ''; curRenderedIndex = 0; } if (curRenderedIndex >= result.length) { html += options.appendToItems ? options.appendToItems(query, result.length) : ''; } if (!result.length && html == '') { options.$results.fadeHide(function() { if (options.$results.isFadeHidden()) { options.$results.html(html); } }); } else { if (options.$results.isFadeHidden()) { options.$results.fadeShow(); } if (!from_index) { options.$results.html(html); } else if (html) { options.$results.append(html); } } updateScrollState(); console.log('render: from ' + from_index + ', ' + j + ' lines, ' + (((new Date) - time) / 1000) + 's'); } function renderLoading() { curRenderedIndex = 0; options.$results.html(options.renderLoading ? options.renderLoading() : ''); updateScrollState(); } function renderEmpty() { curRenderedIndex = 0; options.$results.html(''); updateScrollState(); } function close(no_anim) { console.log(+new Date, 'close', no_anim); clearTimeout(keyUpTimeout); if (!options.$results.hasClass('collapsed')) { if (options.$enter && options.enterEnabled()) { options.$enter.removeClass('selected'); } if (no_anim) { options.$results.animOff(); } options.$results.addClass('collapsed'); options.onClose && options.onClose(); if (no_anim) { options.$results.animOn(); } } } function open() { if ($field.data('disabled')) { return false; } clearTimeout(blurTimeout); hover(curSelectedIndex, true); if (options.$results.hasClass('collapsed')) { options.$results.removeClass('collapsed'); options.onOpen && options.onOpen(); } } function onFocus() { isFocused = true; var value = $field.value(); if (curValue != value || options.searchEnabled() && options.getData() === false) { valueChange(); } open(); } function onBlur() { if (!isFocused) return; console.log(+new Date, 'onblur'); isFocused = false; clearTimeout(blurTimeout); blurTimeout = setTimeout(close, 100, false); options.onBlur && options.onBlur(curValue); } function valueChange() { clearTimeout(blurTimeout); clearTimeout(keyUpTimeout); var value = $field.value(); curValue = value; console.log('valueChange', options.searchEnabled()); if (options.searchEnabled()) { var data = options.getData(); if (data === false) { if (!dataWaiting) { dataWaiting = true; $field.one('dataready.search', function() { dataWaiting = false; valueChange(); }); } if (curValue.length || options.emptyQueryEnabled) { renderLoading(); } else { renderEmpty(); } return; } curResult = search(data, curValue); var index = false; var $scrollableEl = options.resultsNotScrollable ? $(window) : options.$results; $scrollableEl.scrollTop(0); if (curValue.length || options.emptyQueryEnabled) { render(curResult, curValue); if (curResult.length && (!options.enterEnabled())) { index = 0; } if (options.selectFullMatch && curResult.fullMatchIndex !== null) { index = curResult.fullMatchIndex; } } else { renderEmpty(); } } else { curResult = []; var index = false; renderEmpty(); } hover(index, true); } function hover(i, adjust_scroll, middle) { $('.search-item.selected', options.$results).removeClass('selected'); curSelectedIndex = i; if (curSelectedIndex !== false) { var selectedEl = $('.search-item', options.$results).get(curSelectedIndex); if (!selectedEl) { curSelectedIndex = false; } else { $(selectedEl).addClass('selected'); if (adjust_scroll) { adjustScroll($(selectedEl), middle); } if (Math.abs(curSelectedIndex - curRenderedIndex) < 5) { render(curResult, curValue, curRenderedIndex); } } } if (options.$enter && options.enterEnabled()) { options.$enter.toggleClass('selected', curSelectedIndex === false); } } function select(i) { if (i === false) { if (options.enterEnabled()) { if (!options.noCloseOnEnter) { $field.blur(); } options.onEnter && options.onEnter(curValue); if (!options.noCloseOnEnter) { close(true); } } return; } if (!options.noCloseOnSelect) { $field.blur(); } options.onSelect && options.onSelect(curResult[i]); if (!options.noCloseOnSelect) { close(true); } } function onItemHover() { hover($(this).data('i'), true, true); } function onItemMouseOver() { hover($(this).data('i')); } function updateScrollState() { var results = options.$results.get(0); if (results) { options.$results.toggleClass('topscroll', results.scrollTop > 0); options.$results.toggleClass('bottomscroll', results.scrollTop < results.scrollHeight - results.clientHeight); } } function onResultsScroll(e) { updateScrollState(); if (options.resultsNotScrollable) { var bottom = options.$results.offset().top + options.$results.height() - $(window).scrollTop(); if (bottom < $(window).height() * 2) { render(curResult, curValue, curRenderedIndex); } } else { if (this.scrollTop > this.scrollHeight - this.clientHeight - 1000) { render(curResult, curValue, curRenderedIndex); } } } function onItemClick(e) { if (e.metaKey || e.ctrlKey) return true; clearTimeout(blurTimeout); e.stopImmediatePropagation(); e.preventDefault(); select($(this).data('i')); } function adjustScroll($itemEl, middle) { var scrollTop = options.$results.scrollTop(), itemTop = $itemEl.position().top + scrollTop, itemHeight = $itemEl.outerHeight(), itemBottom = itemTop + itemHeight, contHeight = options.$results.height() || 300; if (middle) { options.$results.scrollTop(itemTop - (contHeight - itemHeight) / 2); } else if (itemTop < scrollTop) { options.$results.scrollTop(itemTop); } else if (itemBottom > scrollTop + contHeight) { options.$results.scrollTop(itemBottom - contHeight); } } if (options.$enter && options.enterEnabled()) { options.$enter.on('mouseover.search', onItemMouseOver); options.$enter.on('mousedown.search', onItemClick); options.$enter.data('i', false); } options.$results.on('hover.search', '.search-item', onItemHover); options.$results.on('mouseover.search', '.search-item', onItemMouseOver); options.$results.on('mousedown.search', '.search-item', onItemClick); if (options.resultsNotScrollable) { $(window).on('scroll.search', onResultsScroll); } else { options.$results.on('scroll.search', onResultsScroll); if (options.$results.isFixed()) { options.$results.blockBodyScroll(); } } if (options.initTextarea) { $field.initTextarea(options.initTextarea); } $field.on('keydown.search', onKeyDown); $field.on('keyup.search', onKeyUp); $field.on('focus.search', onFocus); $field.on('blur.search', onBlur); $field.on('input.search', onKeyUp); $field.on('click.search', onClick); $field.on('disable.search', function(e, disable) { $field.data('disabled', disable); $field.attr('contenteditable', disable ? 'false' : 'true'); close(true); }); $field.on('datachange.search', function() { valueChange(); }); $field.on('contentchange.search', function() { if (options.resultsNotScrollable) { var scrolltop = $(window).scrollTop(); } else { var scrolltop = options.$results.scrollTop(); } var limit = options.renderLimit; options.renderLimit = curRenderedIndex; valueChange(); options.renderLimit = limit; if (options.resultsNotScrollable) { $(window).scrollTop(scrolltop); } else { options.$results.scrollTop(scrolltop); } }); options.$results.addClass('collapsed'); if (options.updateOnInit) { valueChange(); } return this; }); }; $.fn.destroySearch = function() { return this.map(function() { var $field = $(this); var options = $field.data('searchOptions'); if (options) { if (options.$enter && options.enterEnabled()) { options.$enter.off('.search'); } options.$results.off('.search'); if (options.resultsNotScrollable) { $(window).off('.search'); } if (options.initTextarea) { $field.destroyTextarea(); } } $field.off('.search'); return this; }); }; $.fn.initSelect = function(options) { return this.map(function() { var $select = $(this); var $field = $('.form-control', $select); var $selected = $('.selected-items', $select); var $results = $('.items-list', $select); var selectedVal = [], selectedMap = {}; $select.data('options', options); function getValue(full) { if (options.multiSelect) { return full ? $.extend({}, selectedMap) : [].concat(selectedVal); } else { return selectedVal.length > 0 ? (full ? selectedMap[selectedVal[0]] : selectedVal[0]) : (full ? false : ''); } } function setValue() { var selValue = getValue(), selValueFull = getValue(true); $select.data('value', selValue); $select.data('valueFull', selValueFull); options.onChange && options.onChange(selValue, selValueFull); } function toggleDD(open) { $select.toggleClass('open', open); } function addSelected(item, noupdate) { var val = (item.prefix || '') + item.val; if (!selectedMap[val]) { if (!options.multiSelect) { for (var i = 0; i < selectedVal.length; i++) { delete selectedMap[selectedVal[i]]; } selectedVal = []; } else if (item.group) { for (var i = selectedVal.length - 1; i >= 0; i--) { if (selectedMap[selectedVal[i]].group == item.group) { delete selectedMap[selectedVal[i]]; selectedVal.splice(i, 1); } } } selectedVal.push(val); selectedMap[val] = item; if (!noupdate) { setValue(); updateSelected(); } } } function delSelected(val) { if (selectedMap[val]) { delete selectedMap[val]; for (var i = 0; i < selectedVal.length; i++) { if (selectedVal[i] == val) { selectedVal.splice(i, 1); break; } } setValue(); updateSelected(); } } function clearSelected() { for (var i = 0; i < selectedVal.length; i++) { var val = selectedVal[i]; delete selectedMap[val]; } selectedVal = []; setValue(); updateSelected(); } function updateSelected() { var html = ''; for (var i = 0; i < selectedVal.length; i++) { var val = selectedVal[i]; var item = selectedMap[val]; html += options.renderSelectedItem ? options.renderSelectedItem(val, item) : '
' + item.name + '
'; } $('.selected-item', $selected).remove(); $selected.prepend(html); options.onUpdate && options.onUpdate(getValue(), getValue(true)); } var initTextarea = null; var isContentEditable = $field.is('[contenteditable]'); if (isContentEditable) { initTextarea = options.noSearch ? { singleLine: true, checkText: function() { return ''; } } : { singleLine: true }; } $field.initSearch($.extend({ $results: $results, emptyQueryEnabled: true, noCloseOnSelect: options.multiSelect, updateOnInit: true, renderItem: function(item) { return '
' + item.name + '
'; }, prepareQuery: function(str) { str = str.toLowerCase(); if (options.searchByLastWord) { str = str.split(/\s+/).pop(); } return str; }, onOpen: function() { toggleDD(true); }, onClose: function() { toggleDD(false); } }, options, { getData: function() { var data = options.getData(); if (data === false) { return false; } var filtered_data = []; for (var i = 0; i < data.length; i++) { if (data[i].hidden) continue; var val = (data[i].prefix || '') + data[i].val; if (!selectedMap[val] || !options.multiSelect) { filtered_data.push(data[i]); } } return filtered_data; }, onSelect: function(item) { var newValue = ''; if (options.searchByLastWord) { var oldValue = $field.value(); var lastWord = oldValue.split(/\s+/).pop(); newValue = oldValue; if (lastWord.length > 0) { newValue = oldValue.substr(0, oldValue.length - lastWord.length); } newValue = newValue.replace(/^\s+/, ''); } $field.value(newValue); addSelected(item); if (options.multiSelect) { $field.trigger('contentchange').focusAndSelect(); } }, initTextarea: initTextarea })); if (options.noSearch) { $select.addClass('no-search'); } if (!isContentEditable) { $field.on('keydown.select', function(e) { if (!e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey && (e.which == Keys.BACKSPACE) && this.selectionStart == this.selectionEnd && !this.selectionStart) { $(this).trigger('backspaceonleft'); } }); } var defValue = $select.defaultValue(); var defSelected = defValue.length ? defValue.split(';') : [], dataMap = {}; if (defSelected.length) { var data = options.getData(); if (data !== false) { for (var i = 0; i < data.length; i++) { var val = (data[i].prefix || '') + data[i].val; dataMap[val] = data[i]; } } for (var i = 0, item; i < defSelected.length; i++) { if (item = dataMap[defSelected[i]]) { addSelected(item, true); } } } $select.data('value', getValue()); $select.data('valueFull', getValue(true)); $select.on('selectval.select', function(e, val, clear) { addSelected(val); if (clear) { $field.value(''); } $field.trigger('datachange'); }); $select.on('deselectval.select', function(e, val) { delSelected(val); $field.trigger('datachange'); }); $select.on('disableselect.select', function(e, disable) { $select.toggleClass('select-disabled', disable); $field.trigger('disable', [disable]); }); $select.on('reset.select', function(e) { $('.selected-item', $selected).each(function() { var val = $(this).attr('data-val'); delSelected(val); }); $field.trigger('datachange'); }); $select.on('datachange', function(e) { if (e.target === this) { $field.trigger('datachange'); } }); $field.on('backspaceonleft.select', function(e) { if (options.focusSelectedBeforeDelete) { var $focused = $('.selected-item.focused', $selected); if ($focused.size() > 0) { var val = $focused.eq(-1).attr('data-val'); delSelected(val); $field.trigger('datachange'); } else { $('.selected-item', $selected).eq(-1).addClass('focused'); } } else { var $items = $('.selected-item', $selected); if ($items.size() > 0) { var val = $items.eq(-1).attr('data-val'); delSelected(val); $field.trigger('datachange'); } } }); $field.on('focus.select', function() { $('.selected-item.focused', $selected).removeClass('focused'); }); $selected.on('click.select', '.selected-item', function(e) { $('.selected-item.focused', $selected).removeClass('focused'); $(this).addClass('focused'); e.stopImmediatePropagation(); }); $selected.on('click.select', '.selected-item .close', function(e) { var val = $(this).parents('.selected-item').attr('data-val'); delSelected(val); if (options.multiSelect) { $field.trigger('datachange').focusAndSelectAll(); } e.stopImmediatePropagation(); }); $select.on('click.select', '.select-clear', function(e) { if ($field.value().length > 0) { $field.value('').trigger('input').focus(); options.onClear && options.onClear(); } else { clearSelected(); $field.focus(); } e.stopImmediatePropagation(); }); $select.on('click.select', function(e) { if ($(e.target).is('.select')) { $field.focus(); } }); if ($select.hasClass('select-disabled')) { $select.trigger('disableselect', [true]); } if (selectedVal.length) { updateSelected(); $field.trigger('datachange'); } $select.data('inited', true); return this; }); }; $.fn.destroySelect = function() { return this.map(function() { var $select = $(this); var $field = $('.form-control', $select); var $selected = $('.selected-items', $select); $field.destroySearch(); $field.off('.select'); $selected.off('.select'); return this; }); } $.fn.hasField = function(name) { return this.first().map(function() { if (this.tagName == 'FORM') { if (this[name]) { return true; } return $('.input[data-name]', this).filter(function() { return ($(this).attr('data-name') == name); }).size() > 0; } return false; }).get(0) || false; }; $.fn.field = function(name) { return this.first().map(function() { if (this.tagName == 'FORM') { if (this[name]) { return this[name]; } return $('.input[data-name],.select[data-name]', this).filter(function() { return ($(this).attr('data-name') == name); }).get(0); } }); }; $.fn.fieldEl = function(name) { var result = []; this.each(function() { $(this).each(function() { result.push(this); }); }); return $(result); }; $.fn.fields = function() { return this.first().map(function() { if (this.tagName == 'FORM') { var fields = {}; for (var i = 0; i < this.elements.length; i++) { var elem = this.elements[i]; fields[elem.name] = elem.value; } return fields; } }).get(0) || {}; }; $.fn.reset = function(val) { return this.each(function() { if (this.tagName == 'FORM') { this.reset(); $('.input[data-name]', this).each(function() { $(this).text($(this).attr('data-value')).trigger('input'); }); $('.select[data-name]', this).each(function() { $(this).trigger('reset'); }); } }); }; $.fn.scrollHeight = function() { return this.first().map(function() { return this.scrollHeight; }).get(0) || ''; }; $.fn.defaultValue = function(val) { if (typeof val !== 'undefined') { return this.each(function() { if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { this.defaultValue = val; } else { $(this).attr('data-value', val); } }); } return this.first().map(function() { if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { return this.defaultValue || ''; } else { return $(this).attr('data-value') || ''; } }).get(0) || ''; }; $.fn.value = function(val) { if (typeof val !== 'undefined') { return this.each(function() { if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT' || this instanceof RadioNodeList) { if (this instanceof RadioNodeList && val === '') { for (var i = 0; i < this.length; i++) { this[i].checked = false; } } else { this.value = val; } } else { $(this).text(val).trigger('input'); } }); } return this.first().map(function() { if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT' || this instanceof RadioNodeList) { return this.value || ''; } else { return $(this).text() || ''; } }).get(0) || ''; }; $.fn.values = function(val) { if (typeof val !== 'undefined') { return this.value(val); } return this.map(function() { if (this.tagName == 'TEXTAREA' || this.tagName == 'INPUT') { return this.value || ''; } else { return $(this).text() || ''; } }).get() || []; }; $.fn.initTextarea = function(options) { options = options || {}; function getRangeText(range) { var div = document.createElement('DIV'); div.appendChild(range.cloneContents()); return getText(div, true); } function isBlockEl(el) { var blockTags = {ADDRESS: 1, ARTICLE: 1, ASIDE: 1, AUDIO: 1, BLOCKQUOTE: 1, CANVAS: 1, DD: 1, DIV: 1, DL: 1, FIELDSET: 1, FIGCAPTION: 1, FIGURE: 1, FIGURE: 1, FIGCAPTION: 1, FOOTER: 1, FORM: 1, H1: 1, H2: 1, H3: 1, H4: 1, H5: 1, H6: 1, HEADER: 1, HGROUP: 1, HR: 1, LI: 1, MAIN: 1, NAV: 1, NOSCRIPT: 1, OL: 1, OUTPUT: 1, P: 1, PRE: 1, SECTION: 1, TABLE: 1, TFOOT: 1, UL: 1, VIDEO: 1}; // return (el.nodeType == el.ELEMENT_NODE && blockTags[el.tagName]); if (el.nodeType == el.ELEMENT_NODE) { var display = $(el).css('display'); if (!display) return blockTags[el.tagName]; return (display == 'block' || display == 'table' || display == 'table-row'); } return false; } function isMetadataEl(el) { var metadataTags = {HEAD: 1, TITLE: 1, BASE: 1, LINK: 1, META: 1, STYLE: 1, SCRIPT: 1}; return (el.nodeType == el.ELEMENT_NODE && metadataTags[el.tagName]); } function getText(el, safe_last_br) { var child = el.firstChild, blocks = [], block = ''; while (child) { if (child.nodeType == child.TEXT_NODE) { block += child.nodeValue; } else if (child.nodeType == child.ELEMENT_NODE && !isMetadataEl(child)) { if (child.tagName == 'BR') { block += '\n'; } else if (child.tagName == 'IMG') { block += child.getAttribute('alt') || ''; } else if (!isBlockEl(child)) { block += getText(child); } else { if (block.length > 0) { if (block.substr(-1) == '\n') { block = block.slice(0, -1); } blocks.push(block); block = ''; } blocks.push(getText(child, safe_last_br)); } } child = child.nextSibling; } if (block.length > 0) { if (!safe_last_br && block.substr(-1) == '\n') { block = block.slice(0, -1); } blocks.push(block); } return blocks.join('\n'); } function getTextNodesIn(node) { var textNodes = []; if (node.nodeType == node.TEXT_NODE) { textNodes.push(node); } else { for (var i = 0, len = node.childNodes.length; i < len; ++i) { textNodes.push.apply(textNodes, getTextNodesIn(node.childNodes[i])); } } return textNodes; } function editableClosest(el) { while (el) { if (el.nodeType == el.ELEMENT_NODE && el.getAttribute('contenteditable') == 'true') { return el; } el = el.parentNode; } return null; } function nonEditableClosest(el) { while (el) { if (el.tagName == 'MARK' && el.getAttribute('contenteditable') == 'false') { return el; } el = el.parentNode; } return null; } function setSelectionRange(el, start, end) { var sel = window.getSelection(); sel.removeAllRanges(); var textNodes = getTextNodesIn(el); var charCount = 0, endCharCount, i, textNode, node, offset, nonEditEl; for (i = 0, charCount = 0; textNode = textNodes[i++]; ) { endCharCount = charCount + textNode.length; if (start >= charCount && (start < endCharCount || (start == endCharCount && i <= textNodes.length))) { if (nonEditEl = nonEditableClosest(textNode)) { var range = document.createRange(); if (start < end) range.setStartBefore(nonEditEl); else range.setStartAfter(nonEditEl); node = range.startContainer; offset = range.startOffset; } else { node = textNode; offset = start - charCount; } sel.collapse(node, offset); break; } charCount = endCharCount; } if (start != end) { for (i = 0, charCount = 0; textNode = textNodes[i++]; ) { endCharCount = charCount + textNode.length; if (end >= charCount && (end < endCharCount || (end == endCharCount && i <= textNodes.length))) { if (nonEditEl = nonEditableClosest(textNode)) { var range = document.createRange(); if (start < end) range.setStartAfter(nonEditEl); else range.setStartBefore(nonEditEl); node = range.startContainer; offset = range.startOffset; } else { node = textNode; offset = end - charCount; } sel.extend(node, offset); break; } charCount = endCharCount; } } } function onKeyDown(e) { if ((e.metaKey || e.ctrlKey) && !e.altKey && e.which == 90) { // Z e.preventDefault(); if (e.shiftKey) { redo(this); } else { undo(this); } } else if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.which == 89) { // Y e.preventDefault(); redo(this); } else if (!e.shiftKey && !e.altKey && e.which == 13) { // Enter if ((e.metaKey || e.ctrlKey) || $(this).data('textOptions').singleLine) { e.preventDefault(); $(this).parents('form').submit(); } } else if ((e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.which == 73 && $(this).data('textOptions').allowTokens) { // I e.preventDefault(); $(this).data('$tokens').filter(':not(.used)').eq(0).trigger('click'); } else if (!e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey && (e.which == Keys.LEFT || e.which == Keys.RIGHT || e.which == Keys.BACKSPACE)) { var isLeft = e.which == Keys.LEFT || e.which == Keys.BACKSPACE; var isBackspace = e.which == Keys.BACKSPACE; var sel = window.getSelection(); if (sel.isCollapsed && sel.focusNode) { if (sel.focusNode.nodeType == sel.focusNode.TEXT_NODE) { var newOffset = sel.focusOffset + (isLeft ? -1 : 1); if (newOffset < 0) { var prevNode = sel.focusNode.previousSibling; if (prevNode && prevNode.nodeType == prevNode.ELEMENT_NODE) { var range = document.createRange(); range.setStartBefore(prevNode); if (isBackspace) { range.setEnd(sel.focusNode, sel.focusOffset); range.deleteContents(); $(sel.focusNode).closest('.input').trigger('input'); } else { sel.collapse(range.startContainer, range.startOffset); } e.preventDefault(); } else { if (isBackspace) { $(sel.focusNode).closest('.input').trigger('backspaceonleft'); } } } else if (newOffset > sel.focusNode.nodeValue.length) { var nextNode = sel.focusNode.nextSibling; if (nextNode.nodeType == nextNode.ELEMENT_NODE && nextNode.tagName != 'BR') { var range = document.createRange(); range.setStartAfter(nextNode); if (!isBackspace) { sel.collapse(range.startContainer, range.startOffset); } e.preventDefault(); } } } else if (sel.focusNode.nodeType == sel.focusNode.ELEMENT_NODE) { var curNode = sel.focusNode.childNodes[sel.focusOffset]; if (isLeft) { var prevNode = curNode ? curNode.previousSibling : sel.focusNode.lastChild; while (prevNode && prevNode.nodeType == prevNode.TEXT_NODE && !prevNode.nodeValue.length) { prevNode = prevNode.previousSibling; } if (prevNode && prevNode.nodeType == prevNode.ELEMENT_NODE) { if (isBackspace) { var range = document.createRange(); range.selectNode(prevNode); range.deleteContents(); $(sel.focusNode).closest('.input').trigger('input'); } else { sel.collapse(sel.focusNode, sel.focusOffset - 1); } e.preventDefault(); } else if (prevNode && prevNode.nodeType == prevNode.TEXT_NODE) { if (isBackspace) { var range = document.createRange(); range.setStart(prevNode, prevNode.nodeValue.length - 1); range.setEnd(prevNode, prevNode.nodeValue.length); range.deleteContents(); $(sel.focusNode).closest('.input').trigger('input'); } else { sel.collapse(prevNode, prevNode.nodeValue.length - 1); } e.preventDefault(); } else { if (isBackspace) { $(sel.focusNode).closest('.input').trigger('backspaceonleft'); } } } else { if (curNode && curNode.nodeType == curNode.ELEMENT_NODE && curNode.tagName != 'BR') { sel.collapse(sel.focusNode, sel.focusOffset + 1); e.preventDefault(); } else if (curNode && curNode.nodeType == curNode.TEXT_NODE) { sel.collapse(curNode, 1); e.preventDefault(); } } } } } } function getFieldRange(field) { var sel = window.getSelection(); if (sel.anchorNode && sel.focusNode) { var rng = document.createRange(); rng.setStart(field, 0); rng.setEnd(sel.anchorNode, sel.anchorOffset); var startOffset = getRangeText(rng).length; rng.setEnd(sel.focusNode, sel.focusOffset); var endOffset = getRangeText(rng).length; return {startOffset: startOffset, endOffset: endOffset}; } var offset = field.childNodes.length; if (field.lastChild && field.lastChild.tagName == 'BR') { offset--; } return {startOffset: offset, endOffset: offset}; } function setFieldRange(field, fieldRange) { if (fieldRange) { setSelectionRange(field, fieldRange.startOffset, fieldRange.endOffset); } } function onSetFocus() { setFieldRange(this, $(this).data('prevSelRange')); } function update(field, text, fieldRange) { var $field = $(field); var tokens = $field.data('tokens'); var options = $field.data('textOptions'); if (options.checkText) { text = options.checkText(text); } var html = cleanHTML(text), fhtml; if (options.allowTokens) { var avail_tokens = []; $.each(tokens, function(i, value) { avail_tokens[i] = cleanHTML(value); }); var avail_count = tokens.length; var $tokens = $field.data('$tokens'); if (avail_count > 0) { html = html.replace(TOKEN_REGEX, function(s) { var i = avail_tokens.indexOf(s); if (i >= 0) { avail_tokens[i] = null; avail_count--; var $token = $tokens.eq(i); if (!$token.hasClass('used')) { $token.prepareSlideX().addClass('used'); } return '' + s + ''; } else { return s; } }); $tokens.each(function(i) { if (avail_tokens[i] !== null) { var $token = $(this); if ($token.hasClass('used')) { $token.prepareSlideX().removeClass('used'); } } }); } $tokens.parents('.key-add-tokens-wrap').toggleClass('empty', !avail_count) } if (options.allowEmoji && options.emojiRE) { html = html.replace(options.emojiRE, function(s) { return '' + EmojiSearch.emojiHtml(s) + ''; }); } html = html.split(getBR()).join('\n'); if (options.singleLine) { html = html.replace(/^\n+|\n+$/g, '').replace(/\n+/g, ' '); } fhtml = $field.html(); if (fhtml === html) { $field.append('
').toggleClass('empty', !$field.text().length); return; } if (fhtml === html + getBR()) { $field.toggleClass('empty', !$field.text().length); return; } fieldRange = fieldRange || getFieldRange(field); $field.html(html + getBR()).toggleClass('empty', !$field.text().length); setFieldRange(field, fieldRange); } function onInput() { var field = this; var $field = $(this); var text = getText(field); update(field, text); var history = $field.data('history'); var fieldRange = getFieldRange(field); var prevSelRange = $field.data('prevSelRange'); var time = +(new Date); history.list = history.index >= 0 ? history.list.slice(0, history.index + 1) : []; if (history.index >= 0 && history.list[history.index]) { var entry = history.list[history.index]; if (entry.text == text) { return; } if (time - entry.time < 1000 && entry.redoSel.startOffset == entry.redoSel.endOffset && (entry.text.length - entry.redoSel.endOffset) == (text.length - fieldRange.endOffset)) { entry.text = text; entry.redoSel = fieldRange; return; } entry.undoSel = prevSelRange; } history.list.push({text: text, redoSel: fieldRange, time: time}); history.index++; } function undo(field) { var $field = $(field); var history = $field.data('history'); if (history.index > 0) { history.index--; var entry = history.list[history.index]; update(field, entry.text, entry.undoSel); } } function redo(field) { var $field = $(field); var history = $field.data('history'); if (history.index < history.list.length - 1) { history.index++; var entry = history.list[history.index]; update(field, entry.text, entry.redoSel); } } function onSelectionChange() { $(this).data('prevSelRange', getFieldRange(this)); var sel = window.getSelection(); if (sel.isCollapsed) { var nonEditEl; if (nonEditEl = nonEditableClosest(sel.focusNode)) { var range = document.createRange(); if (sel.focusOffset < $(nonEditEl).text().length / 2) { range.setStartBefore(nonEditEl); } else { range.setStartAfter(nonEditEl); } sel.collapse(range.startContainer, range.startOffset); } else if (sel.focusNode === this && sel.focusOffset == this.childNodes.length && this.lastChild && this.lastChild.nodeType == 'BR') { sel.collapse(this, this.childNodes.length - 1); } else if (sel.focusNode.nodeType == sel.focusNode.TEXT_NODE && sel.focusOffset == sel.focusNode.nodeValue.length) { var range = document.createRange(); range.setStartAfter(sel.focusNode); sel.collapse(range.startContainer, range.startOffset); } } } if (!$(document).data('selectionchange_inited')) { $(document).data('selectionchange_inited', true); document.execCommand('autoUrlDetect', false, false); $(document).on('selectionchange', function() { var sel = window.getSelection(); var anchorField, focusField; var field, offset; if (sel.anchorNode && (anchorField = editableClosest(sel.anchorNode))) { $(anchorField).triggerHandler('selectionchange'); } if (sel.focusNode && (focusField = editableClosest(sel.focusNode)) && anchorField != focusField) { $(focusField).triggerHandler('selectionchange'); } if (!sel.focusNode && document.activeElement && document.activeElement.getAttribute('contenteditable') == 'true') { field = document.activeElement; offset = field.childNodes.length; if (field.lastChild.tagName == 'BR') { offset--; } sel.collapse(field, offset); } }); } return this.each(function() { var field = this; var $field = $(field); var textOptions = $.extend({}, options); if ($field.data('inited')) { return; } $field.attr('contenteditable', 'true'); $field.data('textOptions', textOptions); function insertTag(e) { e.preventDefault(); document.execCommand('insertText', false, $(this).attr('data-token')); $field.focus(); } $field.data('history', {list: [], index: -1}); if (options.allowTokens) { var tokens_attr = $field.attr('data-tokens'); var tokens = tokens_attr ? tokens_attr.split(' ') : []; var $tokensBtns = $('
'); for (var i = 0; i < tokens.length; i++) { var token = tokens[i] = tokens[i].replace('\xa0', ' '); var $token = $(''); $token.attr('data-token', token).appendTo($tokensBtns); } var ua = navigator.userAgent || '', is_mac = ua.indexOf('Mac') >= 0 || ua.indexOf('AppleWebKit') >= 0 && /Mobile\/\w+/.test(ua); var shortcut = is_mac ? '⌘I' : 'Ctrl+I'; $tokensBtns.attr('data-shortcut', shortcut).wrap('
').parent().wrap('
').parent().toggleClass('empty', !tokens.length).insertAfter($field); var $tokens = $('.field-ins-btn', $tokensBtns); $tokens.on('click.tr-textarea', insertTag); $field.data('$tokens', $tokens); $field.data('tokens', tokens); } if ($field.is('[data-single-line]')) { textOptions.singleLine = true; } if ($field.is('[data-value]')) { $field.value($field.defaultValue()); } else { $field.defaultValue($field.value()); } $field.on('selectionchange.tr-textarea', onSelectionChange); $field.on('keydown.tr-textarea', onKeyDown); $field.on('input.tr-textarea', onInput); $field.on('setfocus.tr-textarea', onSetFocus); $field.trigger('input'); $field.data('inited', true); }); }; $.fn.destroyTextarea = function() { return this.off('.tr-textarea').each(function() { $(this).data('inited', false); var $tokens = $(this).data('$tokens'); if ($tokens) { $tokens.off('.tr-textarea'); } }); }; $.fn.blockBodyScroll = function() { function onResultsMouseWheel(e) { var d = e.originalEvent.wheelDelta; if((this.scrollTop === (this.scrollHeight - this.clientHeight) && d < 0) || (this.scrollTop === 0 && d > 0)) { e.preventDefault(); } } return this.on('mousewheel', onResultsMouseWheel); }; $.fn.initAutosize = function() { return this.map(function(){ autosize(this); return this; }); }; $.fn.updateAutosize = function() { return this.map(function(){ autosize.update(this); return this; }); }; $.fn.destroyAutosize = function() { return this.map(function(){ autosize.destroy(this); return this; }); }; })(jQuery); function getBR() { if (window._brHTML) return window._brHTML; return window._brHTML = $('

').html(); } function cleanHTML(value) { return value.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/\n/g, getBR()); } function uncleanHTML(value) { return $('
').html(value).text(); } function cleanRE(value) { return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); } function wrapHighlight(value, highlight, wrap_tag, prefix_only) { value = cleanHTML(value); if (highlight) { var pattern = cleanRE(cleanHTML(highlight)); if (prefix_only) { pattern = '^' + pattern; } value = value.replace(new RegExp(pattern, 'gi'), '$&<\/strong>'); } if (wrap_tag) { value = value.replace(TOKEN_REGEX, '$&'); } return value; } function wrapSize(size) { if (size < 1024) { return size + ' B'; } else if (size < 1048576) { return (Math.round(size * 10 / 1024.0) / 10) + ' KB'; } else if (size < 1073741824) { return (Math.round(size * 10 / 1048576.0) / 10) + ' MB'; } else { return (Math.round(size * 10 / 1073741824.0) / 10) + ' GB'; } } function dataUrlToBlob(url) { try { var match = null; if (match = url.match(/^data:(image\/gif|image\/jpe?g|image\/png|video\/mp4);base64,(.*)$/)) { var type = match[1], b64 = match[2]; var binary = atob(b64); var array = []; for(var i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], {type: type}); } } catch (e) {} return false; } function copyToClipboard(str) { var $text = $('