锘?function ($) { $.fn.dnnTabs = function (options) { var opts = $.extend({}, $.fn.dnnTabs.defaultOptions, options), $wrap = this; // patch for period in selector - http://jsfiddle.net/9Mst9/2/ $.ui.tabs.prototype._sanitizeSelector = function (hash) { return hash.replace(/:/g, "\\:").replace(/\./g, "\\\."); }; $wrap.each(function () { var showEvent = null, cookieId; if (this.id) { cookieId = 'dnnTabs-' + this.id; if (opts.selected === -1) { var cookieValue = dnn.dom.getCookie(cookieId); if (cookieValue) { opts.selected = cookieValue; } if (opts.selected === -1) { opts.selected = 0; } } } showEvent = (function (cookieId) { return function (event, ui) { dnn.dom.setCookie(cookieId, ui.index, opts.cookieDays, '/', '', false, opts.cookieMilleseconds); }; })(cookieId); $wrap.tabs({ show: showEvent, selected: opts.selected, disabled: opts.disabled, fx: { opacity: opts.opacity, duration: opts.duration } }); // page validation integration - select tab that contain tripped validators if (typeof Page_ClientValidate != "undefined" && $.isFunction(Page_ClientValidate)) { $wrap.find(opts.validationTriggerSelector).click(function (e) { if (!Page_ClientValidate(opts.validationGroup)) { var invalidControl = $wrap.find(opts.invalidItemSelector).eq(0); var $parent = invalidControl.closest(".ui-tabs-panel"); if ($parent.length > 0) { var tabId = $parent.attr("id"); $parent.parent().find("a[href='#" + tabId + "']").click(); } } }); }; }); return $wrap; }; $.fn.dnnTabs.defaultOptions = { opacity: 'toggle', duration: 'fast', validationTriggerSelector: '.dnnPrimaryAction', validationGroup: '', invalidItemSelector: '.dnnFormError[style*="inline"]', regionToToggleSelector: 'fieldset', selected: -1, cookieDays: 0, cookieMilleseconds: 120000 // two minutes }; })(jQuery); (function ($) { $.fn.dnnConfirm = function (options) { var opts = $.extend({}, $.fn.dnnConfirm.defaultOptions, options), $wrap = this; $wrap.each(function () { var $this = $(this), defaultAction = $this.attr('href'), $dnnDialog; if (defaultAction || opts.isButton) { $dnnDialog = $("
").html(opts.text).dialog(opts); $this.click(function (e, isTrigger) { if (isTrigger) { return true; } if ($dnnDialog.is(':visible')) { $dnnDialog.dialog("close"); return true; } e.preventDefault(); $dnnDialog.dialog({ open: function (e) { $('.ui-dialog-buttonpane').find('button:contains("' + opts.noText + '")').addClass('dnnConfirmCancel'); }, position: 'center', buttons: [ { text: opts.yesText, click: function () { $dnnDialog.dialog("close"); if ($.isFunction(opts.callbackTrue)) { opts.callbackTrue.call(this); } else { if (opts.isButton) { $this.trigger("click", [true]); } else { window.location.href = defaultAction; } } }, 'class': opts.buttonYesClass }, { text: opts.noText, click: function () { $(this).dialog("close"); if ($.isFunction(opts.callbackFalse)) { opts.callbackFalse.call(this); }; }, 'class': opts.buttonNoClass } ] }); $dnnDialog.dialog('open'); }); } }); return $wrap; }; $.fn.dnnConfirm.defaultOptions = { text: 'Are you sure?', yesText: 'Yes', noText: 'No', buttonYesClass: 'dnnPrimaryAction', buttonNoClass: 'dnnSecondaryAction', actionUrl: window.location.href, autoOpen: false, resizable: false, modal: true, title: 'Confirm', dialogClass: 'dnnFormPopup dnnClear', isButton: false }; })(jQuery); (function ($) { $.dnnAlert = function (options) { var opts = $.extend({}, $.dnnAlert.defaultOptions, options), $dnnDialog = $("
").html(opts.text).dialog(opts); $dnnDialog.dialog({ buttons: [ { text: opts.okText, "class": opts.buttonOkClass, click: function () { $(this).dialog("close"); if ($.isFunction(opts.callback)) { opts.callback.call(this); }; return false; } } ] }); $dnnDialog.dialog('open'); }; $.dnnAlert.defaultOptions = { okText: 'Ok', autoOpen: false, resizable: false, modal: true, buttonOkClass: 'dnnPrimaryAction', dialogClass: 'dnnFormPopup dnnClear' }; })(jQuery); (function ($) { $.fn.dnnPanels = function (options) { var opts = $.extend({}, $.fn.dnnPanels.defaultOptions, options), $wrap = this; $wrap.each(function () { var $this = $(this); // wire up click event to perform slide toggle $this.find(opts.clickToToggleSelector).click(function (e) { e.preventDefault(); var toggle = $(this).toggleClass(opts.toggleClass).parent().next(opts.regionToToggleSelector).slideToggle(function () { var id = $(toggle.context.parentNode).attr("id"); var cookieId = id ? id.replace(/[^a-zA-Z0-9\-]+/g, "") : ''; if (cookieId) dnn.dom.setCookie(cookieId, $(this).is(':visible'), opts.cookieDays, '/', '', false, opts.cookieMilleseconds); }); //stop event from called again e.stopImmediatePropagation(); }); function collapsePanel($clicker, $region) { $clicker.removeClass(opts.toggleClass); $region.hide(); } function expandPanel($clicker, $region) { $clicker.addClass(opts.toggleClass); $region.show(); } // walk over each selector and expand or collapse as necessary $this.find(opts.sectionHeadSelector).each(function (indexInArray, valueOfElement) { var $this = $(valueOfElement), elementId = $this.attr("id"), cookieId = elementId ? elementId.replace(/[^a-zA-Z0-9\-]+/g, "") : '', cookieValue = cookieId ? dnn.dom.getCookie(cookieId) : '', $clicker = $this.find(opts.clickToToggleIsolatedSelector), $region = $this.next(opts.regionToToggleSelector), $parentSeparator = $this.parents(opts.panelSeparatorSelector), groupPanelIndex = $parentSeparator.find(opts.sectionHeadSelector).index($this) if (cookieValue == "false") { // cookie explicitly set to false collapsePanel($clicker, $region); } else if (cookieValue == "true" || indexInArray === 0) { // cookie set to true OR first panel expandPanel($clicker, $region); } else if ($parentSeparator.size() > 0 && groupPanelIndex === 0) { // grouping is used & its the first panel in its group expandPanel($clicker, $region); } else { // nada... collapsePanel($clicker, $region); } }); // page validation integration - expand collapsed panels that contain tripped validators $this.find(opts.validationTriggerSelector).click(function (e) { if (typeof Page_ClientValidate != "undefined" && $.isFunction(Page_ClientValidate)) { if (!Page_ClientValidate(opts.validationGroup)) { $this.find(opts.invalidItemSelector).each(function () { var $parent = $(this).closest(opts.regionToToggleSelector); if ($parent.is(':hidden')) { $parent.prev(opts.sectionHeadSelector).find(opts.clickToToggleIsolatedSelector).click(); } }); } } }); }); return $wrap; }; $.fn.dnnPanels.defaultOptions = { clickToToggleSelector: 'h2.dnnFormSectionHead a', sectionHeadSelector: '.dnnFormSectionHead', regionToToggleSelector: 'fieldset', toggleClass: 'dnnSectionExpanded', clickToToggleIsolatedSelector: 'a', validationTriggerSelector: '.dnnPrimaryAction', invalidItemSelector: '.dnnFormError[style*="inline"]', validationGroup: '', panelSeparatorSelector: '.ui-tabs-panel', cookieDays: 0, cookieMilleseconds: 120000 // two minutes }; })(jQuery); (function ($) { $.fn.dnnPreview = function (options) { var opts = $.extend({}, $.fn.dnnPreview.defaultOptions, options), $wrap = this; $wrap.each(function () { var $this = $(this); $this.find(opts.linkSelector).click(function (e) { e.preventDefault(); var params = "?"; var skin, container; if (opts.useComboBox) { var skinComboBox = $find(opts.skinSelector); var containerComboBox = $find(opts.containerSelector); skin = skinComboBox ? skinComboBox.get_value() : ''; container = containerComboBox ? containerComboBox.get_value() : ''; } else { skin = $this.find(opts.skinSelector).val(); container = $this.find(opts.containerSelector).val(); } if (skin) { params += "SkinSrc=" + skin; } if (container) { if (skin) { params += "&"; } params += "ContainerSrc=" + container; } if (params != "?") { window.open(encodeURI(opts.baseUrl + params.replace(/.ascx/gi, '')), "skinpreview"); } else { $.dnnAlert({ text: opts.noSelectionMessage, okText: opts.alertOkText, closeText: opts.alertCloseText }); } }); }); return $wrap; }; $.fn.dnnPreview.defaultOptions = { baseUrl: window.location.protocol + "//" + window.location.host + window.location.pathname, linkSelector: 'a.dnnSecondaryAction', skinSelector: '', containerSelector: '', noSelectionMessage: 'Please select a preview option.', alertOkText: 'Ok', alertCloseText: 'close', useComboBox: false }; })(jQuery); (function ($) { $.fn.dnnExpandAll = function (options) { var opts = $.extend({}, $.fn.dnnExpandAll.defaultOptions, options), $elem = this; if (($(opts.targetArea).find(opts.targetSelector + ':visible').length === $(opts.targetArea).find(opts.targetSelector + opts.targetExpandedSelector + ':visible').length) && !$(this).hasClass('expanded')) { $(this).addClass('expanded').text(opts.collapseText); } $elem.click(function (e) { e.preventDefault(); var $this = $(this); if ($this.hasClass('expanded')) { $this.removeClass('expanded').text(opts.expandText); $(opts.targetArea).find(opts.targetSelector + opts.targetExpandedSelector + ':visible').click(); } else { $this.addClass('expanded').text(opts.collapseText); $(opts.targetArea).find(opts.targetSelector + ':visible').not(opts.targetExpandedSelector).click(); } //stop event from called again e.stopImmediatePropagation(); }); return $elem; }; $.fn.dnnExpandAll.defaultOptions = { expandText: 'Expand All', collapseText: 'Collapse All', targetArea: '#dnnHostSettings', targetSelector: 'h2.dnnFormSectionHead a', targetExpandedSelector: '.dnnSectionExpanded' }; })(jQuery); (function ($) { $.fn.dnnTooltip = function (options) { var opts = $.extend({}, $.fn.dnnTooltip.defaultOptions, options), $wrap = this; $wrap.each(function () { var $this = $(this); var dnnFormHelp = $this.prev(); dnnFormHelp.click(function (e) { e.preventDefault(); }); var helpSelector = $this.find(opts.helpSelector); $this.parent().css({ position: 'relative' }); var tooltipHeight = helpSelector.height(); var top = -(tooltipHeight + 30); $this.css({ position: 'absolute', right: '-29%', top: top + 'px' }); var hoverOnToolTip = false, hoverOnPd = false; dnnFormHelp.hoverIntent({ over: function () { hoverOnPd = true; helpSelector.show(); }, out: function () { hoverOnPd = false; if (!$this.hasClass(opts.pinnedClass) && !hoverOnToolTip) { helpSelector.hide(); } }, timeout: 200, interval: 200 }); helpSelector.hover(function () { hoverOnToolTip = true; }, function () { hoverOnToolTip = false; if (!$this.hasClass(opts.pinnedClass) && !hoverOnPd) { helpSelector.hide(); } }); var pinHelper = helpSelector.find(opts.pinSelector); pinHelper.click(function (e) { e.preventDefault(); if ($this.hasClass(opts.pinnedClass)) { helpSelector.css({ "left": '0px', "top": '0px' }).hide().draggable('destroy'); $this.removeClass(opts.pinnedClass); } else { $this.addClass(opts.pinnedClass); if ($.isFunction($().draggable)) { helpSelector.draggable(); } } }); }); return $wrap; }; $.fn.dnnTooltip.defaultOptions = { pinSelector: 'a.pinHelp', helpSelector: '.dnnFormHelpContent', pinnedClass: 'dnnTooltipPinned' }; })(jQuery); (function ($) { var CB = function (e) { if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); }; /* DNN customized checkbox/radiobox */ $.fn.dnnCheckbox = function (options) { /* Default settings */ var settings = { cls: 'dnnCheckbox' /* checkbox */ }; /* Processing settings */ settings = $.extend(settings, options || {}); /* Adds check/uncheck & disable/enable events */ var addEvents = function (object) { var checked = object.checked; var disabled = object.disabled; var $object = $(object); if (object.stateInterval) clearInterval(object.stateInterval); object.stateInterval = setInterval( function () { if (object.disabled != disabled) $object.trigger((disabled = !!object.disabled) ? 'disable' : 'enable'); if (object.checked != checked) $object.trigger((checked = !!object.checked) ? 'check' : 'uncheck'); }, 10 /* in miliseconds. Low numbers this can decrease performance on slow computers, high will increase responce time */ ); return $object; }; return this.each(function () { var ch = this; /* Reference to DOM Element*/ /* Check we needs to add customization or not*/ if ($(this).hasClass('normalCheckBox') || $(this).hasClass('normalRadioButton')) return; var parentCheckBoxHolder = $(this).closest('.normalCheckBox'); var parentRadioButtonHolder = $(this).closest('.normalRadioButton'); if (parentCheckBoxHolder.length || parentRadioButtonHolder.length) return; var $ch = addEvents(ch); /* Adds custom events and returns, jQuery enclosed object */ /* Removing wrapper if already applied */ if (ch.wrapper) ch.wrapper.remove(); /* Creating wrapper for checkbox and assigning "hover" event */ ch.wrapper = $(''); ch.wrapperInner = ch.wrapper.children('span:eq(0)'); ch.wrapper.hover( function (e) { ch.wrapperInner.addClass(settings.cls + '-hover'); CB(e); }, function (e) { ch.wrapperInner.removeClass(settings.cls + '-hover'); CB(e); } ); /* Wrapping checkbox */ $ch.css({ position: 'absolute', zIndex: -1, visibility: 'hidden' }).after(ch.wrapper); /* Trying to find "our" label */ var label = false, parentLabel = false; label = $ch.closest('label'); if (!label.length) label = false; else parentLabel = true; if (!label && $ch.attr('id')) { label = $('label[for="' + $ch.attr('id') + '"]'); if (!label.length) label = false; } var isRadCheckBox = $ch.hasClass('rtChk'); // rad checkbox is a special case, use jQuery eventPath. /* Labe found, applying event hanlers */ if (label) { label.css('display', 'inline-block'); if (!parentLabel) { label.click(function (e) { $ch.triggerHandler('focus'); !isRadCheckBox ? ch.click() : $ch.trigger('click', [e]); $ch.trigger('change', [e]); CB(e); return false; }); } else { label.children().each(function () { var $this = $(this); if ($this.is('input')) return; $this.click(function (e) { $ch.triggerHandler('focus'); !isRadCheckBox ? ch.click() : $ch.trigger('click', [e]); $ch.trigger('change', [e]); CB(e); return false; }); }); } } if (!parentLabel) { ch.wrapper.click(function (e) { $ch.triggerHandler('focus'); !isRadCheckBox ? ch.click() : $ch.trigger('click', [e]); $ch.trigger('change', [e]); CB(e); return false; }); } if (isRadCheckBox) { // stop event propagation for checkbox in rad control. $ch.click(function(e) { CB(e); }); } $ch.bind('disable', function () { ch.wrapperInner.addClass(settings.cls + '-disabled'); }).bind('enable', function () { ch.wrapperInner.removeClass(settings.cls + '-disabled'); }); $ch.bind('check', function () { ch.wrapper.addClass(settings.cls + '-checked'); }).bind('uncheck', function () { ch.wrapper.removeClass(settings.cls + '-checked'); }); /* Applying checkbox state */ if (ch.checked) ch.wrapper.addClass(settings.cls + '-checked'); if (ch.disabled) ch.wrapperInner.addClass(settings.cls + '-disabled'); }); }; $.fn.dnnHelperTipDestroy = function () { return this.each(function () { var pd = this; if (pd.id) { $('div[data-tipholder="' + pd.id + '"]').remove(); } }); }; $.fn.dnnHelperTip = function (options) { /* Default settings */ var settings = { cls: 'dnnHelperTip', helpContent: "This is hover helper tooltip", holderId: '', show: false // immediately show tooltip after call }; /* Processing settings */ settings = $.extend(settings, options || {}); return this.each(function () { var pd = this; var $pd = $(this); /* Removing tooltip if already applied */ if (pd.tooltipWrapper) pd.tooltipWrapper.remove(); /* Creating tooltip wrapper for selected dom and assigning "hover" event */ pd.tooltipWrapper = $('
' + settings.helpContent + '
'); $('body').append(pd.tooltipWrapper); pd.tooltipWrapper.css({ position: 'absolute' }); pd.tooltipWrapperInner = $('.dnnFormHelpContent', pd.tooltipWrapper); var tooltipHeight = pd.tooltipWrapperInner.height(); pd.tooltipWrapperInner.css({ left: '-10px', top: -(tooltipHeight + 30) + 'px' }) var hoverOnPd = false; $pd.hover( function () { hoverOnPd = true; setTimeout(function () { if (hoverOnPd) pd.tooltipWrapperInner.show(); }, 400); }, function () { hoverOnPd = false; setTimeout(function () { if (!hoverOnPd) pd.tooltipWrapperInner.hide(); }, 400); }); if (settings.show) { hoverOnPd = true; setTimeout(function () { pd.tooltipWrapperInner.show(); }, 400); } $pd.bind('mousemove', function (e) { var x = e.pageX; var y = e.pageY; var pos = $('body').css('position'); if (pos == 'relative') y -= 38; pd.tooltipWrapper.css({ left: x + 'px', top: y + 'px', 'z-index': '99999' }); }); }); }; $.fn.dnnProgressbar = function () { var $pd = $(this); var pd = this; if (pd.tooltipWrapper) pd.tooltipWrapper.remove(); pd.tooltipWrapper = $('
').insertAfter($pd); pd.tooltipWrapperInner = $('.dnnFormHelpContent', pd.tooltipWrapper); pd.tooltipWrapperInner.css({ width: '32px', padding: '7px' }); $pd.parent().css({ position: 'relative' }); var hoverOnToolTip = false, hoverOnPd = false; $pd.hoverIntent({ over: function () { hoverOnPd = true; var val = $(this).children(':first').progressbar('value'); pd.update(val); pd.tooltipWrapperInner.show(); }, out: function () { hoverOnPd = false; if (!hoverOnToolTip) { pd.tooltipWrapperInner.hide(); } }, timeout: 200, interval: 200 }); pd.tooltipWrapperInner.hover(function () { hoverOnToolTip = true; }, function () { hoverOnToolTip = false; if (!hoverOnPd) { pd.tooltipWrapperInner.hide(); } }); pd.update = function(value) { pd.tooltipWrapperInner.find('span').html(value + ' %'); var pdTop = $pd.position().top, tooltipHeight = pd.tooltipWrapperInner.height(); pdTop -= (tooltipHeight + 10); var pdLeft = value > 50 ? (value - 4) + '%' : value > 0 ? (value - 2) + '%' : '10px'; pd.tooltipWrapper.css({ position: 'absolute', left: pdLeft, top: pdTop + 'px' }); }; return this; }; $.fn.dnnSpinner = function (options) { // set default options var opt = $.extend({ type: 'range', typedata: '', width: '150px', looping: false }, options); var otypedata; if (options != null && options.typedata != null) { otypedata = $.extend({ min: 1, max: 10, interval: 1, decimalplaces: 0 }, options.typedata); } else { otypedata = $.extend({ min: 1, max: 10, interval: 1, decimalplaces: 0 }); } opt.typedata = otypedata; var inputControl = this; // validate if the object is a input of text type. if (!inputControl.is(':text')) return inputControl; if (inputControl.hasClass('dnnSpinnerInput')) { return inputControl; } else { inputControl.addClass('dnnSpinnerInput'); } // create the Spinner Control body. var strContainerDiv = ''; strContainerDiv += '
'; strContainerDiv += '
'; strContainerDiv += '
'; strContainerDiv += ''; strContainerDiv += ''; strContainerDiv += '
'; // add the above created control to page var objContainerDiv = $(strContainerDiv).insertAfter(inputControl); // hide the input control and place within the Spinner Control body inputControl.insertAfter($("div.dnnSpinnerDisplay", objContainerDiv)); $("div.dnnSpinnerDisplay", objContainerDiv).click(function () { if (opt.type == 'range') { var displayCtrl = $(this); // show inner input var innerInput = $('input[type="text"]', displayCtrl); if (innerInput.length < 1) { var originalVal = displayCtrl.html(); innerInput = $('').val(originalVal); displayCtrl.html(innerInput); innerInput.blur(function () { var newVal = $(this).val(); if (newVal > opt.typedata.max) { newVal = opt.typedata.max; } if (newVal < opt.typedata.min) { newVal = opt.typedata.min; } $(this).remove(); selectedValue = parseInt(newVal); inputControl.val(newVal); displayCtrl.html(newVal); }).keypress(function (e) { var regex = new RegExp("^[0-9]+$"); var key = String.fromCharCode(!e.charCode ? e.which : e.charCode); if (!regex.test(key)) { event.preventDefault(); return false; } }); } innerInput.focus(); } inputControl.triggerHandler('focus'); }); inputControl.css('display', 'none'); switch (opt.type) { case 'range': // set default value; if (opt.defaultVal < opt.typedata.min || opt.defaultVal > opt.typedata.max) { opt.defaultVal = opt.typedata.min; } if (opt.defaultVal % opt.typedata.interval > 0) { opt.defaultVal = parseInt((opt.defaultVal / opt.typedata.interval).toFixed(0)) * opt.typedata.interval; } inputControl.val(opt.defaultVal.toFixed(opt.typedata.decimalplaces)); ($("div.dnnSpinnerDisplay", objContainerDiv)).html(opt.defaultVal.toFixed(opt.typedata.decimalplaces)); var selectedValue = opt.defaultVal; if ((opt.typedata.max - opt.typedata.min) > opt.typedata.interval) { // attach events; $("a.dnnSpinnerTopButton", objContainerDiv).click(function () { if ((selectedValue + opt.typedata.interval) <= opt.typedata.max || opt.looping) { if ((selectedValue + opt.typedata.interval) > opt.typedata.max) { selectedValue = opt.typedata.min - opt.typedata.interval; } var valueData = (selectedValue + opt.typedata.interval).toFixed(opt.typedata.decimalplaces); selectedValue += opt.typedata.interval; ($("div.dnnSpinnerDisplay", objContainerDiv)).html(valueData); inputControl.val(valueData); } inputControl.triggerHandler('focus'); return false; }); $("a.dnnSpinnerBotButton", objContainerDiv).click(function () { if ((selectedValue - opt.typedata.interval) >= opt.typedata.min || opt.looping) { if ((selectedValue - opt.typedata.interval) < opt.typedata.min) { selectedValue = opt.typedata.max + opt.typedata.interval; } var valueData = (selectedValue - opt.typedata.interval).toFixed(opt.typedata.decimalplaces); selectedValue -= opt.typedata.interval; ($("div.dnnSpinnerDisplay", objContainerDiv)).html(valueData); inputControl.val(valueData); } inputControl.triggerHandler('focus'); return false; }); } break; case 'list': if (!opt.typedata.list || opt.typedata.list.lenght == 0) { return inputControl; } var listItems = opt.typedata.list.split(','); var selectedIndex = jQuery.inArray(opt.defaultVal, listItems); if (selectedIndex < 0) { selectedIndex = 0; opt.defaultVal = listItems[0]; } inputControl.val(opt.defaultVal); ($("div.dnnSpinnerDisplay", objContainerDiv)).html(opt.defaultVal); if (listItems.length > 1) { // attach events; $("a.dnnSpinnerBotButton", objContainerDiv).click(function () { if (selectedIndex < (listItems.length - 1) || opt.looping) { if (selectedIndex == listItems.length - 1) { selectedIndex = -1; } selectedIndex++; var valueData = listItems[selectedIndex]; ($("div.dnnSpinnerDisplay", objContainerDiv)).html(valueData); inputControl.val(valueData); } inputControl.triggerHandler('focus'); return false; }); $("a.dnnSpinnerTopButton", objContainerDiv).click(function () { if (selectedIndex > 0 || opt.looping) { if (selectedIndex == 0) { selectedIndex = listItems.length; } selectedIndex--; var valueData = listItems[selectedIndex]; ($("div.dnnSpinnerDisplay", objContainerDiv)).html(valueData); inputControl.val(valueData); } inputControl.triggerHandler('focus'); return false; }); } break; }; // return the selected input control for the chainability return inputControl; }; })(jQuery); // dnn customised jquery autocompleter (function ($) { "use strict"; $.fn.dnnAutocomplete = function (options) { var url; if (arguments.length > 1) { url = options; options = arguments[1]; options.url = url; } else if (typeof options === 'string') { url = options; options = { url: url }; } var opts = $.extend({}, $.fn.dnnAutocomplete.defaults, options); return this.each(function () { var $this = $(this); $this.data('autocompleter', new $.dnnAutocompleter( $this, $.meta ? $.extend({}, opts, $this.data()) : opts )); }); }; /** * Store default options * @type {object} */ $.fn.dnnAutocomplete.defaults = { inputClass: 'acInput', loadingClass: 'acLoading', resultsClass: 'acResults', selectClass: 'acSelect', queryParamName: 'q', extraParams: {}, remoteDataType: false, lineSeparator: '\n', cellSeparator: '|', minChars: 2, maxItemsToShow: 10, delay: 400, useCache: true, maxCacheLength: 10, matchSubset: true, matchCase: false, matchInside: true, mustMatch: false, selectFirst: false, selectOnly: false, showResult: null, preventDefaultReturn: 1, preventDefaultTab: 0, autoFill: false, filterResults: true, sortResults: true, sortFunction: null, onItemSelect: null, onNoMatch: null, onFinish: null, matchStringConverter: null, beforeUseConverter: null, autoWidth: 'min-width', useDelimiter: false, delimiterChar: ',', delimiterKeyCode: 188, processData: null, onError: null, moduleId: null // dnn Module Id context if needed }; /** * Sanitize result * @param {Object} result * @returns {Object} object with members value (String) and data (Object) * @private */ var sanitizeResult = function (result) { var value, data; var type = typeof result; if (type === 'string') { value = result; data = {}; } else if ($.isArray(result)) { value = result[0]; data = result.slice(1); } else if (type === 'object') { value = result.value; data = result.data; } value = String(value); if (typeof data !== 'object') { data = {}; } return { value: value, data: data }; }; /** * Sanitize integer * @param {mixed} value * @param {Object} options * @returns {Number} integer * @private */ var sanitizeInteger = function (value, stdValue, options) { var num = parseInt(value, 10); options = options || {}; if (isNaN(num) || (options.min && num < options.min)) { num = stdValue; } return num; }; /** * Create partial url for a name/value pair */ var makeUrlParam = function (name, value) { return [name, encodeURIComponent(value)].join('='); }; /** * Build an url * @param {string} url Base url * @param {object} [params] Dictionary of parameters */ var makeUrl = function (url, params) { var urlAppend = []; $.each(params, function (index, value) { urlAppend.push(makeUrlParam(index, value)); }); if (urlAppend.length) { url += url.indexOf('?') === -1 ? '?' : '&'; url += urlAppend.join('&'); } return url; }; /** * Default sort filter * @param {object} a * @param {object} b * @param {boolean} matchCase * @returns {number} */ var sortValueAlpha = function (a, b, matchCase) { a = String(a.value); b = String(b.value); if (!matchCase) { a = a.toLowerCase(); b = b.toLowerCase(); } if (a > b) { return 1; } if (a < b) { return -1; } return 0; }; /** * dnnAutocompleter class * @param {object} $elem jQuery object with one input tag * @param {object} options Settings * @constructor */ $.dnnAutocompleter = function ($elem, options) { /** * Assert parameters */ if (!$elem || !($elem instanceof $) || $elem.length !== 1 || $elem.get(0).tagName.toUpperCase() !== 'INPUT') { throw new Error('Invalid parameter for dnnAutocompleter, jQuery object with one element with INPUT tag expected.'); } /** * @constant Link to this instance * @type object * @private */ var self = this; /** * @property {object} Options for this instance * @public */ this.options = options; /** * @property object Cached data for this instance * @private */ this.cacheData_ = {}; /** * @property {number} Number of cached data items * @private */ this.cacheLength_ = 0; /** * @property {string} Class name to mark selected item * @private */ this.selectClass_ = 'jquery-autocomplete-selected-item'; /** * @property {number} Handler to activation timeout * @private */ this.keyTimeout_ = null; /** * @property {number} Handler to finish timeout * @private */ this.finishTimeout_ = null; /** * @property {number} Last key pressed in the input field (store for behavior) * @private */ this.lastKeyPressed_ = null; /** * @property {string} Last value processed by the autocompleter * @private */ this.lastProcessedValue_ = null; /** * @property {string} Last value selected by the user * @private */ this.lastSelectedValue_ = null; /** * @property {boolean} Is this autocompleter active (showing results)? * @see showResults * @private */ this.active_ = false; /** * @property {boolean} Is this autocompleter allowed to finish on blur? * @private */ this.finishOnBlur_ = true; /** * Sanitize options */ this.options.minChars = sanitizeInteger(this.options.minChars, $.fn.dnnAutocomplete.defaults.minChars, { min: 0 }); this.options.maxItemsToShow = sanitizeInteger(this.options.maxItemsToShow, $.fn.dnnAutocomplete.defaults.maxItemsToShow, { min: 0 }); this.options.maxCacheLength = sanitizeInteger(this.options.maxCacheLength, $.fn.dnnAutocomplete.defaults.maxCacheLength, { min: 1 }); this.options.delay = sanitizeInteger(this.options.delay, $.fn.dnnAutocomplete.defaults.delay, { min: 0 }); if (this.options.preventDefaultReturn != 2) { this.options.preventDefaultReturn = this.options.preventDefaultReturn ? 1 : 0; } if (this.options.preventDefaultTab != 2) { this.options.preventDefaultTab = this.options.preventDefaultTab ? 1 : 0; } /** * Init DOM elements repository */ this.dom = {}; /** * Store the input element we're attached to in the repository */ this.dom.$elem = $elem; /** * Switch off the native autocomplete and add the input class */ this.dom.$elem.attr('autocomplete', 'off').addClass(this.options.inputClass); /** * Create DOM element to hold results, and force absolute position */ this.dom.$results = $('
').hide().addClass(this.options.resultsClass).css({ position: 'absolute' }); $('body').append(this.dom.$results); /** * Attach keyboard monitoring to $elem */ $elem.keydown(function (e) { self.lastKeyPressed_ = e.keyCode; switch (self.lastKeyPressed_) { case self.options.delimiterKeyCode: // comma = 188 if (self.options.useDelimiter && self.active_) { self.selectCurrent(); } break; // ignore navigational & special keys case 35: // end case 36: // home case 16: // shift case 17: // ctrl case 18: // alt case 37: // left case 39: // right break; case 38: // up e.preventDefault(); if (self.active_) { self.focusPrev(); } else { self.activate(); } return false; case 40: // down e.preventDefault(); if (self.active_) { self.focusNext(); } else { self.activate(); } return false; case 9: // tab if (self.active_) { self.selectCurrent(); if (self.options.preventDefaultTab) { e.preventDefault(); return false; } } if (self.options.preventDefaultTab === 2) { e.preventDefault(); return false; } break; case 13: // return if (self.active_) { self.selectCurrent(); if (self.options.preventDefaultReturn) { e.preventDefault(); return false; } } if (self.options.preventDefaultReturn === 2) { e.preventDefault(); return false; } break; case 27: // escape if (self.active_) { e.preventDefault(); self.deactivate(true); return false; } break; default: self.activate(); } }); /** * Finish on blur event * Use a timeout because instant blur gives race conditions */ var onBlurFunction = function () { self.deactivate(true); } $elem.blur(function () { if (self.finishOnBlur_) { self.finishTimeout_ = setTimeout(onBlurFunction, 200); } }); /** * Catch a race condition on form submit */ $elem.parents('form').on('submit', onBlurFunction); }; /** * Position output DOM elements * @private */ $.dnnAutocompleter.prototype.position = function () { var offset = this.dom.$elem.offset(); var height = this.dom.$results.outerHeight(); var totalHeight = window.outerHeight; var inputBottom = offset.top + this.dom.$elem.outerHeight(); var bottomIfDown = inputBottom + height; // Set autocomplete results at the bottom of input var position = { top: inputBottom, left: offset.left }; if (bottomIfDown > totalHeight) { // Try to set autocomplete results at the top of input var topIfUp = offset.top - height; if (topIfUp >= 0) { position.top = topIfUp; } } this.dom.$results.css(position); }; /** * Read from cache * @private */ $.dnnAutocompleter.prototype.cacheRead = function (filter) { var filterLength, searchLength, search, maxPos, pos; if (this.options.useCache) { filter = String(filter); filterLength = filter.length; if (this.options.matchSubset) { searchLength = 1; } else { searchLength = filterLength; } while (searchLength <= filterLength) { if (this.options.matchInside) { maxPos = filterLength - searchLength; } else { maxPos = 0; } pos = 0; while (pos <= maxPos) { search = filter.substr(0, searchLength); if (this.cacheData_[search] !== undefined) { return this.cacheData_[search]; } pos++; } searchLength++; } } return false; }; /** * Write to cache * @private */ $.dnnAutocompleter.prototype.cacheWrite = function (filter, data) { if (this.options.useCache) { if (this.cacheLength_ >= this.options.maxCacheLength) { this.cacheFlush(); } filter = String(filter); if (this.cacheData_[filter] !== undefined) { this.cacheLength_++; } this.cacheData_[filter] = data; return this.cacheData_[filter]; } return false; }; /** * Flush cache * @public */ $.dnnAutocompleter.prototype.cacheFlush = function () { this.cacheData_ = {}; this.cacheLength_ = 0; }; /** * Call hook * Note that all called hooks are passed the autocompleter object * @param {string} hook * @param data * @returns Result of called hook, false if hook is undefined */ $.dnnAutocompleter.prototype.callHook = function (hook, data) { var f = this.options[hook]; if (f && $.isFunction(f)) { return f(data, this); } return false; }; /** * Set timeout to activate autocompleter */ $.dnnAutocompleter.prototype.activate = function () { var self = this; if (this.keyTimeout_) { clearTimeout(this.keyTimeout_); } this.keyTimeout_ = setTimeout(function () { self.activateNow(); }, this.options.delay); }; /** * Activate autocompleter immediately */ $.dnnAutocompleter.prototype.activateNow = function () { var value = this.beforeUseConverter(this.dom.$elem.val()); if (value !== this.lastProcessedValue_ && value !== this.lastSelectedValue_) { this.fetchData(value); } }; /** * Get autocomplete data for a given value * @param {string} value Value to base autocompletion on * @private */ $.dnnAutocompleter.prototype.fetchData = function (value) { var self = this; var processResults = function (results, filter) { if (self.options.processData) { results = self.options.processData(results); } self.showResults(self.filterResults(results, filter), filter); }; this.lastProcessedValue_ = value; if (value.length < this.options.minChars) { processResults([], value); } else if (this.options.data) { processResults(this.options.data, value); } else { this.fetchRemoteData(value, function (remoteData) { processResults(remoteData, value); }); } }; /** * Get remote autocomplete data for a given value * @param {string} filter The filter to base remote data on * @param {function} callback The function to call after data retrieval * @private */ $.dnnAutocompleter.prototype.fetchRemoteData = function (filter, callback) { var data = this.cacheRead(filter); if (data) { callback(data); } else { var self = this; var ajaxCallback = function (data) { var parsed = false; if (data !== false) { parsed = self.parseRemoteData(data); self.cacheWrite(filter, parsed); } self.dom.$elem.removeClass(self.options.loadingClass); callback(parsed); }; this.dom.$elem.addClass(this.options.loadingClass); var services = self.options.moduleId ? $.dnnSF(self.options.moduleId) : null; $.ajax({ url: this.makeUrl(filter), beforeSend: services ? services.setModuleHeaders : null, success: ajaxCallback, error: function (jqXHR, textStatus, errorThrown) { if ($.isFunction(self.options.onError)) { self.options.onError(jqXHR, textStatus, errorThrown); } else { ajaxCallback(false); } }, type: 'GET', dataType: 'json', contentType: "application/json" }); } }; /** * Create or update an extra parameter for the remote request * @param {string} name Parameter name * @param {string} value Parameter value * @public */ $.dnnAutocompleter.prototype.setExtraParam = function (name, value) { var index = $.trim(String(name)); if (index) { if (!this.options.extraParams) { this.options.extraParams = {}; } if (this.options.extraParams[index] !== value) { this.options.extraParams[index] = value; this.cacheFlush(); } } }; /** * Build the url for a remote request * If options.queryParamName === false, append query to url instead of using a GET parameter * @param {string} param The value parameter to pass to the backend * @returns {string} The finished url with parameters */ $.dnnAutocompleter.prototype.makeUrl = function (param) { var self = this; var url = this.options.url; var params = {}; params[this.options.queryParamName] = param; return makeUrl(url, params); }; /** * Parse data received from server * @param remoteData Data received from remote server * @returns {array} Parsed data */ $.dnnAutocompleter.prototype.parseRemoteData = function (remoteData) { var data = remoteData; if (typeof data['d'] != 'undefined') { data = $.parseJSON(data['d']); } return data; }; /** * Filter result * @param {Object} result * @param {String} filter * @returns {boolean} Include this result * @private */ $.dnnAutocompleter.prototype.filterResult = function (result, filter) { if (!result.value) { return false; } if (this.options.filterResults) { var pattern = this.matchStringConverter(filter); var testValue = this.matchStringConverter(result.value); if (!this.options.matchCase) { pattern = pattern.toLowerCase(); testValue = testValue.toLowerCase(); } var patternIndex = testValue.indexOf(pattern); if (this.options.matchInside) { return patternIndex > -1; } else { return patternIndex === 0; } } return true; }; /** * Filter results * @param results * @param filter */ $.dnnAutocompleter.prototype.filterResults = function (results, filter) { var filtered = []; var i, result; for (i = 0; i < results.length; i++) { result = sanitizeResult(results[i]); if (this.filterResult(result, filter)) { filtered.push(result); } } if (this.options.sortResults) { filtered = this.sortResults(filtered, filter); } if (this.options.maxItemsToShow > 0 && this.options.maxItemsToShow < filtered.length) { filtered.length = this.options.maxItemsToShow; } return filtered; }; /** * Sort results * @param results * @param filter */ $.dnnAutocompleter.prototype.sortResults = function (results, filter) { var self = this; var sortFunction = this.options.sortFunction; if (!$.isFunction(sortFunction)) { sortFunction = function (a, b, f) { return sortValueAlpha(a, b, self.options.matchCase); }; } results.sort(function (a, b) { return sortFunction(a, b, filter, self.options); }); return results; }; /** * Convert string before matching * @param s * @param a * @param b */ $.dnnAutocompleter.prototype.matchStringConverter = function (s, a, b) { var converter = this.options.matchStringConverter; if ($.isFunction(converter)) { s = converter(s, a, b); } return s; }; /** * Convert string before use * @param s * @param a * @param b */ $.dnnAutocompleter.prototype.beforeUseConverter = function (s, a, b) { s = this.getValue(); var converter = this.options.beforeUseConverter; if ($.isFunction(converter)) { s = converter(s, a, b); } return s; }; /** * Enable finish on blur event */ $.dnnAutocompleter.prototype.enableFinishOnBlur = function () { this.finishOnBlur_ = true; }; /** * Disable finish on blur event */ $.dnnAutocompleter.prototype.disableFinishOnBlur = function () { this.finishOnBlur_ = false; }; /** * Create a results item (LI element) from a result * @param result */ $.dnnAutocompleter.prototype.createItemFromResult = function (result) { var self = this; var $li = $('
  • ' + this.showResult(result.value, result.data) + '
  • '); $li.data({ value: result.value, data: result.data }) .click(function () { self.selectItem($li); }) .mousedown(self.disableFinishOnBlur) .mouseup(self.enableFinishOnBlur) ; return $li; }; /** * Get all items from the results list * @param result */ $.dnnAutocompleter.prototype.getItems = function () { return $('>ul>li', this.dom.$results); }; /** * Show all results * @param results * @param filter */ $.dnnAutocompleter.prototype.showResults = function (results, filter) { var numResults = results.length; var self = this; var $ul = $(''); var i, result, $li, autoWidth, first = false, $first = false; if (numResults) { for (i = 0; i < numResults; i++) { result = results[i]; $li = this.createItemFromResult(result); $ul.append($li); if (first === false) { first = String(result.value); $first = $li; $li.addClass(this.options.firstItemClass); } if (i === numResults - 1) { $li.addClass(this.options.lastItemClass); } } this.dom.$results.html($ul).show(); // Always recalculate position since window size or // input element location may have changed. this.position(); if (this.options.autoWidth) { autoWidth = this.dom.$elem.outerWidth() - this.dom.$results.outerWidth() + this.dom.$results.width(); this.dom.$results.css(this.options.autoWidth, autoWidth); } this.getItems().hover( function () { self.focusItem(this); }, function () { /* void */ } ); if (this.autoFill(first, filter) || this.options.selectFirst || (this.options.selectOnly && numResults === 1)) { this.focusItem($first); } this.active_ = true; } else { this.hideResults(); this.active_ = false; } }; $.dnnAutocompleter.prototype.showResult = function (value, data) { if ($.isFunction(this.options.showResult)) { return this.options.showResult(value, data); } else { return value; } }; $.dnnAutocompleter.prototype.autoFill = function (value, filter) { var lcValue, lcFilter, valueLength, filterLength; if (this.options.autoFill && this.lastKeyPressed_ !== 8) { lcValue = String(value).toLowerCase(); lcFilter = String(filter).toLowerCase(); valueLength = value.length; filterLength = filter.length; if (lcValue.substr(0, filterLength) === lcFilter) { var d = this.getDelimiterOffsets(); var pad = d.start ? ' ' : ''; // if there is a preceding delimiter this.setValue(pad + value); var start = filterLength + d.start + pad.length; var end = valueLength + d.start + pad.length; this.selectRange(start, end); return true; } } return false; }; $.dnnAutocompleter.prototype.focusNext = function () { this.focusMove(+1); }; $.dnnAutocompleter.prototype.focusPrev = function () { this.focusMove(-1); }; $.dnnAutocompleter.prototype.focusMove = function (modifier) { var $items = this.getItems(); modifier = sanitizeInteger(modifier, 0); if (modifier) { for (var i = 0; i < $items.length; i++) { if ($($items[i]).hasClass(this.selectClass_)) { this.focusItem(i + modifier); return; } } } this.focusItem(0); }; $.dnnAutocompleter.prototype.focusItem = function (item) { var $item, $items = this.getItems(); if ($items.length) { $items.removeClass(this.selectClass_).removeClass(this.options.selectClass); if (typeof item === 'number') { if (item < 0) { item = 0; } else if (item >= $items.length) { item = $items.length - 1; } $item = $($items[item]); } else { $item = $(item); } if ($item) { $item.addClass(this.selectClass_).addClass(this.options.selectClass); } } }; $.dnnAutocompleter.prototype.selectCurrent = function () { var $item = $('li.' + this.selectClass_, this.dom.$results); if ($item.length === 1) { this.selectItem($item); } else { this.deactivate(false); } }; $.dnnAutocompleter.prototype.selectItem = function ($li) { var value = $li.data('value'); var data = $li.data('data'); var displayValue = this.displayValue(value, data); var processedDisplayValue = this.beforeUseConverter(displayValue); this.lastProcessedValue_ = processedDisplayValue; this.lastSelectedValue_ = processedDisplayValue; var d = this.getDelimiterOffsets(); var delimiter = this.options.delimiterChar; var elem = this.dom.$elem; var extraCaretPos = 0; if (this.options.useDelimiter) { // if there is a preceding delimiter, add a space after the delimiter if (elem.val().substring(d.start - 1, d.start) == delimiter && delimiter != ' ') { displayValue = ' ' + displayValue; } // if there is not already a delimiter trailing this value, add it if (elem.val().substring(d.end, d.end + 1) != delimiter && this.lastKeyPressed_ != this.options.delimiterKeyCode) { displayValue = displayValue + delimiter; } else { // move the cursor after the existing trailing delimiter extraCaretPos = 1; } } this.setValue(displayValue); this.setCaret(d.start + displayValue.length + extraCaretPos); this.callHook('onItemSelect', { value: value, data: data }); this.deactivate(true); elem.trigger('result', value); }; $.dnnAutocompleter.prototype.displayValue = function (value, data) { if ($.isFunction(this.options.displayValue)) { return this.options.displayValue(value, data); } return value; }; $.dnnAutocompleter.prototype.hideResults = function () { this.dom.$results.hide(); }; $.dnnAutocompleter.prototype.deactivate = function (finish) { if (this.finishTimeout_) { clearTimeout(this.finishTimeout_); } if (this.keyTimeout_) { clearTimeout(this.keyTimeout_); } if (finish) { if (this.lastProcessedValue_ !== this.lastSelectedValue_) { if (this.options.mustMatch) { this.setValue(''); } this.callHook('onNoMatch'); } if (this.active_) { this.callHook('onFinish'); } this.lastKeyPressed_ = null; this.lastProcessedValue_ = null; this.lastSelectedValue_ = null; this.active_ = false; } this.hideResults(); }; $.dnnAutocompleter.prototype.selectRange = function (start, end) { var input = this.dom.$elem.get(0); if (input.setSelectionRange) { input.focus(); input.setSelectionRange(start, end); } else if (input.createTextRange) { var range = input.createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', start); range.select(); } }; /** * Move caret to position * @param {Number} pos */ $.dnnAutocompleter.prototype.setCaret = function (pos) { this.selectRange(pos, pos); }; /** * Get caret position */ $.dnnAutocompleter.prototype.getCaret = function () { var elem = this.dom.$elem; if ($.browser.msie) { // ie var selection = document.selection; if (elem[0].tagName.toLowerCase() != 'textarea') { var val = elem.val(); var range = selection.createRange().duplicate(); range.moveEnd('character', val.length); var s = (range.text == '' ? val.length : val.lastIndexOf(range.text)); range = selection.createRange().duplicate(); range.moveStart('character', -val.length); var e = range.text.length; } else { var range = selection.createRange(); var stored_range = range.duplicate(); stored_range.moveToElementText(elem[0]); stored_range.setEndPoint('EndToEnd', range); var s = stored_range.text.length - range.text.length; var e = s + range.text.length; } } else { // ff, chrome, safari var s = elem[0].selectionStart; var e = elem[0].selectionEnd; } return { start: s, end: e }; }; /** * Set the value that is currently being autocompleted * @param {String} value */ $.dnnAutocompleter.prototype.setValue = function (value) { if (this.options.useDelimiter) { // set the substring between the current delimiters var val = this.dom.$elem.val(); var d = this.getDelimiterOffsets(); var preVal = val.substring(0, d.start); var postVal = val.substring(d.end); value = preVal + value + postVal; } this.dom.$elem.val(value).blur(); }; /** * Get the value currently being autocompleted * @param {String} value */ $.dnnAutocompleter.prototype.getValue = function () { var val = this.dom.$elem.val(); if (this.options.useDelimiter) { var d = this.getDelimiterOffsets(); return val.substring(d.start, d.end).trim(); } else { return val; } }; /** * Get the offsets of the value currently being autocompleted */ $.dnnAutocompleter.prototype.getDelimiterOffsets = function () { var val = this.dom.$elem.val(); if (this.options.useDelimiter) { var preCaretVal = val.substring(0, this.getCaret().start); var start = preCaretVal.lastIndexOf(this.options.delimiterChar) + 1; var postCaretVal = val.substring(this.getCaret().start); var end = postCaretVal.indexOf(this.options.delimiterChar); if (end == -1) end = val.length; end += this.getCaret().start; } else { start = 0; end = val.length; } return { start: start, end: end }; }; })(jQuery); (function ($) { // dnn customized tags var delimiter = new Array(); var tags_callbacks = new Array(); $.fn.dnnDoAutosize = function (o) { var minWidth = $(this).data('minwidth'), maxWidth = $(this).data('maxwidth'), val = '', input = $(this), testSubject = $('#' + $(this).data('tester_id')); if (val === (val = input.val())) { return; } // Enter new content into testSubject var escaped = val.replace(/&/g, '&').replace(/\s/g, ' ').replace(//g, '>'); testSubject.html(escaped); // Calculate new width + whether to change var testerWidth = testSubject.width(), newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, currentWidth = input.width(), isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth) || (newWidth > minWidth && newWidth < maxWidth); // Animate width if (isValidWidthChange) { input.width(newWidth); } }; $.fn.dnnResetAutosize = function (options) { // alert(JSON.stringify(options)); var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(), maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.dnnTagsInput').width() - options.inputPadding), val = '', input = $(this), testSubject = $('').css({ position: 'absolute', top: -9999, left: -9999, width: 'auto', fontSize: input.css('fontSize'), fontFamily: input.css('fontFamily'), fontWeight: input.css('fontWeight'), letterSpacing: input.css('letterSpacing'), whiteSpace: 'nowrap' }), testerId = $(this).attr('id') + '_autosize_tester'; if (!$('#' + testerId).length > 0) { testSubject.attr('id', testerId); testSubject.appendTo('body'); } input.data('minwidth', minWidth); input.data('maxwidth', maxWidth); input.data('tester_id', testerId); input.css('width', minWidth); }; $.fn.dnnAddTag = function (value, options) { options = jQuery.extend({ focus: false, callback: true }, options); this.each(function () { var id = $(this).attr('id'); var tagslist = $(this).val().split(delimiter[id]); if (tagslist[0] == '') { tagslist = new Array(); } value = jQuery.trim(value); if (options.unique) { var skipTag = $(this).dnnTagExist(value); if (skipTag == true) { //Marks fake input as not_valid to let styling it $('#' + id + '_tag').addClass('dnnTagsInvalid'); } } else { var skipTag = false; } if (value != '' && skipTag != true) { $('').addClass('tag').append( $('').text(value).append('  '), $('', { href: '#', title: 'Removing tag' }).click(function () { return $('#' + id).dnnRemoveTag(escape(value)); }) ).insertBefore('#' + id + '_addTag'); tagslist.push(value); $('#' + id + '_tag').val(''); if (options.focus) { $('#' + id + '_tag').focus(); } else { $('#' + id + '_tag').blur(); } $.fn.dnnTagsInput.updateTagsField(this, tagslist); if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) { var f = tags_callbacks[id]['onAddTag']; f.call(this, value); } if (tags_callbacks[id] && tags_callbacks[id]['onChange']) { var i = tagslist.length; var f = tags_callbacks[id]['onChange']; f.call(this, $(this), tagslist[i - 1]); } } }); return false; }; $.fn.dnnRemoveTag = function (value) { value = unescape(value); this.each(function () { var id = $(this).attr('id'); var old = $(this).val().split(delimiter[id]); $('#' + id + '_tagsinput .tag').remove(); str = ''; for (i = 0; i < old.length; i++) { if (old[i] != value) { str = str + delimiter[id] + old[i]; } } $.fn.dnnTagsInput.importTags(this, str); if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) { var f = tags_callbacks[id]['onRemoveTag']; f.call(this, value); } }); return false; }; $.fn.dnnTagExist = function (val) { var id = $(this).attr('id'); var tagslist = $(this).val().split(delimiter[id]); return (jQuery.inArray(val, tagslist) >= 0); //true when tag exists, false when not }; // clear all existing tags and import new ones from a string $.fn.dnnImportTags = function (str) { id = $(this).attr('id'); $('#' + id + '_tagsinput .tag').remove(); $.fn.dnnTagsInput.importTags(this, str); }; $.fn.dnnTagsInput = function (options) { var settings = jQuery.extend({ interactive: true, defaultText: 'add a tag', minChars: 0, maxChars: 50, maxTags: 16, width: '45%', height: '28px', autocomplete: { selectFirst: false }, 'hide': true, 'delimiter': ',', 'unique': true, removeWithBackspace: true, placeholderColor: '#666666', autosize: true, comfortZone: 20, inputPadding: 6 * 2 }, options); this.each(function () { if (settings.hide) { $(this).hide(); } var id = $(this).attr('id'); if (!id || delimiter[$(this).attr('id')]) { id = $(this).attr('id', 'tags' + new Date().getTime()).attr('id'); } var data = jQuery.extend({ pid: id, real_input: '#' + id, holder: '#' + id + '_tagsinput', input_wrapper: '#' + id + '_addTag', fake_input: '#' + id + '_tag' }, settings); delimiter[id] = data.delimiter; if (settings.onAddTag || settings.onRemoveTag || settings.onChange) { tags_callbacks[id] = new Array(); tags_callbacks[id]['onAddTag'] = settings.onAddTag; tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag; tags_callbacks[id]['onChange'] = settings.onChange; } var markup = '
    '; if (settings.interactive) { markup = markup + ''; } markup = markup + '
    '; $(markup).insertAfter(this); $(data.holder).css({ 'width': settings.width, 'min-height': settings.height, 'height': '100%', 'overflow': 'hidden' }); if ($(data.real_input).val() != '') { $.fn.dnnTagsInput.importTags($(data.real_input), $(data.real_input).val()); } if (settings.interactive) { $(data.fake_input).val($(data.fake_input).attr('data-default')); $(data.fake_input).css('color', settings.placeholderColor); $(data.fake_input).dnnResetAutosize(settings); $(data.holder).bind('click', data, function (event) { $(event.data.real_input).triggerHandler('focus'); $(event.data.fake_input).triggerHandler('focus'); }); $(data.fake_input).bind('focus', data, function (event) { if ($(event.data.fake_input).val() == $(event.data.fake_input).attr('data-default')) { $(event.data.fake_input).val(''); } $(event.data.fake_input).css('color', '#000000'); }); if (settings.autocomplete_url != undefined) { autocomplete_options = { source: settings.autocomplete_url }; for (attrname in settings.autocomplete) { autocomplete_options[attrname] = settings.autocomplete[attrname]; } if ($.dnnAutocompleter !== undefined) { $(data.fake_input).dnnAutocomplete(settings.autocomplete_url, settings.autocomplete); $(data.fake_input).bind('result', data, function (event, data, formatted) { if (data) { $('#' + id).dnnAddTag(data, { focus: true, unique: (settings.unique) }); } }); } } else { // if a user tabs out of the field, create a new tag // this is only available if autocomplete is not used. $(data.fake_input).bind('blur', data, function (event) { var d = $(this).attr('data-default'); var tagslist = $(event.data.real_input).val().split(delimiter[id]); if (tagslist[0] == '') { tagslist = new Array(); } if ($(event.data.fake_input).val() != '' && $(event.data.fake_input).val() != d) { if ((event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) && (event.data.maxTags > tagslist.length)) $(event.data.real_input).dnnAddTag($(event.data.fake_input).val(), { focus: true, unique: (settings.unique) }); else $(this).val(''); } else { $(event.data.fake_input).val($(event.data.fake_input).attr('data-default')); $(event.data.fake_input).css('color', settings.placeholderColor); } return false; }); } // if user types a comma, create a new tag $(data.fake_input).bind('keypress', data, function (event) { if (event.which == event.data.delimiter.charCodeAt(0) || event.which == 13) { event.preventDefault(); var tagslist = $(event.data.real_input).val().split(delimiter[id]); if (tagslist[0] == '') { tagslist = new Array(); } if ((event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) && (event.data.maxTags > tagslist.length)) $(event.data.real_input).dnnAddTag($(event.data.fake_input).val(), { focus: true, unique: (settings.unique) }); else $(this).val(''); $(event.data.fake_input).dnnResetAutosize(settings); return false; } else if (event.data.autosize) { $(event.data.fake_input).dnnDoAutosize(settings); } }); //Delete last tag on backspace data.removeWithBackspace && $(data.fake_input).bind('keydown', function (event) { if (event.keyCode == 8 && $(this).val() == '') { event.preventDefault(); var last_tag = $(this).closest('.dnnTagsInput').find('.tag:last').text(); var id = $(this).attr('id').replace(/_tag$/, ''); last_tag = last_tag.replace(/[\s]+$/, ''); $('#' + id).dnnRemoveTag(escape(last_tag)); $(this).trigger('focus'); } }); $(data.fake_input).blur(); //Removes the not_valid class when user changes the value of the fake input if (data.unique) { $(data.fake_input).keydown(function (event) { if (event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[谩茅铆贸煤脕脡脥脫脷帽脩,/]+/)) { $(this).removeClass('dnnTagsInvalid'); } }); } } // if settings.interactive }); return this; }; $.fn.dnnTagsInput.updateTagsField = function (obj, tagslist) { var id = $(obj).attr('id'); $(obj).val(tagslist.join(delimiter[id])); }; $.fn.dnnTagsInput.importTags = function (obj, val) { $(obj).val(''); var id = $(obj).attr('id'); var tags = val.split(delimiter[id]); for (i = 0; i < tags.length; i++) { $(obj).dnnAddTag(tags[i], { focus: false, callback: false }); } if (tags_callbacks[id] && tags_callbacks[id]['onChange']) { var f = tags_callbacks[id]['onChange']; f.call(obj, obj, tags[i]); } }; })(jQuery); (function ($) { // dnnForm customised client side validation $.fn.toggleErrorMessage = function (options) { var defaultOptions = { errorMessage: "Error message", errorCls: "dnnFormError", show: true, removeErrorMessage: true }; options = $.extend(defaultOptions, options); return this.each(function () { var dnnFormItem = $(this).closest('.dnnFormItem'); if (options.show) { var errorSpan = dnnFormItem.find('span.dnnFormMessage.' + options.errorCls); if (errorSpan.length) { errorSpan.html(options.errorMessage); } else { errorSpan = $('' + options.errorMessage + ''); dnnFormItem.append(errorSpan); } if (this.tagName.toLowerCase() == 'div') { // customised controls if ($(this).hasClass('RadComboBox')) { // RadComboBox $(this).addClass('dnnError'); } } else { if ($(this).parent().hasClass('RadPicker')) { // RadDate Picker $(this).parent().find('input.riTextBox').css('border', '1px solid red'); } else if ($(this).hasClass('dnnSpinnerInput')) { // Spinner $(this).parent().css('border', '1px solid red'); } else { // normal ctrl $(this).css('border', '1px solid red'); } } } else { if (options.removeErrorMessage) dnnFormItem.find('span.' + options.errorCls).remove(); else dnnFormItem.find('span.' + options.errorCls).hide(); if (this.tagName.toLowerCase() == 'div') { // customised controls if ($(this).hasClass('RadComboBox')) { // RadComboBox $(this).removeClass('dnnError'); } } else { if ($(this).hasClass('dnnSpinnerInput')) { // Spinner $(this).parent().css('border', '1px solid #ccc'); } else { // normal ctrl $(this).css('border', '1px solid #ccc'); } } } }); }; $.fn.dnnFormSubmit = function (options) { var defaultOptions = { validates: [] }; options = $.extend(defaultOptions, options); return this.each(function () { $(this).click(function () { var formValidate = true; for (var i = 0; i < options.validates.length; i++) { var ele = $('#' + options.validates[i].ele); var func = options.validates[i].func; if (ele.length) { var eleVal = ele.val(); var eleError = func.call(ele.get(0), eleVal); if (eleError) { ele.toggleErrorMessage({ errorMessage: eleError, show: true }); formValidate = false; } else { ele.toggleErrorMessage({ show: false }); } //ele focus then remove error info var hideErrorInfo = function () { $(this).toggleErrorMessage({ show: false }); }; ele.unbind('focus', hideErrorInfo).bind('focus', hideErrorInfo); } } return formValidate; }); }); }; // fix telerik custom datepicker popup arrow position $.dnnRadPickerHack = function (sender) { //when click this icon, hide error info var hideErrorInfo = function () { $(this).toggleErrorMessage({ show: false, removeErrorMessage: false }); }; var dnnRadPickerPopupFix = function () { if (!$.browser.msie || $.browser.version >= 9) { var id = $(this).attr('id'); var popupId = id.replace('popupButton', 'calendar_wrapper'); var popupElement = $('#' + popupId); var wrapperId = id.replace('popupButton', 'wrapper'); var wrapperElement = $('#' + wrapperId); var popupElementTop = popupElement.parent().position().top; var wrapperElementTop = wrapperElement.offset().top; var popupTbl = popupElement.find('.RadCalendar_Default'); var nextEle = popupTbl.next(); if (nextEle.hasClass('RadCalendar_Default_PopupArrow_Down') || nextEle.hasClass('RadCalendar_Default_PopupArrow_Up')) nextEle.remove(); if (popupElementTop < wrapperElementTop) { // popup above the calendar ctrl, so show arrow down popupTbl.after('
    '); } else { // popup below the calendar ctrl, so show arrow up popupTbl.after('
    '); } } $(this).toggleErrorMessage({ show: false }); }; $('.RadPicker_Default a.rcCalPopup').unbind('click', dnnRadPickerPopupFix).bind('click', dnnRadPickerPopupFix); $('.RadPicker_Default .riTextBox').unbind('focus', hideErrorInfo).bind('focus', hideErrorInfo); }; // remove combobox inline style $.dnnComboBoxLoaded = function (sender) { if (sender.constructor.__typeName == "Telerik.Web.UI.RadComboBox") { $(sender._inputDomElement).closest(".RadComboBox").removeAttr("style"); } }; //fix combobox hide error info $.dnnComboBoxHack = function (sender) { $(('#' + sender._clientStateFieldID).replace('_ClientState', '')).toggleErrorMessage({ show: false, removeErrorMessage: false }); }; //fix combobox scroll $.dnnComboBoxScroll = function (sender) { if (!$.browser.msie || $.browser.version >= 9) { $(('#' + sender._clientStateFieldID + ' .rcbScroll').replace('ClientState', 'DropDown')).jScrollPane(); } }; // fix grid issues $.dnnGridCreated = function (sender) { var clientID = sender.ClientID; var $grid = $('#' + clientID); $('input.rgSortDesc, input.rgSortAsc', $grid).click(function () { var href = $(this).parent().find('a').get(0).href; window.location = href; return false; }); if ($grid.hasClass('dnnTooltipGrid')) { $grid.dnnHelperTipDestroy(); $('.rgRow, .rgAltRow', $grid).each(function () { var info = "Here is some text will show up and explian more about this information"; $(this).dnnHelperTip({ helpContent: info, holderId: clientID }); }); } var grid = $find(clientID); // fix a bug while using customised checkbox - remove onclick event then attach onchange var headerCheck = $('.rgCheck', $grid); if (headerCheck.length) { headerCheck.each(function () { var checkbox = $(this).find('input[type="checkbox"]').get(0); var onclick = checkbox.onclick; checkbox.onchange = onclick; checkbox.onclick = null; }); // fix when use customised scrollbar and customised checkbox, the row cannot be correctly selected $('.rgDataDiv input[type="checkbox"]', $grid).change(function () { var masterTable = grid.get_masterTableView(); var rowIndex = $(this).closest('tr').get(0).rowIndex; var checked = this.checked; if (checked) masterTable.selectItem(rowIndex); else masterTable.deselectItem(rowIndex); }); } // initialize dnnGrid customised scrollbar $('.rgDataDiv').each(function () { var $this = $(this); var ele = $this.get(0); ele.scrollPane = $this.jScrollPane(); var api = ele.scrollPane.data('jsp'); var throttleTimeout; $(window).bind( 'resize', function () { if ($.browser.msie) { // IE fires multiple resize events while you are dragging the browser window which // causes it to crash if you try to update the scrollpane on every one. So we need // to throttle it to fire a maximum of once every 50 milliseconds... if (!throttleTimeout) { throttleTimeout = setTimeout( function () { api.reinitialise(); throttleTimeout = null; }, 50 ); } } else { api.reinitialise(); } } ); if (window.__rgDataDivScrollTopPersistArray && window.__rgDataDivScrollTopPersistArray.length) { var y = window.__rgDataDivScrollTopPersistArray.pop(); api.scrollToY(y); } }); }; })(jQuery); (function ($) { var types = ['DOMMouseScroll', 'mousewheel']; if ($.event.fixHooks) { for (var i = types.length; i;) { $.event.fixHooks[types[--i]] = $.event.mouseHooks; } } $.event.special.mousewheel = { setup: function () { if (this.addEventListener) { for (var i = types.length; i;) { this.addEventListener(types[--i], handler, false); } } else { this.onmousewheel = handler; } }, teardown: function () { if (this.removeEventListener) { for (var i = types.length; i;) { this.removeEventListener(types[--i], handler, false); } } else { this.onmousewheel = null; } } }; $.fn.extend({ mousewheel: function (fn) { return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); }, unmousewheel: function (fn) { return this.unbind("mousewheel", fn); } }); function handler(event) { var orgEvent = event || window.event, args = [].slice.call(arguments, 1), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; event = $.event.fix(orgEvent); event.type = "mousewheel"; // Old school scrollwheel delta if (orgEvent.wheelDelta) { delta = orgEvent.wheelDelta / 120; } if (orgEvent.detail) { delta = -orgEvent.detail / 3; } // New school multidimensional scroll (touchpads) deltas deltaY = delta; // Gecko if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) { deltaY = 0; deltaX = -1 * delta; } // Webkit if (orgEvent.wheelDeltaY !== undefined) { deltaY = orgEvent.wheelDeltaY / 120; } if (orgEvent.wheelDeltaX !== undefined) { deltaX = -1 * orgEvent.wheelDeltaX / 120; } // Add event and delta to the front of the arguments args.unshift(event, delta, deltaX, deltaY); return ($.event.dispatch || $.event.handle).apply(this, args); } })(jQuery); (function ($) { $.fn.dnnFileInput = function () { return this.each(function () { var $ctrl = $(this); if (this.wrapper) return; //if this.wrapper is undefined, then we check if parent node is a wrapper if (this.parentNode && this.parentNode.tagName.toLowerCase() == 'span' && this.parentNode.className == 'dnnInputFileWrapper') { return; } this.wrapper = $(""); $ctrl.wrap(this.wrapper); var text = $ctrl.data('text'); text = text || '閫夋嫨鏂囦欢'; var btn = $("" + text + ""); btn.insertBefore($ctrl); $ctrl.change(function () { var val = $(this).val(); if (val != '') { var lastIdx = val.lastIndexOf('\\') + 1; val = val.substring(lastIdx, val.length); } else { val = text; } $(this).prev().html(val); }); }); }; })(jQuery); (function ($) { var supportAjaxUpload = function () { var xhr = new XMLHttpRequest; return !!(xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)); }; $.fn.dnnFileUpload = function (settings) { return this.each(function () { // set scope and settings, service var scope = $(this).attr('id'); dnn.dnnFileUpload.setSettings(scope, settings); var service = $.dnnSF(); // hide progress $('#' + settings.progressBarId).parent().hide(); // detect draggable support or not var droppableSpan = $('#' + settings.dropZoneId + '>span'); if ('draggable' in document.createElement('span')) { droppableSpan.show(); } else { droppableSpan.hide(); } // set file upload var url = service.getServiceRoot('internalservices') + 'fileupload/postfile'; if (!supportAjaxUpload()) { var antiForgeryToken = $('input[name="__RequestVerificationToken"]').val(); url += '?__RequestVerificationToken=' + antiForgeryToken; } $('#' + scope + ' input[type="file"]').fileupload({ url: url, beforeSend: service.setModuleHeaders, dropZone: $('#' + settings.dropZoneId), replaceFileInput: false, submit: function (e, data) { data.formData = { folder: settings.folder, filter: settings.fileFilter }; return true; }, progressall: function (e, data) { var progress = parseInt(data.loaded / data.total * 100, 10); if (progress < 100) { $('#' + settings.progressBarId).parent().show(); $('#' + settings.progressBarId + '>div').css('width', progress + '%'); } else $('#' + settings.progressBarId).parent().hide(); }, done: function (e, data) { $('#' + settings.progressBarId).parent().hide(); var img = new Image(); $(img).load(function () { $('#' + settings.dropZoneId + ' img').remove(); $(img).css({ 'max-width': 180, 'max-height': 150 }).insertBefore($('#' + settings.dropZoneId + ' span')); }); var src; var testContent = $('pre', data.result); if (testContent.length) { src = testContent.text(); } else src = data.result; if (src && $.trim(src)) { img.src = src; // set updated files into combo var foldersCombo = $find(settings.foldersComboId); var folderPath = foldersCombo.get_value(); url = service.getServiceRoot('internalservices') + 'fileupload/loadfiles'; $.ajax({ url: url, type: 'POST', data: { FolderPath: folderPath, FileFilter: settings.fileFilter, Required: false }, beforeSend: service.setModuleHeaders, success: function (d) { var combo = $find(settings.filesComboId); combo.clearItems(); for (var i = 0; i < d.length; i++) { var txt = d[i].Text; var val = d[i].Value; var comboItem = new Telerik.Web.UI.RadComboBoxItem(); comboItem.set_text(txt); comboItem.set_value(val); combo.get_items().add(comboItem); if (src.indexOf(txt) > 0) { comboItem.select(); $('#' + settings.fileIdId).val(val); var path = folderPath ? folderPath + '/' + txt : txt; $('#' + settings.filePathId).val(path); } } }, error: function () { } }); } } }); // set initial thumb image setTimeout(function () { var filesCombo = $find(settings.filesComboId); var selectedFileId = filesCombo.get_value(); url = service.getServiceRoot('internalservices') + 'fileupload/loadimage'; if (selectedFileId) { $.ajax({ url: url, type: 'GET', data: { fileId: selectedFileId }, success: function (d) { var img = new Image(); $(img).load(function () { $('#' + settings.dropZoneId + ' img').remove(); $(img).css({ 'max-width': 180, 'max-height': 150 }).insertBefore($('#' + settings.dropZoneId + ' span')); }); img.src = d; }, error: function () { } }); } }, 500); }); }; if (typeof dnn === 'undefined') dnn = {}; dnn.dnnFileUpload = dnn.dnnFileUpload || {}; dnn.dnnFileUpload.settings = {}; dnn.dnnFileUpload.setSettings = function (scope, settings) { dnn.dnnFileUpload.settings[scope] = settings; }; dnn.dnnFileUpload.getSettings = function (sender) { var senderId = sender.get_id(); var scope = $('#' + senderId).closest('.dnnFileUploadScope').attr('id'); return dnn.dnnFileUpload.settings[scope]; }; dnn.dnnFileUpload.Folders_Changed = function (sender, e) { var settings = dnn.dnnFileUpload.getSettings(sender); if (!settings) return; var item = e.get_item(); if (item) { var folderPath = item.get_value(); settings.folder = folderPath; var service = $.dnnSF(); var url = service.getServiceRoot('internalservices') + 'fileupload/loadfiles'; $.ajax({ url: url, type: 'POST', data: { FolderPath: folderPath, FileFilter: settings.fileFilter, Required: false }, beforeSend: service.setModuleHeaders, success: function (d) { var combo = $find(settings.filesComboId); combo.clearItems(); for (var i = 0; i < d.length; i++) { var txt = d[i].Text; var val = d[i].Value; var comboItem = new Telerik.Web.UI.RadComboBoxItem(); comboItem.set_text(txt); comboItem.set_value(val); combo.get_items().add(comboItem); if (i == 0) { comboItem.select(); } } }, error: function () { } }); } }; dnn.dnnFileUpload.Files_Changed = function (sender, e) { var settings = dnn.dnnFileUpload.getSettings(sender); if (!settings) return; var item = e.get_item(); if (item) { var fileId = item.get_value(); $('#' + settings.fileIdId).val(fileId); var fileName = item.get_text(); var folderPath = settings.folder; var path = folderPath ? folderPath + '/' + fileName : fileName; $('#' + settings.filePathId).val(path); var service = $.dnnSF(); var url = service.getServiceRoot('internalservices') + 'fileupload/loadimage'; if (fileId) { $.ajax({ url: url, type: 'GET', data: { fileId: fileId }, success: function (d) { var img = new Image(); $(img).load(function () { $('#' + settings.dropZoneId + ' img').remove(); $(img).css({ 'max-width': 180, 'max-height': 150 }).insertBefore($('#' + settings.dropZoneId + ' span')); }); img.src = d; }, error: function () { } }); } else $('#' + settings.dropZoneId + ' img').remove(); } }; })(jQuery); (function ($) { $.detectScroll = function () { var divs = document.getElementsByTagName('div'); return $.grep(divs, function (n) { return $(n).hasClass('dnnScroll'); }); }; /* BELOW jscrollPane code */ $.fn.jScrollPane = function (settings) { // JScrollPane "class" - public methods are available through $('selector').data('jsp') function JScrollPane(elem, s) { var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY, verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition, verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown, horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight, reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth, wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false, originalElement = elem.clone(false, false).empty(), mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp'; originalPadding = elem.css('paddingTop') + ' ' + elem.css('paddingRight') + ' ' + elem.css('paddingBottom') + ' ' + elem.css('paddingLeft'); originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) + (parseInt(elem.css('paddingRight'), 10) || 0); function initialise(s) { var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY, hasContainingSpaceChanged, originalScrollTop, originalScrollLeft, maintainAtBottom = false, maintainAtRight = false; settings = s; if (pane === undefined) { originalScrollTop = elem.scrollTop(); originalScrollLeft = elem.scrollLeft(); elem.css( { overflow: 'hidden', padding: 0 } ); // TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should // come back to it later and check once it is unhidden... paneWidth = elem.innerWidth() + originalPaddingTotalWidth; paneHeight = elem.innerHeight(); elem.width(paneWidth); pane = $('
    ').css('padding', originalPadding).append(elem.children()); container = $('
    ') .css({ 'width': paneWidth + 'px', 'height': paneHeight + 'px' } ).append(pane).appendTo(elem); /* // Move any margins from the first and last children up to the container so they can still // collapse with neighbouring elements as they would before jScrollPane firstChild = pane.find(':first-child'); lastChild = pane.find(':last-child'); elem.css( { 'margin-top': firstChild.css('margin-top'), 'margin-bottom': lastChild.css('margin-bottom') } ); firstChild.css('margin-top', 0); lastChild.css('margin-bottom', 0); */ } else { elem.css('width', ''); maintainAtBottom = settings.stickToBottom && isCloseToBottom(); maintainAtRight = settings.stickToRight && isCloseToRight(); hasContainingSpaceChanged = elem.innerWidth() + originalPaddingTotalWidth != paneWidth || elem.outerHeight() != paneHeight; if (hasContainingSpaceChanged) { paneWidth = elem.innerWidth() + originalPaddingTotalWidth; paneHeight = elem.innerHeight(); container.css({ width: paneWidth + 'px', height: paneHeight + 'px' }); } // If nothing changed since last check... if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) { elem.width(paneWidth); return; } previousContentWidth = contentWidth; pane.css('width', ''); elem.width(paneWidth); container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end(); } pane.css('overflow', 'auto'); if (s.contentWidth) { contentWidth = s.contentWidth; } else { contentWidth = pane[0].scrollWidth; } contentHeight = pane[0].scrollHeight; pane.css('overflow', ''); percentInViewH = contentWidth / paneWidth; percentInViewV = contentHeight / paneHeight; isScrollableV = percentInViewV > 1; isScrollableH = percentInViewH > 1; if (!(isScrollableH || isScrollableV)) { elem.removeClass('jspScrollable'); pane.css({ top: 0, width: container.width() - originalPaddingTotalWidth }); removeMousewheel(); removeFocusHandler(); removeKeyboardNav(); removeClickOnTrack(); } else { elem.addClass('jspScrollable'); isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition); if (isMaintainingPositon) { lastContentX = contentPositionX(); lastContentY = contentPositionY(); } initialiseVerticalScroll(); initialiseHorizontalScroll(); resizeScrollbars(); if (isMaintainingPositon) { scrollToX(maintainAtRight ? (contentWidth - paneWidth) : lastContentX, false); scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false); } initFocusHandler(); initMousewheel(); initTouch(); if (settings.enableKeyboardNavigation) { initKeyboardNav(); } if (settings.clickOnTrack) { initClickOnTrack(); } observeHash(); if (settings.hijackInternalLinks) { hijackInternalLinks(); } } if (settings.autoReinitialise && !reinitialiseInterval) { reinitialiseInterval = setInterval( function () { initialise(settings); }, settings.autoReinitialiseDelay ); } else if (!settings.autoReinitialise && reinitialiseInterval) { clearInterval(reinitialiseInterval); } originalScrollTop && elem.scrollTop(0) && scrollToY(originalScrollTop, false); originalScrollLeft && elem.scrollLeft(0) && scrollToX(originalScrollLeft, false); elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]); } function initialiseVerticalScroll() { if (isScrollableV) { container.append( $('
    ').append( $('
    '), $('
    ').append( $('
    ').append( $('
    '), $('
    ') ) ), $('
    ') ) ); verticalBar = container.find('>.jspVerticalBar'); verticalTrack = verticalBar.find('>.jspTrack'); verticalDrag = verticalTrack.find('>.jspDrag'); if (settings.showArrows) { arrowUp = $('').bind( 'mousedown.jsp', getArrowScroll(0, -1) ).bind('click.jsp', nil); arrowDown = $('').bind( 'mousedown.jsp', getArrowScroll(0, 1) ).bind('click.jsp', nil); if (settings.arrowScrollOnHover) { arrowUp.bind('mouseover.jsp', getArrowScroll(0, -1, arrowUp)); arrowDown.bind('mouseover.jsp', getArrowScroll(0, 1, arrowDown)); } appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown); } verticalTrackHeight = paneHeight; container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each( function () { verticalTrackHeight -= $(this).outerHeight(); } ); verticalDrag.hover( function () { verticalDrag.addClass('jspHover'); }, function () { verticalDrag.removeClass('jspHover'); } ).bind( 'mousedown.jsp', function (e) { // Stop IE from allowing text selection $('html').bind('dragstart.jsp selectstart.jsp', nil); verticalDrag.addClass('jspActive'); var startY = e.pageY - verticalDrag.position().top; $('html').bind( 'mousemove.jsp', function (e) { positionDragY(e.pageY - startY, false); } ).bind('mouseup.jsp mouseleave.jsp', cancelDrag); return false; } ); sizeVerticalScrollbar(); } } function sizeVerticalScrollbar() { verticalTrack.height(verticalTrackHeight + 'px'); verticalDragPosition = 0; scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth(); // Make the pane thinner to allow for the vertical scrollbar pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth); // Add margin to the left of the pane if scrollbars are on that side (to position // the scrollbar on the left or right set it's left or right property in CSS) try { if (verticalBar.position().left === 0) { pane.css('margin-left', scrollbarWidth + 'px'); } } catch (err) { } } function initialiseHorizontalScroll() { if (isScrollableH) { container.append( $('
    ').append( $('
    '), $('
    ').append( $('
    ').append( $('
    '), $('
    ') ) ), $('
    ') ) ); horizontalBar = container.find('>.jspHorizontalBar'); horizontalTrack = horizontalBar.find('>.jspTrack'); horizontalDrag = horizontalTrack.find('>.jspDrag'); if (settings.showArrows) { arrowLeft = $('').bind( 'mousedown.jsp', getArrowScroll(-1, 0) ).bind('click.jsp', nil); arrowRight = $('').bind( 'mousedown.jsp', getArrowScroll(1, 0) ).bind('click.jsp', nil); if (settings.arrowScrollOnHover) { arrowLeft.bind('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft)); arrowRight.bind('mouseover.jsp', getArrowScroll(1, 0, arrowRight)); } appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight); } horizontalDrag.hover( function () { horizontalDrag.addClass('jspHover'); }, function () { horizontalDrag.removeClass('jspHover'); } ).bind( 'mousedown.jsp', function (e) { // Stop IE from allowing text selection $('html').bind('dragstart.jsp selectstart.jsp', nil); horizontalDrag.addClass('jspActive'); var startX = e.pageX - horizontalDrag.position().left; $('html').bind( 'mousemove.jsp', function (e) { positionDragX(e.pageX - startX, false); } ).bind('mouseup.jsp mouseleave.jsp', cancelDrag); return false; } ); horizontalTrackWidth = container.innerWidth(); sizeHorizontalScrollbar(); } } function sizeHorizontalScrollbar() { container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each( function () { horizontalTrackWidth -= $(this).outerWidth(); } ); horizontalTrack.width(horizontalTrackWidth + 'px'); horizontalDragPosition = 0; } function resizeScrollbars() { if (isScrollableH && isScrollableV) { var horizontalTrackHeight = horizontalTrack.outerHeight(), verticalTrackWidth = verticalTrack.outerWidth(); verticalTrackHeight -= horizontalTrackHeight; $(horizontalBar).find('>.jspCap:visible,>.jspArrow').each( function () { horizontalTrackWidth += $(this).outerWidth(); } ); horizontalTrackWidth -= verticalTrackWidth; paneHeight -= verticalTrackWidth; paneWidth -= horizontalTrackHeight; horizontalTrack.parent().append( $('
    ').css('width', horizontalTrackHeight + 'px') ); sizeVerticalScrollbar(); sizeHorizontalScrollbar(); } // reflow content if (isScrollableH) { pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px'); } contentHeight = pane.outerHeight(); percentInViewV = contentHeight / paneHeight; if (isScrollableH) { horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth); if (horizontalDragWidth > settings.horizontalDragMaxWidth) { horizontalDragWidth = settings.horizontalDragMaxWidth; } else if (horizontalDragWidth < settings.horizontalDragMinWidth) { horizontalDragWidth = settings.horizontalDragMinWidth; } horizontalDrag.width(horizontalDragWidth + 'px'); dragMaxX = horizontalTrackWidth - horizontalDragWidth; _positionDragX(horizontalDragPosition); // To update the state for the arrow buttons } if (isScrollableV) { verticalDragHeight = Math.ceil(1 / percentInViewV * verticalTrackHeight); if (verticalDragHeight > settings.verticalDragMaxHeight) { verticalDragHeight = settings.verticalDragMaxHeight; } else if (verticalDragHeight < settings.verticalDragMinHeight) { verticalDragHeight = settings.verticalDragMinHeight; } verticalDrag.height(verticalDragHeight + 'px'); dragMaxY = verticalTrackHeight - verticalDragHeight; _positionDragY(verticalDragPosition); // To update the state for the arrow buttons } } function appendArrows(ele, p, a1, a2) { var p1 = "before", p2 = "after", aTemp; // Sniff for mac... Is there a better way to determine whether the arrows would naturally appear // at the top or the bottom of the bar? if (p == "os") { p = /Mac/.test(navigator.platform) ? "after" : "split"; } if (p == p1) { p2 = p; } else if (p == p2) { p1 = p; aTemp = a1; a1 = a2; a2 = aTemp; } ele[p1](a1)[p2](a2); } function getArrowScroll(dirX, dirY, ele) { return function () { arrowScroll(dirX, dirY, this, ele); this.blur(); return false; }; } function arrowScroll(dirX, dirY, arrow, ele) { arrow = $(arrow).addClass('jspActive'); var eve, scrollTimeout, isFirst = true, doScroll = function () { if (dirX !== 0) { jsp.scrollByX(dirX * settings.arrowButtonSpeed); } if (dirY !== 0) { jsp.scrollByY(dirY * settings.arrowButtonSpeed); } scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq); isFirst = false; }; doScroll(); eve = ele ? 'mouseout.jsp' : 'mouseup.jsp'; ele = ele || $('html'); ele.bind( eve, function () { arrow.removeClass('jspActive'); scrollTimeout && clearTimeout(scrollTimeout); scrollTimeout = null; ele.unbind(eve); } ); } function initClickOnTrack() { removeClickOnTrack(); if (isScrollableV) { verticalTrack.bind( 'mousedown.jsp', function (e) { if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) { var clickedTrack = $(this), offset = clickedTrack.offset(), direction = e.pageY - offset.top - verticalDragPosition, scrollTimeout, isFirst = true, doScroll = function () { var offset = clickedTrack.offset(), pos = e.pageY - offset.top - verticalDragHeight / 2, contentDragY = paneHeight * settings.scrollPagePercent, dragY = dragMaxY * contentDragY / (contentHeight - paneHeight); if (direction < 0) { if (verticalDragPosition - dragY > pos) { jsp.scrollByY(-contentDragY); } else { positionDragY(pos); } } else if (direction > 0) { if (verticalDragPosition + dragY < pos) { jsp.scrollByY(contentDragY); } else { positionDragY(pos); } } else { cancelClick(); return; } scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq); isFirst = false; }, cancelClick = function () { scrollTimeout && clearTimeout(scrollTimeout); scrollTimeout = null; $(document).unbind('mouseup.jsp', cancelClick); }; doScroll(); $(document).bind('mouseup.jsp', cancelClick); return false; } } ); } if (isScrollableH) { horizontalTrack.bind( 'mousedown.jsp', function (e) { if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) { var clickedTrack = $(this), offset = clickedTrack.offset(), direction = e.pageX - offset.left - horizontalDragPosition, scrollTimeout, isFirst = true, doScroll = function () { var offset = clickedTrack.offset(), pos = e.pageX - offset.left - horizontalDragWidth / 2, contentDragX = paneWidth * settings.scrollPagePercent, dragX = dragMaxX * contentDragX / (contentWidth - paneWidth); if (direction < 0) { if (horizontalDragPosition - dragX > pos) { jsp.scrollByX(-contentDragX); } else { positionDragX(pos); } } else if (direction > 0) { if (horizontalDragPosition + dragX < pos) { jsp.scrollByX(contentDragX); } else { positionDragX(pos); } } else { cancelClick(); return; } scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq); isFirst = false; }, cancelClick = function () { scrollTimeout && clearTimeout(scrollTimeout); scrollTimeout = null; $(document).unbind('mouseup.jsp', cancelClick); }; doScroll(); $(document).bind('mouseup.jsp', cancelClick); return false; } } ); } } function removeClickOnTrack() { if (horizontalTrack) { horizontalTrack.unbind('mousedown.jsp'); } if (verticalTrack) { verticalTrack.unbind('mousedown.jsp'); } } function cancelDrag() { $('html').unbind('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp'); if (verticalDrag) { verticalDrag.removeClass('jspActive'); } if (horizontalDrag) { horizontalDrag.removeClass('jspActive'); } } function positionDragY(destY, animate) { if (!isScrollableV) { return; } if (destY < 0) { destY = 0; } else if (destY > dragMaxY) { destY = dragMaxY; } // can't just check if(animate) because false is a valid value that could be passed in... if (animate === undefined) { animate = settings.animateScroll; } if (animate) { jsp.animate(verticalDrag, 'top', destY, _positionDragY); } else { verticalDrag.css('top', destY); _positionDragY(destY); } } function _positionDragY(destY) { if (destY === undefined) { destY = verticalDrag.position().top; } container.scrollTop(0); verticalDragPosition = destY; var isAtTop = verticalDragPosition === 0, isAtBottom = verticalDragPosition == dragMaxY, percentScrolled = destY / dragMaxY, destTop = -percentScrolled * (contentHeight - paneHeight); if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) { wasAtTop = isAtTop; wasAtBottom = isAtBottom; elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]); } updateVerticalArrows(isAtTop, isAtBottom); pane.css('top', destTop); elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll'); } function positionDragX(destX, animate) { if (!isScrollableH) { return; } if (destX < 0) { destX = 0; } else if (destX > dragMaxX) { destX = dragMaxX; } if (animate === undefined) { animate = settings.animateScroll; } if (animate) { jsp.animate(horizontalDrag, 'left', destX, _positionDragX); } else { horizontalDrag.css('left', destX); _positionDragX(destX); } } function _positionDragX(destX) { if (destX === undefined) { destX = horizontalDrag.position().left; } container.scrollTop(0); horizontalDragPosition = destX; var isAtLeft = horizontalDragPosition === 0, isAtRight = horizontalDragPosition == dragMaxX, percentScrolled = destX / dragMaxX, destLeft = -percentScrolled * (contentWidth - paneWidth); if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) { wasAtLeft = isAtLeft; wasAtRight = isAtRight; elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]); } updateHorizontalArrows(isAtLeft, isAtRight); pane.css('left', destLeft); elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll'); } function updateVerticalArrows(isAtTop, isAtBottom) { if (settings.showArrows) { arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled'); arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled'); } } function updateHorizontalArrows(isAtLeft, isAtRight) { if (settings.showArrows) { arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled'); arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled'); } } function scrollToY(destY, animate) { var percentScrolled = destY / (contentHeight - paneHeight); positionDragY(percentScrolled * dragMaxY, animate); } function scrollToX(destX, animate) { var percentScrolled = destX / (contentWidth - paneWidth); positionDragX(percentScrolled * dragMaxX, animate); } function scrollToElement(ele, stickToTop, animate) { var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX; // Legal hash values aren't necessarily legal jQuery selectors so we need to catch any // errors from the lookup... try { e = $(ele); } catch (err) { return; } eleHeight = e.outerHeight(); eleWidth = e.outerWidth(); container.scrollTop(0); container.scrollLeft(0); // loop through parents adding the offset top of any elements that are relatively positioned between // the focused element and the jspPane so we can get the true distance from the top // of the focused element to the top of the scrollpane... while (!e.is('.jspPane')) { eleTop += e.position().top; eleLeft += e.position().left; e = e.offsetParent(); if (/^body|html$/i.test(e[0].nodeName)) { // we ended up too high in the document structure. Quit! return; } } viewportTop = contentPositionY(); maxVisibleEleTop = viewportTop + paneHeight; if (eleTop < viewportTop || stickToTop) { // element is above viewport destY = eleTop - settings.verticalGutter; } else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport destY = eleTop - paneHeight + eleHeight + settings.verticalGutter; } if (destY) { scrollToY(destY, animate); } viewportLeft = contentPositionX(); maxVisibleEleLeft = viewportLeft + paneWidth; if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport destX = eleLeft - settings.horizontalGutter; } else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter; } if (destX) { scrollToX(destX, animate); } } function contentPositionX() { return -pane.position().left; } function contentPositionY() { return -pane.position().top; } function isCloseToBottom() { var scrollableHeight = contentHeight - paneHeight; return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10); } function isCloseToRight() { var scrollableWidth = contentWidth - paneWidth; return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10); } function initMousewheel() { container.unbind(mwEvent).bind( mwEvent, function (event, delta, deltaX, deltaY) { var dX = horizontalDragPosition, dY = verticalDragPosition; jsp.scrollBy(deltaX * settings.mouseWheelSpeed, -deltaY * settings.mouseWheelSpeed, false); // return true if there was no movement so rest of screen can scroll return dX == horizontalDragPosition && dY == verticalDragPosition; } ); } function removeMousewheel() { container.unbind(mwEvent); } function nil() { return false; } function initFocusHandler() { pane.find(':input,a').unbind('focus.jsp').bind( 'focus.jsp', function (e) { scrollToElement(e.target, false); } ); } function removeFocusHandler() { pane.find(':input,a').unbind('focus.jsp'); } function initKeyboardNav() { var keyDown, elementHasScrolled, validParents = []; isScrollableH && validParents.push(horizontalBar[0]); isScrollableV && validParents.push(verticalBar[0]); // IE also focuses elements that don't have tabindex set. pane.focus( function () { elem.focus(); } ); elem.attr('tabindex', 0) .unbind('keydown.jsp keypress.jsp') .bind( 'keydown.jsp', function (e) { if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)) { return; } var dX = horizontalDragPosition, dY = verticalDragPosition; switch (e.keyCode) { case 40: // down case 38: // up case 34: // page down case 32: // space case 33: // page up case 39: // right case 37: // left keyDown = e.keyCode; keyDownHandler(); break; case 35: // end scrollToY(contentHeight - paneHeight); keyDown = null; break; case 36: // home scrollToY(0); keyDown = null; break; } elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition; return !elementHasScrolled; } ).bind( 'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls... function (e) { if (e.keyCode == keyDown) { keyDownHandler(); } return !elementHasScrolled; } ); if (settings.hideFocus) { elem.css('outline', 'none'); if ('hideFocus' in container[0]) { elem.attr('hideFocus', true); } } else { elem.css('outline', ''); if ('hideFocus' in container[0]) { elem.attr('hideFocus', false); } } function keyDownHandler() { var dX = horizontalDragPosition, dY = verticalDragPosition; switch (keyDown) { case 40: // down jsp.scrollByY(settings.keyboardSpeed, false); break; case 38: // up jsp.scrollByY(-settings.keyboardSpeed, false); break; case 34: // page down case 32: // space jsp.scrollByY(paneHeight * settings.scrollPagePercent, false); break; case 33: // page up jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false); break; case 39: // right jsp.scrollByX(settings.keyboardSpeed, false); break; case 37: // left jsp.scrollByX(-settings.keyboardSpeed, false); break; } elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition; return elementHasScrolled; } } function removeKeyboardNav() { elem.attr('tabindex', '-1') .removeAttr('tabindex') .unbind('keydown.jsp keypress.jsp'); } function observeHash() { if (location.hash && location.hash.length > 1) { var e, retryInt, hash = escape(location.hash.substr(1)) // hash must be escaped to prevent XSS ; try { e = $('#' + hash + ', a[name="' + hash + '"]'); } catch (err) { return; } if (e.length && pane.find(hash)) { // nasty workaround but it appears to take a little while before the hash has done its thing // to the rendered page so we just wait until the container's scrollTop has been messed up. if (container.scrollTop() === 0) { retryInt = setInterval( function () { if (container.scrollTop() > 0) { scrollToElement(e, true); $(document).scrollTop(container.position().top); clearInterval(retryInt); } }, 50 ); } else { scrollToElement(e, true); $(document).scrollTop(container.position().top); } } } } function hijackInternalLinks() { // only register the link handler once if ($(document.body).data('jspHijack')) { return; } // remember that the handler was bound $(document.body).data('jspHijack', true); // use live handler to also capture newly created links $(document.body).delegate('a[href*=#]', 'click', function (event) { // does the link point to the same page? // this also takes care of cases with a -Tag or Links not starting with the hash # // e.g. when the current url already is index.html var href = this.href.substr(0, this.href.indexOf('#')), locationHref = location.href, hash, element, container, jsp, scrollTop, elementTop; if (location.href.indexOf('#') !== -1) { locationHref = location.href.substr(0, location.href.indexOf('#')); } if (href !== locationHref) { // the link points to another page return; } // check if jScrollPane should handle this click event hash = escape(this.href.substr(this.href.indexOf('#') + 1)); // find the element on the page element; try { element = $('#' + hash + ', a[name="' + hash + '"]'); } catch (e) { // hash is not a valid jQuery identifier return; } if (!element.length) { // this link does not point to an element on this page return; } container = element.closest('.jspScrollable'); jsp = container.data('jsp'); // jsp might be another jsp instance than the one, that bound this event // remember: this event is only bound once for all instances. jsp.scrollToElement(element, true); if (container[0].scrollIntoView) { // also scroll to the top of the container (if it is not visible) scrollTop = $(window).scrollTop(); elementTop = element.offset().top; if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) { container[0].scrollIntoView(); } } // jsp handled this event, prevent the browser default (scrolling :P) event.preventDefault(); }); } // Init touch on iPad, iPhone, iPod, Android function initTouch() { var startX, startY, touchStartX, touchStartY, moved, moving = false; container.unbind('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').bind( 'touchstart.jsp', function (e) { var touch = e.originalEvent.touches[0]; startX = contentPositionX(); startY = contentPositionY(); touchStartX = touch.pageX; touchStartY = touch.pageY; moved = false; moving = true; } ).bind( 'touchmove.jsp', function (ev) { if (!moving) { return; } var touchPos = ev.originalEvent.touches[0], dX = horizontalDragPosition, dY = verticalDragPosition; jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY); moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5; // return true if there was no movement so rest of screen can scroll return dX == horizontalDragPosition && dY == verticalDragPosition; } ).bind( 'touchend.jsp', function (e) { moving = false; /*if(moved) { return false; }*/ } ).bind( 'click.jsp-touchclick', function (e) { if (moved) { moved = false; return false; } } ); } function destroy() { var currentY = contentPositionY(), currentX = contentPositionX(); elem.removeClass('jspScrollable').unbind('.jsp'); elem.replaceWith(originalElement.append(pane.children())); originalElement.scrollTop(currentY); originalElement.scrollLeft(currentX); // clear reinitialize timer if active if (reinitialiseInterval) { clearInterval(reinitialiseInterval); } } // Public API $.extend( jsp, { // Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it // was initialised). The settings object which is passed in will override any settings from the // previous time it was initialised - if you don't pass any settings then the ones from the previous // initialisation will be used. reinitialise: function (s) { s = $.extend({}, settings, s); initialise(s); }, // Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so // that it can be seen within the viewport. If stickToTop is true then the element will appear at // the top of the viewport, if it is false then the viewport will scroll as little as possible to // show the element. You can also specify if you want animation to occur. If you don't provide this // argument then the animateScroll value from the settings object is used instead. scrollToElement: function (ele, stickToTop, animate) { scrollToElement(ele, stickToTop, animate); }, // Scrolls the pane so that the specified co-ordinates within the content are at the top left // of the viewport. animate is optional and if not passed then the value of animateScroll from // the settings object this jScrollPane was initialised with is used. scrollTo: function (destX, destY, animate) { scrollToX(destX, animate); scrollToY(destY, animate); }, // Scrolls the pane so that the specified co-ordinate within the content is at the left of the // viewport. animate is optional and if not passed then the value of animateScroll from the settings // object this jScrollPane was initialised with is used. scrollToX: function (destX, animate) { scrollToX(destX, animate); }, // Scrolls the pane so that the specified co-ordinate within the content is at the top of the // viewport. animate is optional and if not passed then the value of animateScroll from the settings // object this jScrollPane was initialised with is used. scrollToY: function (destY, animate) { scrollToY(destY, animate); }, // Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate // is optional and if not passed then the value of animateScroll from the settings object this // jScrollPane was initialised with is used. scrollToPercentX: function (destPercentX, animate) { scrollToX(destPercentX * (contentWidth - paneWidth), animate); }, // Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate // is optional and if not passed then the value of animateScroll from the settings object this // jScrollPane was initialised with is used. scrollToPercentY: function (destPercentY, animate) { scrollToY(destPercentY * (contentHeight - paneHeight), animate); }, // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then // the value of animateScroll from the settings object this jScrollPane was initialised with is used. scrollBy: function (deltaX, deltaY, animate) { jsp.scrollByX(deltaX, animate); jsp.scrollByY(deltaY, animate); }, // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then // the value of animateScroll from the settings object this jScrollPane was initialised with is used. scrollByX: function (deltaX, animate) { var destX = contentPositionX() + Math[deltaX < 0 ? 'floor' : 'ceil'](deltaX), percentScrolled = destX / (contentWidth - paneWidth); positionDragX(percentScrolled * dragMaxX, animate); }, // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then // the value of animateScroll from the settings object this jScrollPane was initialised with is used. scrollByY: function (deltaY, animate) { var destY = contentPositionY() + Math[deltaY < 0 ? 'floor' : 'ceil'](deltaY), percentScrolled = destY / (contentHeight - paneHeight); positionDragY(percentScrolled * dragMaxY, animate); }, // Positions the horizontal drag at the specified x position (and updates the viewport to reflect // this). animate is optional and if not passed then the value of animateScroll from the settings // object this jScrollPane was initialised with is used. positionDragX: function (x, animate) { positionDragX(x, animate); }, // Positions the vertical drag at the specified y position (and updates the viewport to reflect // this). animate is optional and if not passed then the value of animateScroll from the settings // object this jScrollPane was initialised with is used. positionDragY: function (y, animate) { positionDragY(y, animate); }, // This method is called when jScrollPane is trying to animate to a new position. You can override // it if you want to provide advanced animation functionality. It is passed the following arguments: // * ele - the element whose position is being animated // * prop - the property that is being animated // * value - the value it's being animated to // * stepCallback - a function that you must execute each time you update the value of the property // You can use the default implementation (below) as a starting point for your own implementation. animate: function (ele, prop, value, stepCallback) { var params = {}; params[prop] = value; ele.animate( params, { 'duration': settings.animateDuration, 'easing': settings.animateEase, 'queue': false, 'step': stepCallback } ); }, // Returns the current x position of the viewport with regards to the content pane. getContentPositionX: function () { return contentPositionX(); }, // Returns the current y position of the viewport with regards to the content pane. getContentPositionY: function () { return contentPositionY(); }, // Returns the width of the content within the scroll pane. getContentWidth: function () { return contentWidth; }, // Returns the height of the content within the scroll pane. getContentHeight: function () { return contentHeight; }, // Returns the horizontal position of the viewport within the pane content. getPercentScrolledX: function () { return contentPositionX() / (contentWidth - paneWidth); }, // Returns the vertical position of the viewport within the pane content. getPercentScrolledY: function () { return contentPositionY() / (contentHeight - paneHeight); }, // Returns whether or not this scrollpane has a horizontal scrollbar. getIsScrollableH: function () { return isScrollableH; }, // Returns whether or not this scrollpane has a vertical scrollbar. getIsScrollableV: function () { return isScrollableV; }, // Gets a reference to the content pane. It is important that you use this method if you want to // edit the content of your jScrollPane as if you access the element directly then you may have some // problems (as your original element has had additional elements for the scrollbars etc added into // it). getContentPane: function () { return pane; }, // Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the // animateScroll value from settings is used instead. scrollToBottom: function (animate) { positionDragY(dragMaxY, animate); }, // Hijacks the links on the page which link to content inside the scrollpane. If you have changed // the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the // contents of your scroll pane will work then call this function. hijackInternalLinks: $.noop, // Removes the jScrollPane and returns the page to the state it was in before jScrollPane was // initialised. destroy: function () { destroy(); } } ); initialise(s); } // Pluginifying code... settings = $.extend({}, $.fn.jScrollPane.defaults, settings); // Apply default speed $.each(['mouseWheelSpeed', 'arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function () { settings[this] = settings[this] || settings.speed; }); return this.each( function () { var elem = $(this), jspApi = elem.data('jsp'); if (jspApi) { jspApi.reinitialise(settings); } else { $("script", elem).filter('[type="text/javascript"], :not([type])').remove(); jspApi = new JScrollPane(elem, settings); elem.data('jsp', jspApi); } } ); }; $.fn.jScrollPane.defaults = { showArrows: false, maintainPosition: true, stickToBottom: false, stickToRight: false, clickOnTrack: true, autoReinitialise: false, autoReinitialiseDelay: 500, verticalDragMinHeight: 0, verticalDragMaxHeight: 99999, horizontalDragMinWidth: 0, horizontalDragMaxWidth: 99999, contentWidth: undefined, animateScroll: false, animateDuration: 300, animateEase: 'linear', hijackInternalLinks: false, verticalGutter: 4, horizontalGutter: 4, mouseWheelSpeed: 0, arrowButtonSpeed: 0, arrowRepeatFreq: 50, arrowScrollOnHover: false, trackClickSpeed: 0, trackClickRepeatFreq: 70, verticalArrowPositions: 'split', horizontalArrowPositions: 'split', enableKeyboardNavigation: true, hideFocus: false, keyboardSpeed: 0, initialDelay: 300, // Delay before starting repeating speed: 30, // Default speed when others falsey scrollPagePercent: .8 // Percent of visible area scrolled when pageUp/Down or track area pressed }; })(jQuery); (function ($) { $.fn.dnnSettingDropdown = function () { var clicked = function () { if ($(this).hasClass('dnnButtonDropdown')) { $(this).removeClass('dnnButtonDropdown').addClass('dnnButtonDropdown-clicked'); $(this).next().show(); } }; var hideDropdown = function () { var btn = $(this).children(':first'); if (btn.hasClass('dnnButtonDropdown-clicked')) { btn.removeClass('dnnButtonDropdown-clicked').addClass('dnnButtonDropdown'); btn.next().fadeOut(); } }; var hoverConfig = { over: function () { }, out: hideDropdown, timout: 600 }; return $(this).each(function () { $(this).unbind('click', clicked).bind('click', clicked); $(this).parent().hoverIntent(hoverConfig); }); }; })(jQuery); (function ($) { /* Start customised controls */ var dnnInitCustomisedCtrls = function () { $('input[type="checkbox"]').dnnCheckbox(); $('input[type="radio"]').dnnCheckbox({ cls: 'dnnRadiobutton' }); $('.dnnTooltip').dnnTooltip(); var divs = $.detectScroll(); for (var i = 0; i < divs.length; i++) $(divs[i]).jScrollPane(); $('input[type="text"], input[type="password"]').unbind('focus', inputFocusFix).bind('focus', inputFocusFix); $(':file').dnnFileInput(); }; var inputFocusFix = function () { var errorMsg = $(this).next(); if (errorMsg.hasClass('dnnFormError')) errorMsg.hide(); }; var saveRgDataDivScrollTop = function () { window.__rgDataDivScrollTopPersistArray = []; $('.rgDataDiv').each(function () { var $this = $(this); var ele = $this.get(0); if (ele.scrollPane) { var api = ele.scrollPane.data('jsp'); var y = api.getContentPositionY(); window.__rgDataDivScrollTopPersistArray.push(y); } }); }; window.__rgDataDivScrollTopPersistArray = []; $(window).ajaxComplete(dnnInitCustomisedCtrls); Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(saveRgDataDivScrollTop); Sys.WebForms.PageRequestManager.getInstance().add_endRequest(dnnInitCustomisedCtrls); $(dnnInitCustomisedCtrls); })(jQuery);