--- a/src/ztfy/myams/resources/js/myams.js Mon Jun 04 12:32:06 2018 +0200
+++ b/src/ztfy/myams/resources/js/myams.js Mon Jun 04 12:32:38 2018 +0200
@@ -10,10 +10,10 @@
* ©2014-2016 Thierry Florac <tflorac@ulthar.net>
*/
+"use strict";
+
(function($, globals) {
- "use strict";
-
var console = globals.console;
/**
@@ -90,6 +90,13 @@
/**
* JQuery filter on parents class
+ * This filter is often combined with ":not()" to select DOM objects which don't have
+ * parents of a given class.
+ * For example:
+ *
+ * $('.hint:not(:parents(.nohints))', element);
+ *
+ * will select all elements with ".hint" class which don't have a parent with '.nohints' class.
*/
$.expr[':'].parents = function(obj, index, meta /*, stack*/) {
return $(obj).parents(meta[3]).length > 0;
@@ -98,7 +105,7 @@
/**
* JQuery 'scrollbarWidth' function
- * Get width of vertical scrollbar
+ * Get width of default vertical scrollbar
*/
if ($.scrollbarWidth === undefined) {
$.scrollbarWidth = function() {
@@ -116,16 +123,16 @@
*/
$.fn.extend({
- /*
+ /**
* Check if current object is empty or not
*/
exists: function() {
return $(this).length > 0;
},
- /*
+ /**
* Get object if it supports given CSS class,
- * otherwise looks for parents
+ * otherwise look for parents
*/
objectOrParentWithClass: function(klass) {
if (this.hasClass(klass)) {
@@ -135,7 +142,7 @@
}
},
- /*
+ /**
* Build an array of attributes of the given selection
*/
listattr: function(attr) {
@@ -146,7 +153,7 @@
return result;
},
- /*
+ /**
* CSS style function
* Code from Aram Kocharyan on stackoverflow.com
*/
@@ -176,7 +183,7 @@
}
},
- /*
+ /**
* Remove CSS classes starting with a given prefix
*/
removeClassPrefix: function (prefix) {
@@ -189,7 +196,7 @@
return this;
},
- /*
+ /**
* Context menu handler
*/
contextMenu: function(settings) {
@@ -216,7 +223,7 @@
if (e.ctrlKey) {
return;
}
- //open menu
+ // open menu
$(settings.menuSelector).data("invokedOn", $(e.target))
.show()
.css({
@@ -235,7 +242,7 @@
return false;
});
- //make sure menu closes on any click
+ // make sure menu closes on any click
$(document).click(function () {
$(settings.menuSelector).hide();
});
@@ -576,9 +583,9 @@
};
/**
- * Get script or CSS file using browser cache
- * Script or CSS URLs can include variable names, given between braces, as in
- * {MyAMS.baseURL}
+ * Get target URL matching given source
+ *
+ * Given URL can include variable names (with their namespace), given between braces, as in {MyAMS.baseURL}
*/
MyAMS.getSource = function(url) {
return url.replace(/{[^{}]*}/g, function(match) {
@@ -586,6 +593,13 @@
});
};
+ /**
+ * Script loader function
+ *
+ * @param url: script URL
+ * @param callback: a callback to be called after script loading
+ * @param options: a set of options to be added to AJAX call
+ */
MyAMS.getScript = function(url, callback, options) {
if (typeof(callback) === 'object') {
options = callback;
@@ -606,19 +620,44 @@
return $.ajax(settings);
};
- MyAMS.getCSS = function(url, id) {
+ /**
+ * CSS file loader function
+ * Cross-browser code copied from Stoyan Stefanov blog to be able to
+ * call a callback when CSS is realy loaded.
+ * See: https://www.phpied.com/when-is-a-stylesheet-really-loaded
+ *
+ * @param url: CSS file URL
+ * @param id: a unique ID given to CSS file
+ * @param callback: optional callback function to be called when CSS file is loaded. If set, callback is called
+ * with a 'first_load' boolean argument to indicate is CSS was already loaded (*false* value) or not (*true*
+ * value).
+ * @param options: callback options
+ */
+ MyAMS.getCSS = function(url, id, callback, options) {
+ if (callback) {
+ callback = ams.getFunctionByName(callback);
+ }
var head = $('HEAD');
- var css = $('link[data-ams-id="' + id + '"]', head);
- if (css.length === 0) {
- var source = ams.getSource(url);
- if (ams.devmode) {
- source += '?_=' + new Date().getTime();
- }
- $('<link />').attr({rel: 'stylesheet',
- type: 'text/css',
- href: source,
- 'data-ams-id': id})
- .appendTo(head);
+ var style = $('style[data-ams-id="' + id + '"]', head);
+ if (style.length === 0) {
+ style = $('<style>').attr('data-ams-id', id)
+ .text('@import "' + ams.getSource(url) + '";');
+ if (callback) {
+ var styleInterval = setInterval(function() {
+ try {
+ var _check = style[0].sheet.cssRules; // Is only populated when file is loaded
+ callback.call(window, true, options);
+ clearInterval(styleInterval);
+ } catch (e) {
+ // CSS is not loaded yet...
+ }
+ }, 10);
+ }
+ style.appendTo(head);
+ } else {
+ if (callback) {
+ callback.call(window, false, options);
+ }
}
};
@@ -628,11 +667,14 @@
*/
MyAMS.event = {
+ /**
+ * Stop current event propagation
+ */
stop: function(event) {
if (!event) {
event = window.event;
}
- if (event) {
+ if (event && (typeof(event) !== 'string')) {
if (event.stopPropagation) {
event.stopPropagation();
event.preventDefault();
@@ -650,6 +692,9 @@
*/
MyAMS.browser = {
+ /**
+ * Get IE version
+ */
getInternetExplorerVersion: function() {
var rv = -1;
if (navigator.appName === "Microsoft Internet Explorer") {
@@ -662,6 +707,9 @@
return rv;
},
+ /**
+ * Display alert for old IE version
+ */
checkVersion: function() {
var msg = "You're not using Windows Internet Explorer.";
var ver = this.getInternetExplorerVersion();
@@ -677,6 +725,9 @@
}
},
+ /**
+ * Check if IE is in version 8 or lower
+ */
isIE8orlower: function() {
var msg = "0";
var ver = this.getInternetExplorerVersion();
@@ -691,6 +742,14 @@
},
+ /**
+ * Copy selection to clipboard
+ *
+ * If 'text' argument is provided, given text is copied to clipboard.
+ * Otherwise, text ou event's source is copied.
+ * Several methods are tested to do clipboard copy (based on browser features); il copy can't be done,
+ * a prompt is displayed to allow user to make a manual copy.
+ */
copyToClipboard: function(text) {
function doCopy(text) {
@@ -722,7 +781,7 @@
? ams.i18n.CLIPBOARD_TEXT_COPY_OK
: ams.i18n.CLIPBOARD_CHARACTER_COPY_OK,
icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
- timeout: 1000
+ timeout: 3000
});
} else if (globals.prompt) {
globals.prompt(MyAMS.i18n.CLIPBOARD_COPY, text);
@@ -812,12 +871,12 @@
/**
* Check for given feature and download script if necessary
*
- * @checker: pointer to a javascript object which will be downloaded in undefined
- * @source: URL of a javascript file containing requested feature
- * @callback: pointer to a function which will be called after the script is downloaded. The first
+ * @param checker: pointer to a javascript object which will be downloaded in undefined
+ * @param source: URL of a javascript file containing requested feature
+ * @param callback: pointer to a function which will be called after the script is downloaded. The first
* argument of this callback is a boolean value indicating if the script was just downloaded (true)
* or if the requested object was already loaded (false)
- * @options: callback options
+ * @param options: callback options
*/
check: function(checker, source, callback, options) {
@@ -1142,8 +1201,7 @@
message = result.message;
if (typeof(message) === 'string') {
if ((status === 'info') || (status === 'success')) {
- ams.skin.smallBox(status,
- {
+ ams.skin.smallBox(status, {
title: message,
icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
timeout: 3000
@@ -1160,16 +1218,25 @@
}
}
if (result.smallbox) {
- ams.skin.smallBox(result.smallbox_status || status,
- {title: result.smallbox,
- icon: 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
- timeout: 3000});
+ message = result.smallbox;
+ if (typeof(message) === 'string') {
+ ams.skin.smallBox(result.smallbox_status || status, {
+ title: result.smallbox,
+ icon: result.smallbox_icon || 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
+ timeout: result.smallbox_timeout || 3000
+ });
+ } else {
+ ams.skin.smallBox(message.status || status, {
+ title: message.message,
+ icon: message.icon || 'fa fa-fw fa-info-circle font-xs align-top margin-top-10',
+ timeout: message.timeout || 3000
+ });
+ }
}
if (result.messagebox) {
message = result.messagebox;
if (typeof(message) === 'string') {
- ams.skin.messageBox('info',
- {
+ ams.skin.messageBox('info', {
title: ams.i18n.ERROR_OCCURED,
content: message,
timeout: 10000
@@ -1179,12 +1246,13 @@
if (messageStatus === 'error' && form && target) {
ams.executeFunctionByName(form.data('ams-form-submit-error') || 'MyAMS.form.finalizeSubmitOnError', form, target);
}
- ams.skin.messageBox(messageStatus,
- {title: message.title || ams.i18n.ERROR_OCCURED,
- content: message.content,
- icon: message.icon,
- number: message.number,
- timeout: message.timeout === null ? undefined : (message.timeout || 10000)});
+ ams.skin.messageBox(messageStatus, {
+ title: message.title || ams.i18n.ERROR_OCCURED,
+ content: message.content,
+ icon: message.icon,
+ number: message.number,
+ timeout: message.timeout === null ? undefined : (message.timeout || 10000)
+ });
}
}
if (result.event) {
@@ -1197,6 +1265,9 @@
}
for (index =0; index < result.events.length; index++) {
event = result.events[index];
+ if (event === null) {
+ continue;
+ }
if (typeof(event) === 'string') {
form.trigger(event, result.events_options);
} else {
@@ -2117,14 +2188,16 @@
widget = $('[name="' + widgetData.name + ':list"]', form);
}
if (widget.exists()) {
+ // Update widget state
widget.parents('label:first')
.removeClassPrefix('state-')
.addClass('state-error')
.after('<span for="name" class="state-error">' + widgetData.message + '</span>');
- }
- // complete form alert message
- if (widgetData.label) {
- message.push(widgetData.label + ' : ' + widgetData.message);
+ } else {
+ // complete form alert message
+ if (widgetData.label) {
+ message.push(widgetData.label + ' : ' + widgetData.message);
+ }
}
// mark parent tab (if any) with error status
var tabIndex = widget.parents('.tab-pane').index() + 1;
@@ -2136,7 +2209,7 @@
}
}
}
- ams.skin.alert($('fieldset:first', form), errors.error_level || 'error', header, message, errors.error_message);
+ ams.skin.alert($('.form-group:first', form), errors.error_level || 'error', header, message, errors.error_message);
}
}
};
@@ -2459,21 +2532,16 @@
/** Select2 selection formatter */
select2FormatSelection: function(object, container) {
- if (object instanceof Array) {
- $(object).each(function() {
- if (typeof(this) === 'object') {
- container.append(this.text);
- } else {
- container.append(this);
- }
- });
- } else {
- if (typeof(object) === 'object') {
- container.append(object.text);
+ if (!(object instanceof Array)) {
+ object = [object];
+ }
+ $(object).each(function() {
+ if (typeof(this) === 'object') {
+ container.append(this.text);
} else {
- container.append(object);
+ container.append(this);
}
- }
+ });
},
/** Select2 'select-all' helper */
@@ -2538,6 +2606,48 @@
}
},
+ /** Select2 helper to automate selection change */
+ select2ChangeHelper: function() {
+ var source = $(this);
+ var data = source.data();
+ var target = $(data.amsSelect2HelperTarget);
+ switch (data.amsSelect2HelperType) {
+ case 'html':
+ target.html('<div class="text-center"><i class="fa fa-2x fa-gear fa-spin"></i></div>');
+ var params = {};
+ params[data.amsSelect2HelperArgument || 'value'] = source.val();
+ $.get(data.amsSelect2HelperUrl, params,
+ ams.getFunctionByName(data.amsSelect2HelperCallback) || function(result) {
+ if (result) {
+ target.html(result);
+ ams.initContent(target);
+ } else {
+ target.empty();
+ }
+ });
+ break;
+ case 'json-rpc':
+ target.html('<div class="text-center"><i class="fa fa-2x fa-gear fa-spin"></i></div>');
+ ams.jsonrpc.post(data.amsSelect2HelperMethod,
+ {value: source.val()},
+ {url: data.amsSelect2HelperUrl},
+ ams.getFunctionByName(data.amsSelect2HelperCallback) || function(result) {
+ if (result.result) {
+ target.html(result.result);
+ ams.initContent(target);
+ } else {
+ target.empty();
+ }
+ });
+ break;
+ default:
+ var callback = data.amsSelect2HelperCallback;
+ if (callback) {
+ ams.executeFunctionByName(callback, source, data);
+ }
+ }
+ },
+
/** Context menu handler */
contextMenuHandler: function(target, menu) {
var menuData = menu.data();
@@ -2590,6 +2700,23 @@
/** Datetimepicker dialog cleaner callback */
datetimepickerDialogHiddenCallback: function() {
$('.datepicker, .timepicker, .datetimepicker', this).datetimepicker('destroy');
+ },
+
+ /** Set SEO status */
+ setSEOStatus: function() {
+ var input = $(this);
+ var progress = input.siblings('.progress').children('.progress-bar');
+ var length = Math.min(input.val().length, 100);
+ var status = 'success';
+ if (length < 20 || length > 80) {
+ status = 'danger';
+ } else if (length < 40 || length > 66) {
+ status = 'warning';
+ }
+ progress.removeClassPrefix('progress-bar')
+ .addClass('progress-bar')
+ .addClass('progress-bar-' + status)
+ .css('width', length + '%');
}
};
@@ -2881,30 +3008,31 @@
if (hints.length > 0) {
ams.ajax.check($.fn.tipsy,
ams.baseURL + 'ext/jquery-tipsy' + ams.devext + '.js',
- function () {
+ function() {
ams.getCSS(ams.baseURL + '../css/ext/jquery-tipsy' + ams.devext + '.css',
- 'jquery-tipsy');
- hints.each(function () {
- var hint = $(this);
- var data = hint.data();
- var dataOptions = {
- html: data.amsHintHtml,
- title: ams.getFunctionByName(data.amsHintTitleGetter) || function () {
- var hint = $(this);
- var result = hint.attr('original-title') ||
- hint.attr(data.amsHintTitleAttr || 'title') ||
- (data.amsHintHtml ? hint.html() : hint.text());
- result = result.replace(/\?_="/, '?_=' + new Date().getTime() + '"');
- return result;
- },
- opacity: data.amsHintOpacity || 0.95,
- gravity: data.amsHintGravity || 'sw',
- offset: data.amsHintOffset || 0
- };
- var settings = $.extend({}, dataOptions, data.amsHintOptions);
- settings = ams.executeFunctionByName(data.amsHintInitCallback, hint, settings) || settings;
- var plugin = hint.tipsy(settings);
- ams.executeFunctionByName(data.amsHintAfterInitCallback, hint, plugin, settings);
+ 'jquery-tipsy', function() {
+ hints.each(function () {
+ var hint = $(this);
+ var data = hint.data();
+ var dataOptions = {
+ html: data.amsHintHtml === undefined ? (hint.attr('title') || '').startsWith('<') : data.amsHintHtml,
+ title: ams.getFunctionByName(data.amsHintTitleGetter) || function () {
+ var hint = $(this);
+ var result = hint.attr('original-title') ||
+ hint.attr(data.amsHintTitleAttr || 'title') ||
+ (data.amsHintHtml ? hint.html() : hint.text());
+ result = result.replace(/\?_="/, '?_=' + new Date().getTime() + '"');
+ return result;
+ },
+ opacity: data.amsHintOpacity || 0.95,
+ gravity: data.amsHintGravity || 'sw',
+ offset: data.amsHintOffset || 0
+ };
+ var settings = $.extend({}, dataOptions, data.amsHintOptions);
+ settings = ams.executeFunctionByName(data.amsHintInitCallback, hint, settings) || settings;
+ var plugin = hint.tipsy(settings);
+ ams.executeFunctionByName(data.amsHintAfterInitCallback, hint, plugin, settings);
+ });
});
});
}
@@ -3032,6 +3160,7 @@
if (isChecked) {
if (data.amsCheckerMode === 'disable') {
fieldset.removeAttr('disabled');
+ $('.select2', fieldset).removeAttr('disabled');
} else {
fieldset.removeClass('switched');
}
@@ -3043,6 +3172,7 @@
} else {
if (data.amsCheckerMode === 'disable') {
fieldset.prop('disabled', 'disabled');
+ $('.select2', fieldset).attr('disabled', 'disabled');
} else {
fieldset.addClass('switched');
}
@@ -3066,6 +3196,7 @@
} else {
if (data.amsCheckerMode === 'disable') {
fieldset.attr('disabled', 'disabled');
+ $('.select2', fieldset).attr('disabled', 'disabled');
} else {
fieldset.addClass('switched');
}
@@ -3108,7 +3239,9 @@
var draggable = $(this);
var data = draggable.data();
var dataOptions = {
+ cursor: data.amsDraggableCursor || 'move',
containment: data.amsDraggableContainment,
+ connectToSortable: data.amsDraggableConnectSortable,
helper: ams.getFunctionByName(data.amsDraggableHelper) || data.amsDraggableHelper,
start: ams.getFunctionByName(data.amsDraggableStart),
stop: ams.getFunctionByName(data.amsDraggableStop)
@@ -3123,6 +3256,27 @@
},
/**
+ * Droppable plug-in
+ */
+ droppable: function(element) {
+ var droppables = $('.droppable', element);
+ if (droppables.length > 0) {
+ droppables.each(function() {
+ var droppable = $(this);
+ var data = droppable.data();
+ var dataOptions = {
+ accept: data.amsdroppableAccept,
+ drop: ams.getFunctionByName(data.amsDroppableDrop)
+ };
+ var settings = $.extend({}, dataOptions, data.amsDroppableOptions);
+ settings = ams.executeFunctionByName(data.amsDroppableInitCallback, droppable, settings) || settings;
+ var plugin = droppable.droppable(settings);
+ ams.executeFunctionByName(data.amsDroppableAfterInitCallback, droppable, plugin, settings);
+ });
+ }
+ },
+
+ /**
* Sortable plug-in
*/
sortable: function(element) {
@@ -3200,17 +3354,85 @@
},
/**
+ * Treeview plug-in
+ */
+ treeview: function(element) {
+ var treeviews = $('.treeview', element);
+ if (treeviews.length > 0) {
+ ams.ajax.check($.fn.treview,
+ ams.baseURL + 'ext/bootstrap-treeview' + ams.devext + '.js',
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/bootstrap-treeview' + ams.devext + '.css',
+ 'bootstrap-treeview',
+ function() {
+ treeviews.each(function () {
+ var treeview = $(this);
+ var data = treeview.data();
+ var dataOptions = {
+ data: data.amsTreeviewData,
+ levels: data.amsTreeviewLevels,
+ injectStyle: data.amsTreeviewInjectStyle,
+ expandIcon: data.amsTreeviewExpandIcon || 'fa fa-fw fa-plus-square-o',
+ collapseIcon: data.amsTreeviewCollaspeIcon || 'fa fa-fw fa-minus-square-o',
+ emptyIcon: data.amsTreeviewEmptyIcon || 'fa fa-fw',
+ nodeIcon: data.amsTreeviewNodeIcon,
+ selectedIcon: data.amsTreeviewSelectedIcon,
+ checkedIcon: data.amsTreeviewCheckedIcon || 'fa fa-fw fa-check-square-o',
+ uncheckedIcon: data.amsTreeviewUncheckedIcon || 'fa fa-fw fa-square-o',
+ color: data.amsTreeviewColor,
+ backColor: data.amsTreeviewBackColor,
+ borderColor: data.amsTreeviewBorderColor,
+ onHoverColor: data.amsTreeviewHoverColor,
+ selectedColor: data.amsTreeviewSelectedColor,
+ selectedBackColor: data.amsTreeviewSelectedBackColor,
+ unselectableColor: data.amsTreeviewUnselectableColor || 'rgba(1,1,1,0.25)',
+ unselectableBackColor: data.amsTreeviewUnselectableBackColor || 'rgba(1,1,1,0.25)',
+ enableLinks: data.amsTreeviewEnableLinks,
+ highlightSelected: data.amsTreeviewHighlightSelected,
+ highlightSearchResults: data.amsTreeviewhighlightSearchResults,
+ showBorder: data.amsTreeviewShowBorder,
+ showIcon: data.amsTreeviewShowIcon,
+ showCheckbox: data.amsTreeviewShowCheckbox,
+ showTags: data.amsTreeviewShowTags,
+ toggleUnselectable: data.amsTreeviewToggleUnselectable,
+ multiSelect: data.amsTreeviewMultiSelect,
+ onNodeChecked: ams.getFunctionByName(data.amsTreeviewNodeChecked),
+ onNodeCollapsed: ams.getFunctionByName(data.amsTreeviewNodeCollapsed),
+ onNodeDisabled: ams.getFunctionByName(data.amsTreeviewNodeDisabled),
+ onNodeEnabled: ams.getFunctionByName(data.amsTreeviewNodeEnabled),
+ onNodeExpanded: ams.getFunctionByName(data.amsTreeviewNodeExpanded),
+ onNodeSelected: ams.getFunctionByName(data.amsTreeviewNodeSelected),
+ onNodeUnchecked: ams.getFunctionByName(data.amsTreeviewNodeUnchecked),
+ onNodeUnselected: ams.getFunctionByName(data.amsTreeviewNodeUnselected),
+ onSearchComplete: ams.getFunctionByName(data.amsTreeviewSearchComplete),
+ onSearchCleared: ams.getFunctionByName(data.amsTreeviewSearchCleared)
+ };
+ var settings = $.extend({}, dataOptions, data.amsTreeviewOptions);
+ settings = ams.executeFunctionByName(data.amsTreeviewInitcallback, treeview, settings) || settings;
+ var plugin = treeview.treeview(settings);
+ ams.executeFunctionByName(data.amsTreeviewAfterInitCallback, treeview, plugin, settings);
+ });
+ });
+ });
+ }
+ },
+
+ /**
* Select2 plug-in
*/
select2: function(element) {
var selects = $('.select2', element);
if (selects.length > 0) {
ams.ajax.check($.fn.select2,
- ams.baseURL + 'ext/jquery-select2-3.5.2' + ams.devext + '.js',
+ ams.baseURL + 'ext/jquery-select2-3.5.4' + ams.devext + '.js',
function() {
selects.each(function() {
var select = $(this);
var data = select.data();
+ if (data.select2) {
+ // Already initialized
+ return;
+ }
var dataOptions = {
placeholder: data.amsSelect2Placeholder,
multiple: data.amsSelect2Multiple,
@@ -3459,26 +3681,29 @@
ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
function(first_load) {
if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
}
- datepickers.each(function() {
- var input = $(this);
- var data = input.data();
- var dataOptions = {
- lang: data.amsDatetimepickerLang || ams.lang,
- format: data.amsDatetimepickerFormat || 'd/m/y',
- datepicker: true,
- dayOfWeekStart: 1,
- timepicker: false,
- closeOnDateSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
- weeks: data.amsDatetimepickerWeeks
- };
- var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
- settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
- var plugin = input.datetimepicker(settings);
- ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
- });
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css',
+ 'jquery-datetimepicker',
+ function () {
+ datepickers.each(function () {
+ var input = $(this);
+ var data = input.data();
+ var dataOptions = {
+ lang: data.amsDatetimepickerLang || ams.lang,
+ format: data.amsDatetimepickerFormat || 'd/m/y',
+ datepicker: true,
+ dayOfWeekStart: 1,
+ timepicker: false,
+ closeOnDateSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
+ weeks: data.amsDatetimepickerWeeks
+ };
+ var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
+ settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
+ var plugin = input.datetimepicker(settings);
+ ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
+ });
+ });
});
}
},
@@ -3493,27 +3718,30 @@
ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
function(first_load) {
if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
}
- datetimepickers.each(function() {
- var input = $(this);
- var data = input.data();
- var dataOptions = {
- lang: data.amsDatetimepickerLang || ams.lang,
- format: data.amsDatetimepickerFormat || 'd/m/y H:i',
- datepicker: true,
- dayOfWeekStart: 1,
- timepicker: true,
- closeOnDateSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
- closeOnTimeSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
- weeks: data.amsDatetimepickerWeeks
- };
- var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
- settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
- var plugin = input.datetimepicker(settings);
- ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
- });
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css',
+ 'jquery-datetimepicker',
+ function () {
+ datetimepickers.each(function () {
+ var input = $(this);
+ var data = input.data();
+ var dataOptions = {
+ lang: data.amsDatetimepickerLang || ams.lang,
+ format: data.amsDatetimepickerFormat || 'd/m/y H:i',
+ datepicker: true,
+ dayOfWeekStart: 1,
+ timepicker: true,
+ closeOnDateSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
+ closeOnTimeSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect,
+ weeks: data.amsDatetimepickerWeeks
+ };
+ var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
+ settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
+ var plugin = input.datetimepicker(settings);
+ ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
+ });
+ });
});
}
},
@@ -3528,24 +3756,27 @@
ams.baseURL + 'ext/jquery-datetimepicker' + ams.devext + '.js',
function(first_load) {
if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css', 'jquery-datetimepicker');
ams.dialog.registerHideCallback(ams.helpers.datetimepickerDialogHiddenCallback);
}
- timepickers.each(function() {
- var input = $(this);
- var data = input.data();
- var dataOptions = {
- lang: data.amsDatetimepickerLang || ams.lang,
- format: data.amsDatetimepickerFormat || 'H:i',
- datepicker: false,
- timepicker: true,
- closeOnTimeSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect
- };
- var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
- settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
- var plugin = input.datetimepicker(settings);
- ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
- });
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-datetimepicker' + ams.devext + '.css',
+ 'jquery-datetimepicker',
+ function() {
+ timepickers.each(function () {
+ var input = $(this);
+ var data = input.data();
+ var dataOptions = {
+ lang: data.amsDatetimepickerLang || ams.lang,
+ format: data.amsDatetimepickerFormat || 'H:i',
+ datepicker: false,
+ timepicker: true,
+ closeOnTimeSelect: data.amsDatetimepickerCloseOnSelect === undefined ? true : data.amsDatetimepickerCloseOnSelect
+ };
+ var settings = $.extend({}, dataOptions, data.amsDatetimepickerOptions);
+ settings = ams.executeFunctionByName(data.amsDatetimepickerInitCallback, input, settings) || settings;
+ var plugin = input.datetimepicker(settings);
+ ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
+ });
+ });
});
}
},
@@ -3558,21 +3789,52 @@
if (colorpickers.length > 0) {
ams.ajax.check($.fn.minicolors,
ams.baseURL + 'ext/jquery-minicolors' + ams.devext + '.js',
- function(first_load) {
- if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-minicolors' + ams.devext + '.css', 'jquery-minicolors');
- }
- colorpickers.each(function() {
- var input = $(this);
- var data = input.data();
- var dataOptions = {
- position: data.amsColorpickerPosition || input.closest('label.input').data('ams-colorpicker-position') || 'bottom left'
- };
- var settings = $.extend({}, dataOptions, data.amsColorpickerOptions);
- settings = ams.executeFunctionByName(data.amsColorpickerInitCallback, input, settings) || settings;
- var plugin = input.minicolors(settings);
- ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
- });
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-minicolors' + ams.devext + '.css',
+ 'jquery-minicolors',
+ function () {
+ colorpickers.each(function () {
+ var input = $(this);
+ var data = input.data();
+ var dataOptions = {
+ position: data.amsColorpickerPosition || input.closest('label.input').data('ams-colorpicker-position') || 'bottom left'
+ };
+ var settings = $.extend({}, dataOptions, data.amsColorpickerOptions);
+ settings = ams.executeFunctionByName(data.amsColorpickerInitCallback, input, settings) || settings;
+ var plugin = input.minicolors(settings);
+ ams.executeFunctionByName(data.amsDatetimepickerAfterInitCallback, input, plugin, settings);
+ });
+ });
+ });
+ }
+ },
+
+ /**
+ * Drag & drop upload plug-in
+ */
+ dndupload: function(element) {
+ var uploads = $('.dndupload', element);
+ if (uploads.length > 0) {
+ ams.ajax.check($.fn.dndupload,
+ ams.baseURL + 'ext/jquery-dndupload' + ams.devext + '.js',
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-dndupload' + ams.devext + '.css',
+ 'jquery-dndupload',
+ function () {
+ uploads.each(function () {
+ var upload = $(this);
+ var data = upload.data();
+ var dataOptions = {
+ action: data.amsDnduploadAction || upload.attr('action') || 'upload-files',
+ fieldname: data.amsDnduploadFieldname || 'files',
+ autosubmit: data.amsDnduploadAutosubmit
+ };
+ var settings = $.extend({}, dataOptions, data.amsDnduploadOptions);
+ settings = ams.executeFunctionByName(data.amsDnduploadInitCallback, upload, settings) || settings;
+ var plugin = upload.dndupload(settings);
+ ams.executeFunctionByName(data.amsDnduploadAfterInitcallback, upload, plugin, settings);
+ });
+ });
});
}
},
@@ -3680,7 +3942,7 @@
if (tables.length > 0) {
ams.ajax.check($.fn.dataTable,
ams.baseURL + 'ext/jquery-dataTables-1.9.4' + ams.devext + '.js',
- function(first_load) {
+ function() {
ams.ajax.check($.fn.dataTableExt.oPagination.bootstrap_full,
ams.baseURL + 'myams-dataTables' + ams.devext + '.js',
function() {
@@ -3724,8 +3986,10 @@
var sortable = sortables[index];
if (sortable !== undefined) {
column = columns[index] || {};
- column.bSortable = sortable;
+ column.bSortable = typeof(sortable) === 'string' ? JSON.parse(sortable) : sortable;
columns[index] = column;
+ } else {
+ columns[index] = columns[index] || {};
}
}
// Check columns types
@@ -3736,11 +4000,16 @@
column = columns[index] || {};
column.sType = sortType;
columns[index] = column;
+ } else {
+ columns[index] = columns[index] || {};
}
}
// Set options
var dataOptions = {
bJQueryUI: false,
+ bServerSide: data.amsDatatableServerSide || false,
+ sAjaxSource: data.amsDatatableServerSide === true ? data.amsDatatableAjaxSource : undefined,
+ sServerMethod: data.amsDatatableServerSide === true ? 'POST' : undefined,
bFilter: data.amsDatatableGlobalFilter !== false || extensions.indexOf('columnfilter') >= 0,
bPaginate: data.amsDatatablePagination !== false,
bInfo: data.amsDatatableInfo !== false,
@@ -3812,7 +4081,7 @@
sources.push(ams.baseURL + 'ext/jquery-dataTables-keyTable' + ams.devext + '.js');
break;
case 'rowgrouping':
- checkers.push($.fn.rowGrouping());
+ checkers.push($.fn.rowGrouping);
sources.push(ams.baseURL + 'ext/jquery-dataTables-rowGrouping' + ams.devext + '.js');
break;
case 'rowreordering':
@@ -3934,7 +4203,7 @@
if (tables.length > 0) {
ams.ajax.check($.fn.tableDnD,
ams.baseURL + 'ext/jquery-tablednd' + ams.devext + '.js',
- function(first_load) {
+ function() {
tables.each(function() {
var table = $(this);
var data = table.data();
@@ -3958,29 +4227,32 @@
if (target) {
// Disable row click handler
$(row).data('ams-disabled-handlers', 'click');
- var rows = [];
- $(dnd_table.rows).each(function() {
- var rowId = $(this).data('ams-element-name');
- if (rowId) {
- rows.push(rowId);
+ try {
+ var rows = [];
+ $(dnd_table.rows).each(function() {
+ var rowId = $(this).data('ams-element-name');
+ if (rowId) {
+ rows.push(rowId);
+ }
+ });
+ var localTarget = ams.getFunctionByName(target);
+ if (typeof(localTarget) === 'function') {
+ localTarget.call(table, dnd_table, rows);
+ } else {
+ if (!target.startsWith(window.location.protocol)) {
+ var location = data.amsLocation;
+ if (location) {
+ target = location + '/' + target;
+ }
+ }
+ ams.ajax.post(target, {names: JSON.stringify(rows)});
}
- });
- var localTarget = ams.getFunctionByName(target);
- if (typeof(localTarget) === 'function') {
- localTarget.call(table, dnd_table, rows);
- } else {
- if (!target.startsWith(window.location.protocol)) {
- var location = data.amsLocation;
- if (location) {
- target = location + '/' + target;
- }
- }
- ams.ajax.post(target, {names: JSON.stringify(rows)});
+ } finally {
+ // Restore row click handler
+ setTimeout(function() {
+ $(row).removeData('ams-disabled-handlers');
+ }, 50);
}
- // Restore row click handler
- setTimeout(function() {
- $(row).removeData('ams-disabled-handlers');
- }, 50);
}
return false;
}
@@ -4002,7 +4274,7 @@
if (wizards.length > 0) {
ams.ajax.check($.fn.bootstrapWizard,
ams.baseURL + 'ext/bootstrap-wizard-1.4.2' + ams.devext + '.js',
- function(first_load) {
+ function() {
wizards.each(function() {
var wizard = $(this);
var data = wizard.data();
@@ -4064,14 +4336,22 @@
var dataOptions = {
theme: data.amsTinymceTheme || "modern",
language: ams.lang,
- plugins: [
- "advlist autosave autolink lists link image charmap print preview hr anchor pagebreak",
+ menubar: data.amsTinymceMenubar !== false,
+ statusbar: data.amsTinymceStatusbar !== false,
+ plugins: data.amsTinymcePlugins || [
+ "advlist autosave autolink lists link charmap print preview hr anchor pagebreak",
"searchreplace wordcount visualblocks visualchars code fullscreen",
- "insertdatetime media nonbreaking save table contextmenu directionality",
+ "insertdatetime nonbreaking save table contextmenu directionality",
"emoticons paste textcolor colorpicker textpattern autoresize"
],
- toolbar1: data.amsTinymceToolbar1 || "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent",
- toolbar2: data.amsTinymceToolbar2 || "forecolor backcolor emoticons | charmap link image media | fullscreen preview print | code",
+ toolbar: data.amsTinymceToolbar,
+ toolbar1: data.amsTinymceToolbar1 === false ? false : data.amsTinymceToolbar1 ||
+ "undo redo | styleselect | bold italic | alignleft " +
+ "aligncenter alignright alignjustify | bullist numlist " +
+ "outdent indent",
+ toolbar2: data.amsTinymceToolbar2 === false ? false : data.amsTinymceToolbar2 ||
+ "forecolor backcolor emoticons | charmap link image media | " +
+ "fullscreen preview print | code",
content_css: data.amsTinymceContentCss,
formats: data.amsTinymceFormats,
style_formats: data.amsTinymceStyleFormats,
@@ -4091,6 +4371,9 @@
if (data.amsTinymceExternalPlugins) {
var names = data.amsTinymceExternalPlugins.split(/\s+/);
for (var index in names) {
+ if (!names.hasOwnProperty(index)) {
+ continue;
+ }
var pluginSrc = editor.data('ams-tinymce-plugin-' + names[index]);
tinymce.PluginManager.load(names[index], ams.getSource(pluginSrc));
}
@@ -4124,44 +4407,45 @@
if (images.length > 0) {
ams.ajax.check($.fn.imgAreaSelect,
ams.baseURL + 'ext/jquery-imgareaselect-0.9.11-rc1' + ams.devext + '.js',
- function(first_load) {
- if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-imgareaselect' + ams.devext + '.css');
- }
- images.each(function() {
- var image = $(this);
- var data = image.data();
- var parent = data.amsImgareaselectParent ? image.parents(data.amsImgareaselectParent) : 'body';
- var dataOptions = {
- instance: true,
- handles: true,
- parent: parent,
- x1: data.amsImgareaselectX1 || 0,
- y1: data.amsImgareaselectY1 || 0,
- x2: data.amsImgareaselectX2 || data.amsImgareaselectImageWidth,
- y2: data.amsImgareaselectY2 || data.amsImgareaselectImageHeight,
- imageWidth: data.amsImgareaselectImageWidth,
- imageHeight: data.amsImgareaselectImageHeight,
- minWidth: 128,
- minHeight: 128,
- aspectRatio: data.amsImgareaselectRatio,
- onSelectEnd: ams.getFunctionByName(data.amsImgareaselectSelectEnd) || function(img, selection) {
- var target = data.amsImgareaselectTargetField || 'image_';
- $('input[name="' + target + 'x1"]', parent).val(selection.x1);
- $('input[name="' + target + 'y1"]', parent).val(selection.y1);
- $('input[name="' + target + 'x2"]', parent).val(selection.x2);
- $('input[name="' + target + 'y2"]', parent).val(selection.y2);
- }
- };
- var settings = $.extend({}, dataOptions, data.amsImgareaselectOptions);
- settings = ams.executeFunctionByName(data.amsImgareaselectInitCallback, image, settings) || settings;
- var plugin = image.imgAreaSelect(settings);
- ams.executeFunctionByName(data.amsImgareaselectAfterInitCallback, image, plugin, settings);
- // Add update timeout when plug-in is displayed into a modal dialog
- setTimeout(function() {
- plugin.update();
- }, 250);
- });
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-imgareaselect' + ams.devext + '.css',
+ 'jquery-imgareaselect',
+ function() {
+ images.each(function () {
+ var image = $(this);
+ var data = image.data();
+ var parent = data.amsImgareaselectParent ? image.parents(data.amsImgareaselectParent) : 'body';
+ var dataOptions = {
+ instance: true,
+ handles: true,
+ parent: parent,
+ x1: data.amsImgareaselectX1 || 0,
+ y1: data.amsImgareaselectY1 || 0,
+ x2: data.amsImgareaselectX2 || data.amsImgareaselectImageWidth,
+ y2: data.amsImgareaselectY2 || data.amsImgareaselectImageHeight,
+ imageWidth: data.amsImgareaselectImageWidth,
+ imageHeight: data.amsImgareaselectImageHeight,
+ minWidth: 128,
+ minHeight: 128,
+ aspectRatio: data.amsImgareaselectRatio,
+ onSelectEnd: ams.getFunctionByName(data.amsImgareaselectSelectEnd) || function (img, selection) {
+ var target = data.amsImgareaselectTargetField || 'image_';
+ $('input[name="' + target + 'x1"]', parent).val(selection.x1);
+ $('input[name="' + target + 'y1"]', parent).val(selection.y1);
+ $('input[name="' + target + 'x2"]', parent).val(selection.x2);
+ $('input[name="' + target + 'y2"]', parent).val(selection.y2);
+ }
+ };
+ var settings = $.extend({}, dataOptions, data.amsImgareaselectOptions);
+ settings = ams.executeFunctionByName(data.amsImgareaselectInitCallback, image, settings) || settings;
+ var plugin = image.imgAreaSelect(settings);
+ ams.executeFunctionByName(data.amsImgareaselectAfterInitCallback, image, plugin, settings);
+ // Add update timeout when plug-in is displayed into a modal dialog
+ setTimeout(function () {
+ plugin.update();
+ }, 250);
+ });
+ });
});
}
},
@@ -4174,90 +4458,141 @@
if (fancyboxes.length > 0) {
ams.ajax.check($.fn.fancybox,
ams.baseURL + 'ext/jquery-fancybox-2.1.5' + ams.devext + '.js',
- function(first_load) {
- if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-fancybox-2.1.5' + ams.devext + '.css');
- }
- fancyboxes.each(function() {
- var fancybox = $(this);
- var data = fancybox.data();
- var elements = fancybox;
- if (data.amsFancyboxElements) {
- elements = $(data.amsFancyboxElements, fancybox);
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-fancybox-2.1.5' + ams.devext + '.css',
+ 'jquery-fancybox',
+ function() {
+ fancyboxes.each(function () {
+ var fancybox = $(this);
+ var data = fancybox.data();
+ var elements = fancybox;
+ var index,
+ helper;
+ if (data.amsFancyboxElements) {
+ elements = $(data.amsFancyboxElements, fancybox);
+ }
+ var helpers = (data.amsFancyboxHelpers || '').split(/\s+/);
+ if (helpers.length > 0) {
+ for (index = 0; index < helpers.length; index++) {
+ helper = helpers[index];
+ switch (helper) {
+ case 'buttons':
+ ams.ajax.check($.fancybox.helpers.buttons,
+ ams.baseURL + 'ext/fancybox-helpers/fancybox-buttons' + ams.devext + '.js');
+ break;
+ case 'thumbs':
+ ams.ajax.check($.fancybox.helpers.thumbs,
+ ams.baseURL + 'ext/fancybox-helpers/fancybox-thumbs' + ams.devext + '.js');
+ break;
+ case 'media':
+ ams.ajax.check($.fancybox.helpers.media,
+ ams.baseURL + 'ext/fancybox-helpers/fancybox-media' + ams.devext + '.js');
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ var dataOptions = {
+ type: data.amsFancyboxType,
+ padding: data.amsFancyboxPadding || 10,
+ margin: data.amsFancyboxMargin || 10,
+ loop: data.amsFancyboxLoop,
+ beforeLoad: ams.getFunctionByName(data.amsFancyboxBeforeLoad) || function () {
+ var title;
+ if (data.amsFancyboxTitleGetter) {
+ title = ams.executeFunctionByName(data.amsFancyboxTitleGetter, this);
+ }
+ if (!title) {
+ var content = $('*:first', this.element);
+ title = content.attr('original-title') || content.attr('title');
+ if (!title) {
+ title = $(this.element).attr('original-title') || $(this.element).attr('title');
+ }
+ }
+ this.title = title;
+ },
+ afterLoad: ams.getFunctionByName(data.amsFancyboxAfterLoad),
+ helpers: {
+ title: {
+ type: 'inside'
+ }
+ }
+ };
+ if (helpers.length > 0) {
+ for (index = 0; index < helpers.length; index++) {
+ helper = helpers[index];
+ switch (helper) {
+ case 'buttons':
+ dataOptions.helpers.buttons = {
+ position: data.amsFancyboxButtonsPosition || 'top'
+ };
+ break;
+ case 'thumbs':
+ dataOptions.helpers.thumbs = {
+ width: data.amsFancyboxThumbsWidth || 50,
+ height: data.amsFancyboxThumbsHeight || 50
+ };
+ break;
+ case 'media':
+ dataOptions.helpers.media = true;
+ break;
+ }
+ }
+ }
+ var settings = $.extend({}, dataOptions, data.amsFancyboxOptions);
+ settings = ams.executeFunctionByName(data.amsFancyboxInitCallback, fancybox, settings) || settings;
+ var plugin = elements.fancybox(settings);
+ ams.executeFunctionByName(data.amsFancyboxAfterInitCallback, fancybox, plugin, settings);
+ });
+ });
+ });
+ }
+ },
+
+ /**
+ * Flot charts
+ */
+ chart: function(element) {
+ var charts = $('.chart', element);
+ if (charts.length > 0) {
+ ams.ajax.check($.fn.plot,
+ ams.baseURL + 'flot/jquery.flot' + ams.devext + '.js',
+ function() {
+ charts.each(function() {
+
+ function checkPlugin(plugin) {
+ for (var index in $.plot.plugins) {
+ if ($.plot.plugins.hasOwnProperty(index)) {
+ var pluginInfo = $.plot.plugins[index];
+ if (pluginInfo.name === plugin) {
+ return pluginInfo;
+ }
+ }
+ }
+ return null;
}
- var helpers = (data.amsFancyboxHelpers || '').split(/\s+/);
- if (helpers.length > 0) {
- for (var index=0; index < helpers.length; index++) {
- var helper = helpers[index];
- switch (helper) {
- case 'buttons':
- ams.ajax.check($.fancybox.helpers.buttons,
- ams.baseURL + 'ext/fancybox-helpers/fancybox-buttons' + ams.devext + '.js');
- break;
- case 'thumbs':
- ams.ajax.check($.fancybox.helpers.thumbs,
- ams.baseURL + 'ext/fancybox-helpers/fancybox-thumbs' + ams.devext + '.js');
- break;
- case 'media':
- ams.ajax.check($.fancybox.helpers.media,
- ams.baseURL + 'ext/fancybox-helpers/fancybox-media' + ams.devext + '.js');
- break;
- default:
- break;
+
+ var chart = $(this);
+ var data = chart.data();
+ var dataOptions = {};
+ var plugins = (data.amsChartPlugins || '').split(/\s+/);
+ if (plugins.length > 0) {
+ for (var index in plugins) {
+ if (plugins.hasOwnProperty(index)) {
+ var pluginName = plugins[index];
+ if (!checkPlugin(pluginName)) {
+ ams.getScript(ams.baseURL + 'flot/jquery.flot.' + pluginName + ams.devext + '.js');
+ }
}
}
}
- var dataOptions = {
- type: data.amsFancyboxType,
- padding: data.amsFancyboxPadding || 10,
- margin: data.amsFancyboxMargin || 10,
- loop: data.amsFancyboxLoop,
- beforeLoad: ams.getFunctionByName(data.amsFancyboxBeforeLoad) || function() {
- var title;
- if (data.amsFancyboxTitleGetter) {
- title = ams.executeFunctionByName(data.amsFancyboxTitleGetter, this);
- }
- if (!title) {
- var content = $('*:first', this.element);
- title = content.attr('original-title') || content.attr('title');
- if (!title) {
- title = $(this.element).attr('original-title') || $(this.element).attr('title');
- }
- }
- this.title = title;
- },
- afterLoad: ams.getFunctionByName(data.amsFancyboxAfterLoad),
- helpers: {
- title: {
- type: 'inside'
- }
- }
- };
- if (helpers.length > 0) {
- for (index = 0; index < helpers.length; index++) {
- helper = helpers[index];
- switch (helper) {
- case 'buttons':
- dataOptions.helpers.buttons = {
- position: data.amsFancyboxButtonsPosition || 'top'
- };
- break;
- case 'thumbs':
- dataOptions.helpers.thumbs = {
- width: data.amsFancyboxThumbsWidth || 50,
- height: data.amsFancyboxThumbsHeight || 50
- };
- break;
- case 'media':
- dataOptions.helpers.media = true;
- break;
- }
- }
- }
- var settings = $.extend({}, dataOptions, data.amsFancyboxOptions);
- settings = ams.executeFunctionByName(data.amsFancyboxInitCallback, fancybox, settings) || settings;
- var plugin = elements.fancybox(settings);
- ams.executeFunctionByName(data.amsFancyboxAfterInitCallback, fancybox, plugin, settings);
+ var settings = $.extend({}, dataOptions, data.amsChartOptions);
+ settings = ams.executeFunctionByName(data.amsChartInitCallback, chart, settings) || settings;
+ var chartData = data.amsChartData;
+ chartData = ams.executeFunctionByName(data.amsChartInitData, chart, chartData) || chartData;
+ var plugin = chart.plot(chartData, settings);
+ ams.executeFunctionByName(data.amsChartAfterInitCallback, chart, plugin, settings);
});
});
}
@@ -4288,22 +4623,22 @@
function() {
ams.ajax.check($.fn.mCustomScrollbar,
ams.baseURL + 'ext/jquery-mCustomScrollbar' + ams.devext + '.js',
- function(first_load) {
- if (first_load) {
- ams.getCSS(ams.baseURL + '../css/ext/jquery-mCustomScrollbar.css',
- 'jquery-mCustomScrollbar');
- }
- scrollbars.each(function() {
- var scrollbar = $(this);
- var data = scrollbar.data();
- var dataOptions = {
- theme: data.amsScrollbarTheme || 'light'
- };
- var settings = $.extend({}, dataOptions, data.amsScrollbarOptions);
- settings = ams.executeFunctionByName(data.amsScrollbarInitCallback, scrollbar, settings) || settings;
- var plugin = scrollbar.mCustomScrollbar(settings);
- ams.executeFunctionByName(data.amsScrollbarAfterInitCallback, scrollbar, plugin, settings);
- });
+ function() {
+ ams.getCSS(ams.baseURL + '../css/ext/jquery-mCustomScrollbar.css',
+ 'jquery-mCustomScrollbar',
+ function () {
+ scrollbars.each(function () {
+ var scrollbar = $(this);
+ var data = scrollbar.data();
+ var dataOptions = {
+ theme: data.amsScrollbarTheme || 'light'
+ };
+ var settings = $.extend({}, dataOptions, data.amsScrollbarOptions);
+ settings = ams.executeFunctionByName(data.amsScrollbarInitCallback, scrollbar, settings) || settings;
+ var plugin = scrollbar.mCustomScrollbar(settings);
+ ams.executeFunctionByName(data.amsScrollbarAfterInitCallback, scrollbar, plugin, settings);
+ });
+ });
});
});
}
@@ -4483,10 +4818,9 @@
/**
* Delete an element from a container table
*
- * @param element
* @returns {Function}
*/
- deleteElement: function(element) {
+ deleteElement: function() {
return function() {
var link = $(this);
MyAMS.skin.bigBox({
@@ -4496,12 +4830,15 @@
buttons: ams.i18n.BTN_OK_CANCEL
}, function(button) {
if (button === ams.i18n.BTN_OK) {
- var table = link.parents('table').first();
- var location = table.data('ams-location') || '';
var tr = link.parents('tr').first();
+ var table = tr.parents('table').first();
+ var location = tr.data('ams-location') || table.data('ams-location') || '';
+ if (location) {
+ location += '/';
+ }
var deleteTarget = tr.data('ams-delete-target') || table.data('ams-delete-target') || 'delete-element.json';
var objectName = tr.data('ams-element-name');
- MyAMS.ajax.post(location + '/' + deleteTarget, {'object_name': objectName}, function(result, status) {
+ MyAMS.ajax.post(location + deleteTarget, {'object_name': objectName}, function(result, status) {
if (result.status === 'success') {
if (table.hasClass('datatable')) {
table.dataTable().fnDeleteRow(tr[0]);
@@ -4518,6 +4855,251 @@
}
});
};
+ },
+
+ /**
+ * Switch element visibility
+ */
+ switchElementVisibility: function() {
+ return function() {
+ var source = $(this);
+ var element = source.parents('tr').first();
+ var container = element.parents('table');
+ ams.ajax.post(container.data('ams-location') + '/' +
+ container.data('ams-visibility-switcher'),
+ {object_name: element.data('ams-element-name')},
+ function(result, status) {
+ if (result.visible) {
+ $('i', source).attr('class', 'fa fa-fw fa-eye');
+ } else {
+ $('i', source).attr('class', 'fa fa-fw fa-eye-slash text-danger');
+ }
+ });
+ }
+ }
+ };
+
+
+ /**
+ * Tree management
+ */
+ MyAMS.tree = {
+
+ /**
+ * Open close tree node inside a table
+ */
+ switchTableNode: function() {
+
+ function removeChildNodes(node_id) {
+ $('tr[data-ams-tree-node-parent-id="' + node_id + '"]').each(function() {
+ var row = $(this);
+ removeChildNodes(row.data('ams-tree-node-id'));
+ row.remove();
+ })
+ }
+
+ var node = $(this);
+ var switcher = $('i.switch', node);
+ var tr = node.parents('tr').first();
+ var table = tr.parents('table').first();
+ if (switcher.hasClass('fa-minus-square-o')) {
+ removeChildNodes(tr.data('ams-tree-node-id'));
+ switcher.removeClass('fa-minus-square-o')
+ .addClass('fa-plus-square-o');
+ } else {
+ var location = tr.data('ams-location') || table.data('ams-location') || '';
+ var treeNodesTarget = tr.data('ams-tree-nodes-target') || table.data('ams-tree-nodes-target') || 'get-tree-nodes.json';
+ var sourceName = tr.data('ams-element-name');
+ switcher.removeClass('fa-plus-square-o')
+ .addClass('fa-cog fa-spin');
+ MyAMS.ajax.post(location + '/' + sourceName + '/' + treeNodesTarget, {
+ can_sort: !$('td.sorter', tr).is(':empty')
+ }, function(result, status) {
+ if (result.length > 0) {
+ var old_row = tr;
+ for (var index = 0; index < result.length; index++) {
+ var new_row = $(result[index]);
+ new_row.insertAfter(old_row)
+ .addClass('no-drag-handle');
+ ams.initContent(new_row);
+ old_row = new_row;
+ }
+ if (table.hasClass('table-dnd')) {
+ table.tableDnDUpdate();
+ }
+ }
+ switcher.removeClass('fa-cog fa-spin')
+ .addClass('fa-minus-square-o');
+ });
+ }
+ },
+
+ /**
+ * Open close all tree nodes
+ */
+ switchTree: function() {
+ var th = $(this);
+ var switcher = $('i.switch', th);
+ var table = $(this).parents('table').first();
+ var tableID = table.data('ams-tree-node-id');
+ if (switcher.hasClass('fa-minus-square-o')) {
+ $('tr[data-ams-tree-node-parent-id]').filter('tr[data-ams-tree-node-parent-id!="' + tableID + '"]').remove();
+ $('i.switch', table).removeClass('fa-minus-square-o')
+ .addClass('fa-plus-square-o');
+ } else {
+ var tr = $('tbody tr', table).first();
+ var location = table.data('ams-location') || '';
+ var target = table.data('ams-tree-nodes-target') || 'get-tree.json';
+ switcher.removeClass('fa-plus-square-o')
+ .addClass('fa-cog fa-spin');
+ MyAMS.ajax.post(location + '/' + target, {
+ can_sort: !$('td.sorter', tr).is(':empty')
+ }, function(result, status) {
+ $('tr[data-ams-tree-node-id]', table).remove();
+ var old_row = null;
+ for (var index = 0; index < result.length; index++) {
+ var new_row = $(result[index]);
+ if (old_row === null) {
+ new_row.appendTo($('tbody', table));
+ } else {
+ new_row.insertAfter(old_row);
+ }
+ new_row.addClass('no-drag-handle');
+ ams.initContent(new_row);
+ old_row = new_row;
+ }
+ if (table.hasClass('table-dnd')) {
+ table.tableDnDUpdate();
+ }
+ $('i.switch', table).removeClass('fa-plus-square-o')
+ .addClass('fa-minus-square-o');
+ switcher.removeClass('fa-cog fa-spin')
+ .addClass('fa-minus-square-o');
+ });
+ }
+ },
+
+ /**
+ * Sort and re-parent tree elements
+ */
+ sortTree: function(dnd_table, row) {
+ var data = $(dnd_table).data();
+ var target = data.amsTabledndDropTarget;
+ if (target) {
+ // Disable row click handler
+ row = $(row);
+ row.data('ams-disabled-handlers', 'click');
+ try {
+ // Get root ID
+ var tableID = row.parents('table').first().data('ams-tree-node-id');
+ // Get moved row ID
+ var rowID = row.data('ams-tree-node-id');
+ var rowParentID = row.data('ams-tree-node-parent-id');
+ // Get new parent ID
+ var parent = row.prev('tr');
+ if (parent.exists()) {
+ // Move below an existing row
+ var parentID = parent.data('ams-tree-node-id');
+ // Check switcher state
+ var switcher = $('.switch', parent);
+ if (switcher.hasClass('fa-minus-square-o')) {
+ // Opened folder: move as child
+ if (rowParentID === parentID) {
+ // Don't change parent
+ var action = 'reorder';
+ } else {
+ // Change parent
+ action = 'reparent';
+ }
+ } else {
+ // Closed folder or simple item: move as sibling
+ parentID = parent.data('ams-tree-node-parent-id');
+ if (rowParentID === parentID) {
+ // Don't change parent
+ action = 'reorder';
+ } else {
+ // Change parent
+ action = 'reparent';
+ }
+ }
+ } else {
+ // Move to site root
+ parentID = tableID;
+ switcher = null;
+ if (rowParentID === parentID) {
+ // Already child of site root
+ action = 'reorder';
+ } else {
+ // Move from inner folder to site root
+ action = 'reparent';
+ }
+ }
+ // Call ordering target
+ var localTarget = ams.getFunctionByName(target);
+ if (typeof(localTarget) === 'function') {
+ localTarget.call(table, dnd_table, post_data);
+ } else {
+ if (!target.startsWith(window.location.protocol)) {
+ var location = data.amsLocation;
+ if (location) {
+ target = location + '/' + target;
+ }
+ }
+ var post_data = {
+ action: action,
+ child: rowID,
+ parent: parentID,
+ order: JSON.stringify($('tr[data-ams-tree-node-id]').listattr('data-ams-tree-node-id')),
+ can_sort: !$('td.sorter', row).is(':empty')
+ };
+ ams.ajax.post(target, post_data, function(result) {
+
+ function removeChildRows(rowID) {
+ var childs = $('tr[data-ams-tree-node-parent-id="' + rowID + '"]');
+ childs.each(function() {
+ var childRow = $(this);
+ var childID = childRow.attr('data-ams-tree-node-id');
+ removeChildRows(childID);
+ childRow.remove();
+ });
+ }
+
+ if (result.status) {
+ ams.ajax.handleJSON(result);
+ } else {
+ // Remove moved row childrens
+ var body = $(row).parents('tbody').first();
+ removeChildRows(rowID);
+ if (post_data.action === 'reparent') {
+ // Remove new parent childrens
+ removeChildRows(parentID);
+ row.remove();
+ var old_row = $('tr[data-ams-tree-node-id="' + parentID + '"]');
+ for (var index = 0; index < result.length; index++) {
+ var new_row = $(result[index]);
+ if (old_row.exists()) {
+ new_row.insertAfter(old_row)
+ .addClass('no-drag-handle');
+ } else {
+ new_row.prependTo(body)
+ .addClass('no-drag-handle');
+ }
+ ams.initContent(new_row);
+ old_row = new_row;
+ }
+ }
+ $('tr').parents('table').tableDnDUpdate();
+ }
+ });
+ }
+ } finally {
+ // Restore row click handler
+ setTimeout(function() {
+ $(row).removeData('ams-disabled-handlers');
+ }, 50);
+ }
+ }
+ return false;
}
};
@@ -4595,6 +5177,89 @@
},
/**
+ * Replace given form with new content
+ */
+ refreshContent: function(changes) {
+ var target = $('[id="' + changes.object_id + '"]');
+ target.replaceWith($(changes.content));
+ target = $('[id="' + changes.object_id + '"]');
+ MyAMS.initContent(target);
+ return target;
+ },
+
+ /**
+ * Replace given widget with given content
+ */
+ refreshWidget: function(changes) {
+ var target = $('[id="' + changes.parent_id + '"]');
+ var widget = $('[name="' + changes.widget_name + '"]', target);
+ if (!widget.exists()) {
+ widget = $('[name="' + changes.widget_name + ':list"]', target);
+ }
+ var label = widget.parents('label.input').last();
+ label.html(changes.content);
+ MyAMS.initContent(label);
+ return label;
+ },
+
+ /**
+ * Replace given table with new content
+ */
+ refreshTable: function(changes) {
+ var widget = $('[id="' + changes.object_id + '"]').parents('.ams-widget:first');
+ widget.replaceWith($(changes.table));
+ widget = $('[id="' + changes.object_id + '"]').parents('.ams-widget:first');
+ MyAMS.initContent(widget);
+ return widget;
+ },
+
+ /**
+ * Replace given table with new content
+ * If table is located inside a switched fieldset, fieldset is opened
+ *
+ * @param changes
+ */
+ refreshSwitchedTable: function(changes) {
+ var widget = ams.skin.refreshTable(changes);
+ var legend = widget.siblings('legend');
+ if (legend.parents('fieldset:first').hasClass('switched')) {
+ legend.click();
+ }
+ },
+
+ /**
+ * Replace given row with new content
+ */
+ refreshRow: function(changes) {
+ var tr = $('tr[id="' + changes.object_id + '"]');
+ var table = tr.parents('table').first();
+ var new_tr = $(changes.row);
+ tr.replaceWith(new_tr);
+ MyAMS.initContent(new_tr);
+ if (table.hasClass('table-dnd')) {
+ new_tr.addClass('no-drag-handle');
+ table.tableDnDUpdate();
+ }
+ return new_tr;
+ },
+
+ /**
+ * Replace given row cell with new content
+ */
+ refreshRowCell: function(changes) {
+ var tr = $('tr[id="' + changes.object_id + '"]');
+ var table = tr.parents('table').first();
+ var headRow = $('tr', $('thead', table));
+ var headCell = $('th[data-ams-column-name="' + changes.col_name + '"]', headRow);
+ var index = $('th', headRow).index(headCell);
+ if (index > -1) {
+ var cell = $($('td', tr).get(index));
+ cell.html(changes.cell);
+ MyAMS.initContent(cell);
+ }
+ },
+
+ /**
* Initialize desktop and mobile widgets
*/
_initDesktopWidgets: function(element) {
@@ -4661,13 +5326,9 @@
content += '</ul>';
}
content += '</div>';
- var alert = $(content).prependTo(parent);
+ $(content).insertBefore(parent);
if (parent.exists) {
- ams.ajax.check($.scrollTo,
- ams.baseURL + 'ext/jquery-scrollTo.min.js',
- function() {
- $.scrollTo(parent, {offset: {top: -50}});
- });
+ ams.skin.scrollTo(parent, {offset: {top: -50}});
}
},
@@ -4745,6 +5406,29 @@
},
/**
+ * Scroll to given element
+ *
+ * @param element: the element to which to scroll
+ * @param options: scroll options
+ */
+ scrollTo: function(element, options) {
+ ams.ajax.check($.scrollTo,
+ ams.baseURL + 'ext/jquery-scrollto-2.1.2' + ams.devext + '.js',
+ function() {
+ var body = $('body');
+ var offset = options.offset || 0;
+ if (body.hasClass('fixed-header')) {
+ offset -= $('#header').height();
+ }
+ if (body.hasClass('fixed-ribbon')) {
+ offset -= $('#ribbon').height();
+ }
+ options = $.extend({}, options, {offset: offset});
+ $.scrollTo(element, options);
+ });
+ },
+
+ /**
* Initialize breadcrumbs based on active menu position
*/
_drawBreadCrumb: function() {
@@ -4934,10 +5618,13 @@
ams.stats.logPageview();
}
},
- error: function(request, options, error) {
+ error: function(request, errorOptions, error) {
container.html('<h3 class="error"><i class="fa fa-warning txt-color-orangeDark"></i> ' +
ams.i18n.ERROR + error + '</h3>' +
request.responseText);
+ if (options && options.afterErrorCallback) {
+ ams.executeFunctionByName(options.afterErrorCallback, this);
+ }
},
async: options.async === undefined ? true : options.async
};
@@ -4948,7 +5635,7 @@
/**
* Change user language
*/
- setLanguage: function(options) {
+ setLanguage: function(event, options) {
var lang = options.lang;
var handlerType = options.handler_type || 'json';
switch (handlerType) {
@@ -5067,7 +5754,7 @@
}
// Hide menu button
- $('#hide-menu >:first-child > A').click(function(e) {
+ $('#hide-menu').find('>:first-child >A').click(function(e) {
body.toggleClass("hidden-menu");
e.preventDefault();
});
@@ -5326,7 +6013,7 @@
});
// Initialize custom click handlers
- $(document).on('click', '[data-ams-click-handler]', function(e) {
+ $(document).on('click', '[data-ams-click-handler]', function(event) {
var source = $(this);
var handlers = source.data('ams-disabled-handlers');
if ((handlers === true) || (handlers === 'click') || (handlers === 'all')) {
@@ -5335,20 +6022,20 @@
var data = source.data();
if (data.amsClickHandler) {
if ((data.amsStopPropagation === true) || (data.amsClickStopPropagation === true)) {
- e.stopPropagation();
+ event.stopPropagation();
}
if (data.amsClickKeepDefault !== true) {
- e.preventDefault();
+ event.preventDefault();
}
var callback = ams.getFunctionByName(data.amsClickHandler);
if (callback !== undefined) {
- callback.call(source, data.amsClickHandlerOptions);
+ callback.call(source, event, data.amsClickHandlerOptions);
}
}
});
// Initialize custom change handlers
- $(document).on('change', '[data-ams-change-handler]', function(e) {
+ $(document).on('change', '[data-ams-change-handler]', function(event) {
var source = $(this);
// Disable change handlers for readonly inputs
// These change handlers are activated by IE!!!
@@ -5361,16 +6048,26 @@
}
var data = source.data();
if (data.amsChangeHandler) {
+ if ((data.amsStopPropagation === true) || (data.amsChangeStopPropagation === true)) {
+ event.stopPropagation();
+ }
if (data.amsChangeKeepDefault !== true) {
- e.preventDefault();
+ event.preventDefault();
}
var callback = ams.getFunctionByName(data.amsChangeHandler);
if (callback !== undefined) {
- callback.call(source, data.amsChangeHandlerOptions);
+ callback.call(source, event, data.amsChangeHandlerOptions);
}
}
});
+ // Submit form when CTRL+Enter key is pressed in textarea
+ $(document).on('keydown', 'textarea', function(e) {
+ if ((e.keyCode === 10 || e.keyCode === 13) && (e.ctrlKey || e.metaKey)) {
+ $(this).closest('form').submit();
+ }
+ });
+
// Notify reset to update Select2 widgets
$(document).on('reset', 'form', function(e) {
var form = $(this);
@@ -5380,7 +6077,10 @@
$('INPUT.select2[type="hidden"]', form).each(function() {
var input = $(this);
var select = input.data('select2');
- input.select2('val', input.data('ams-select2-input-value').split(select.opts.separator));
+ var value = input.data('ams-select2-input-value');
+ if (value) {
+ input.select2('val', value.split(select.opts.separator));
+ }
});
form.find('.select2').trigger('change');
$('[data-ams-reset-callback]', form).each(function() {
@@ -5410,6 +6110,12 @@
}
});
+ // Initialize custom event on click
+ $(document).on('click', '[data-ams-click-event]', function(e) {
+ var source = $(this);
+ $(e.target).trigger(source.data('ams-click-event'), source.data('ams-click-event-options'));
+ });
+
// Handle update on file upload placeholder
$(document).on('change', 'input[type="file"]', function(e) {
e.preventDefault();
@@ -5420,6 +6126,11 @@
}
});
+ // Always blur readonly inputs
+ $(document).on('focus', 'input[readonly="readonly"]', function() {
+ $(this).blur();
+ });
+
// Prevent bootstrap dialog from blocking TinyMCE focus
$(document).on('focusin', function(e) {
if ($(e.target).closest('.mce-window').length) {
@@ -5428,32 +6139,52 @@
});
// Disable clicks on disabled tabs
- $("a[data-toggle=tab]", ".nav-tabs").on("click", function(e) {
+ $(document).on("click", '.nav-tabs a[data-toggle=tab]', function(e) {
if ($(this).parent('li').hasClass("disabled")) {
e.preventDefault();
return false;
}
});
+ // Automatically set orientation of dropdown menus
+ $(document).on('show.bs.dropdown', '.btn-group', function() {
+ var menu = $(this);
+ var ul = menu.children('.dropdown-menu');
+ var menuRect = menu.get(0).getBoundingClientRect();
+ var position = menuRect.top;
+ var buttonHeight = menuRect.height;
+ var menuHeight = ul.outerHeight();
+ if (position > menuHeight && $(window).height() - position < buttonHeight + menuHeight) {
+ menu.addClass("dropup");
+ }
+ }).on('hidden.bs.dropdown', '.btn-group', function() {
+ // always reset after close
+ $(this).removeClass('dropup');
+ });
+
// Enable tabs dynamic loading
$(document).on('show.bs.tab', function(e) {
var link = $(e.target);
+ if (link.exists() && (link.get(0).tagName !== 'A')) {
+ link = $('a[href]', link);
+ }
var data = link.data();
if (data.amsUrl) {
if (data.amsTabLoaded) {
return;
}
- try {
- link.append('<i class="fa fa-spin fa-cog margin-left-5"></i>');
- ams.skin.loadURL(data.amsUrl, link.attr('href'), {afterLoadCallback: function() {
+ link.append('<i class="fa fa-spin fa-cog margin-left-5"></i>');
+ ams.skin.loadURL(data.amsUrl, link.attr('href'), {
+ afterLoadCallback: function() {
if (data.amsTabLoadOnce) {
link.data('ams-tab-loaded', true);
}
- }});
- }
- finally {
- $('i', link).remove();
- }
+ $('i', link).remove();
+ },
+ afterErrorCallback: function() {
+ $('i', link).remove();
+ }
+ });
}
});
@@ -5471,6 +6202,11 @@
});
});
+ // Enable custom MyAMS refresh events
+ $(document).on('myams.refresh', function(event, settings) {
+ MyAMS.executeFunctionByName(settings.handler || MyAMS.skin.refreshContent, event.target, settings);
+ });
+
// Init page content
ams.initContent(document);
if (ams.ajaxNav && nav.exists()) {
@@ -5593,6 +6329,18 @@
CLOSE: "Close",
NEXT: "Next",
PREVIOUS: "Previous"
+ },
+ dndupload: {
+ FILES_SELECTED: '{count} files selected',
+ CHOOSE_FILE: 'Select file(s)',
+ ADD_INFO: 'to add them to current folder,',
+ DRAG_FILE: 'or drag and drop them here!',
+ UPLOAD: 'Upload',
+ UPLOADING: 'Uploading…',
+ DONE: 'Done!',
+ UPLOAD_MORE: 'Upload more?',
+ ERROR: 'Error!',
+ TRY_AGAIN: 'Try again?'
}
};
@@ -5600,6 +6348,8 @@
$(document).ready(function() {
$ = jQuery.noConflict();
var html = $('HTML');
+ html.removeClass('no-js')
+ .addClass('js');
var lang = html.attr('lang') || html.attr('xml:lang');
if (lang && !lang.startsWith('en')) {
MyAMS.lang = lang;