[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-3' - loleaflet/debug loleaflet/dist loleaflet/main.css

Henry Castro hcastro at collabora.com
Tue May 29 14:56:16 UTC 2018


 loleaflet/debug/document/loleaflet.html    |    1 
 loleaflet/dist/loleaflet.css               |   30 
 loleaflet/dist/loleaflet.html              |   17 
 loleaflet/dist/menubar.css                 |    4 
 loleaflet/dist/toolbar.css                 |   30 
 loleaflet/dist/toolbar/toolbar.js          |  342 
 loleaflet/dist/toolbar/w2ui-1.5.rc1.js     |18953 +++++++++++++++++++++++++++++
 loleaflet/dist/toolbar/w2ui-1.5.rc1.min.js |   14 
 loleaflet/dist/toolbar/w2ui.min.js         |   11 
 loleaflet/dist/w2ui-1.5.rc1.css            | 3247 ++++
 loleaflet/main.css                         |    2 
 11 files changed, 22319 insertions(+), 332 deletions(-)

New commits:
commit 793bfbe2d91156f2f4ceb71319116240ccb9ac80
Author: Henry Castro <hcastro at collabora.com>
Date:   Tue Mar 27 22:58:38 2018 -0400

    loleaflet: fix responsive toolbar button issues
    
    Updates the w2ui to version 1.5 (RC1).
    
    Change-Id: I6a24f498511d4b03a04e68af0aa3715bb8c1b48b
    Reviewed-on: https://gerrit.libreoffice.org/52987
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/loleaflet/debug/document/loleaflet.html b/loleaflet/debug/document/loleaflet.html
index ddb760f98..429da430e 100644
--- a/loleaflet/debug/document/loleaflet.html
+++ b/loleaflet/debug/document/loleaflet.html
@@ -79,7 +79,6 @@
         <div id="toolbar" style="visibility: hidden;"></div>
         <div id="toolbar-up"></div>
         <div id="formulabar"></div>
-        <div id="toolbar-up-more"></div>
     </div>
     <input id="insertgraphic" type="file" onchange="onInsertFile()" style="position: fixed; top: -100em">
 
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index e8d88a974..e220f5126 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -66,6 +66,21 @@ body {
     top: 30px;
 }
 
+#toolbar-wrapper {
+    position: relative;
+    table-layout: fixed;
+    border-collapse: collapse;
+    width: 100%;
+}
+
+#toolbar-logo {
+    width: 125px;
+}
+
+#toolbar-hamburger {
+    width: 0;
+}
+
 @media (max-width: 767px) {
     /* Show slidesorter beyond 768px only */
     #presentation-controls-wrapper {
@@ -90,18 +105,11 @@ body {
     #spreadsheet-row-column-frame.readonly {
 	top: 30px !important;
     }
-    /* There seems to be some bug in w2ui library -
-     * when we are in very low screen width, the toolbar-up-more
-     * toolbar's width doesn't adjust according to its children toolbar
-     * items and truncate all items but one. Lets override the width in
-     * mobile mode to prevent that.
-     */
-    #toolbar-up-more {
-	width: auto !important;
+    #toolbar-logo {
+	width: 32px;
     }
-    /* We need to give space for options menu */
-    #tb_toolbar-up_item_rightmenupadding {
-	padding-right: 40px;
+    #toolbar-hamburger {
+	width: 41px;
     }
     #closebutton {
 	display: none;
diff --git a/loleaflet/dist/loleaflet.html b/loleaflet/dist/loleaflet.html
index e18a9cda8..3a3424c79 100644
--- a/loleaflet/dist/loleaflet.html
+++ b/loleaflet/dist/loleaflet.html
@@ -45,11 +45,16 @@
 	</label>
 	<ul id="main-menu" class="sm sm-simple lo-menu"></ul>
       </nav>
-      <div id="toolbar-wrapper">
-	<div id="toolbar-up"></div>
-	<div id="formulabar"></div>
-	<div id="toolbar-up-more"></div>
-      </div>
+      <table id="toolbar-wrapper">
+       <tr>
+         <td id="toolbar-logo"></td>
+         <td id="toolbar-up"</td>
+         <td id="toolbar-hamburger"></td>
+       </tr>
+       <tr>
+         <td colspan="3" id="formulabar"></td>
+       </tr>
+      </table>
       <input id="insertgraphic" type="file" style="position: fixed; top: -100em">
     </div>
 
@@ -100,6 +105,6 @@
     </script>
     <!--%BRANDING_JS%--> <!-- logo onclick handler -->
     <script src="/loleaflet/%VERSION%/bundle.js"></script>
-    <script src="/loleaflet/%VERSION%/toolbar/w2ui.min.js"></script>
+    <script src="/loleaflet/%VERSION%/toolbar/w2ui-1.5.rc1.min.js"></script>
     <script src="/loleaflet/%VERSION%/toolbar/toolbar.js"></script>
 </body></html>
diff --git a/loleaflet/dist/menubar.css b/loleaflet/dist/menubar.css
index 1d8e5eecb..f192eacbf 100644
--- a/loleaflet/dist/menubar.css
+++ b/loleaflet/dist/menubar.css
@@ -190,10 +190,6 @@
     display: block;
 }
 
-#toolbar-wrapper {
-    position: relative;
-}
-
 /* desktop mode */
 @media (min-width: 768px) {
     /* hide the button in desktop view */
diff --git a/loleaflet/dist/toolbar.css b/loleaflet/dist/toolbar.css
index 984014b66..4025453a4 100644
--- a/loleaflet/dist/toolbar.css
+++ b/loleaflet/dist/toolbar.css
@@ -2,7 +2,7 @@
     left: 0;
     right: 0;
     text-align: center;
-    z-index: 1000;
+    z-index: 999;
     overflow: visible !important;
 }
 
@@ -16,7 +16,7 @@
     z-index: 1000;
 }
 
-#toolbar-up-more,w2ui-toolbar {
+w2ui-toolbar {
     position: absolute;
     right: 10px;
     top: 40px;
@@ -52,15 +52,15 @@
     z-index: 11;
 }
 
+.w2ui-scroll-left,
+.w2ui-scroll-right {
+    z-index: 15;
+}
+
 /* center the toolbar */
 #tb_presentation-toolbar_item_left {
     width: 50%;
 }
-/* leave space for branding logo */
-#tb_toolbar-up_item_left {
-    width: 0;
-    padding-left: 125px;
-}
 
 #addressInput {
     height: 29px;
@@ -72,6 +72,12 @@
     width: 700px;
 }
 
+ at media (max-width: 767px) {
+     #formulaInput {
+	width: 500px;
+     }
+}
+
 #document-name-input {
     position: fixed;
     z-index: 1050;
@@ -119,10 +125,6 @@
 }
 .closebuttonimage{ background: url('../images/lc_closedoc.svg') no-repeat center !important; }
 
-#tb_toolbar-up_item_right {
-    width: 100%;
-}
-
 #tb_toolbar-down_item_left {
     width: 45%;
 }
@@ -313,11 +315,6 @@ button.leaflet-control-search-next
 .w2ui-icon.specialcharacter{ background: url('../images/lc_insertsymbol.svg') no-repeat center !important; }
 .w2ui-icon.insertobjectchart{ background: url('../images/lc_drawchart.svg') no-repeat center !important; }
 
-#inserttable-wrapper {
-    position: relative;
-    right: 230px; /* insertable popup has ~230px width, place it to open from its right edge to the left */
-}
-
 .inserttable-pop {
     z-index: 1000;
     box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
@@ -348,7 +345,6 @@ button.leaflet-control-search-next
     position: relative;
     padding: 2px;
     display: block;
-    width: 232px;
     height: 232px;
 }
 
diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index ad2a05848..e05f33529 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -11,63 +11,23 @@ function onDelete(e) {
 	}
 }
 
-// When we are in mobile view, only these items in toolbar-up will be shown
-var toolbarUpMobileItems = [
-	'left',
-	'save',
-	'savebreak',
-	'bold',
-	'italic',
-	'underline',
-	'strikeout',
-	'formatbreak',
-	'leftpara',
-	'centerpara',
-	'rightpara',
-	'justifypara',
-	'right',
-	'rightmenupadding'
-];
-
-var statusbarMobileItems = [
-	'search',
-	'searchprev',
-	'searchnext',
-	'cancelsearch',
-	'left',
-	'right',
-	'userlist',
-	'userlistbreak',
-	'prev',
-	'next'
-];
-
 var nUsers, oneUser, noUser;
 
 function _mobilify() {
 	var toolbarUp = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
 	var statusbar = w2ui['toolbar-down'];
 
-	for (var itemIdx in toolbarUp.items) {
-		var id = toolbarUp.items[itemIdx].id;
-		if (toolbarUpMobileItems.indexOf(id) === -1 && toolbarUp.get(id) && !toolbarUp.get(id).hidden) {
-			toolbarUp.hide(id);
-		}
-	}
-	for (itemIdx in toolbarUpMore.items) {
-		id = toolbarUpMore.items[itemIdx].id;
-		if (toolbarUpMobileItems.indexOf(id) === -1 && toolbarUpMore.get(id) && !toolbarUpMore.get(id).hidden) {
-			toolbarUpMore.hide(id);
+	toolbarUp.items.forEach(function(item) {
+		if (item.mobile === false && !item.hidden) {
+			toolbarUp.hide(item.id);
 		}
-	}
+	});
 
-	for (itemIdx in statusbar.items) {
-		id = statusbar.items[itemIdx].id;
-		if (statusbarMobileItems.indexOf(id) === -1 && !statusbar.get(id).hidden) {
-			statusbar.hide(id);
+	statusbar.items.forEach(function(item) {
+		if (item.mobile === false && !item.hidden) {
+			statusbar.hide(item.id);
 		}
-	}
+	});
 
 	nUsers = '%n';
 	oneUser = '1';
@@ -79,28 +39,19 @@ function _mobilify() {
 
 function _unmobilify() {
 	var toolbarUp = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
 	var statusbar = w2ui['toolbar-down'];
 
-	for (var itemIdx in toolbarUp.items) {
-		var id = toolbarUp.items[itemIdx].id;
-		if (toolbarUpMobileItems.indexOf(id) === -1 && toolbarUp.get(id) && toolbarUp.get(id).hidden) {
-			toolbarUp.show(id);
+	toolbarUp.items.forEach(function(item) {
+		if (item.mobile === false && item.hidden) {
+			toolbarUp.show(item.id);
 		}
-	}
-	for (itemIdx in toolbarUpMore.items) {
-		id = toolbarUpMore.items[itemIdx].id;
-		if (toolbarUpMobileItems.indexOf(id) === -1 && toolbarUpMore.get(id) && toolbarUpMore.get(id).hidden) {
-			toolbarUpMore.show(id);
-		}
-	}
+	});
 
-	for (itemIdx in statusbar.items) {
-		id = statusbar.items[itemIdx].id;
-		if (statusbarMobileItems.indexOf(id) === -1 && statusbar.get(id).hidden) {
-			statusbar.show(id);
+	statusbar.items.forEach(function(item) {
+		if (item.mobile === false && item.hidden) {
+			statusbar.show(item.id);
 		}
-	}
+	});
 
 	nUsers = _('%n users');
 	oneUser = _('1 user');
@@ -111,9 +62,8 @@ function _unmobilify() {
 }
 
 function resizeToolbar() {
-	var hasMoreItems = false;
 	var toolbarUp = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
+	var statusbar = w2ui['toolbar-down'];
 
 	if ($(window).width() < mobileWidth) {
 		_mobilify();
@@ -121,44 +71,8 @@ function resizeToolbar() {
 		_unmobilify();
 	}
 
-	toolbarUp.refresh();
-	toolbarUpMore.refresh();
-	// move items from toolbar-up-more -> toolbar-up
-	while ($('#toolbar-up')[0].scrollWidth <= $(window).width()) {
-		var item = toolbarUpMore.items[0];
-		if (!item) {
-			break;
-		}
-		toolbarUpMore.items.shift();
-		toolbarUp.insert('right', item);
-	}
-
-	// move items from toolbar-up -> toolbar-up-more
-	while ($('#toolbar-up')[0].scrollWidth > Math.max($(window).width(), parseInt($('body').css('min-width')))) {
-		var itemId = toolbarUp.items[toolbarUp.items.length - 4].id;
-		item = toolbarUp.get(itemId);
-		toolbarUp.remove(itemId);
-		toolbarUpMore.insert(toolbarUpMore.items[0], item);
-		hasMoreItems = true;
-	}
-
-	if (hasMoreItems) {
-		w2ui['toolbar-up'].show('more');
-	}
-	else {
-		w2ui['toolbar-up'].hide('more');
-	}
-
-	// resize toolbar-up-more
-	var lastItem = $('#toolbar-up-more>table>tbody>tr>td[valign="middle"]').last();
-	if (lastItem.length) {
-		$('#toolbar-up-more').width($(lastItem).position().left + $(lastItem).width());
-		w2ui['toolbar-up-more'].render();
-	} else {
-		$('#toolbar-up-more').hide();
-		var toolbar = w2ui['toolbar-up'];
-		toolbar.uncheck('more');
-	}
+	toolbarUp.resize();
+	statusbar.resize();
 }
 
 function _cancelSearch() {
@@ -171,7 +85,7 @@ function _cancelSearch() {
 	map.focus();
 }
 
-function onClick(id, item, subItem) {
+function onClick(e, id, item, subItem) {
 	if (w2ui['toolbar-up'].get(id) !== null) {
 		var toolbar = w2ui['toolbar-up'];
 		var item = toolbar.get(id);
@@ -192,10 +106,6 @@ function onClick(id, item, subItem) {
 		toolbar = w2ui['presentation-toolbar'];
 		item = toolbar.get(id);
 	}
-	else if (w2ui['toolbar-up-more'].get(id) !== null) {
-		toolbar = w2ui['toolbar-up-more'];
-		item = toolbar.get(id);
-	}
 	else if (item && subItem)
 	{
 		var command = {
@@ -300,30 +210,11 @@ function onClick(id, item, subItem) {
 	else if (id === 'insertgraphic') {
 		L.DomUtil.get('insertgraphic').click();
 	}
-	else if (id === 'inserttable') {
-		$('#inserttable-popup').toggle();
+	else if (id === 'fontcolor' && e.color) {
+		onColorPick(id, e.color);
 	}
-	else if (id === 'fontcolor') {
-		// absolutely no idea why, but without the timeout, the popup is
-		// closed as soon as it is opend
-		setTimeout(function () {
-			$('#fontColorPicker').colorpicker({
-				strings: _('Theme Colors') + ',' + _('Standard Colors') + ',' + _('Web Colors') + ',' + _('Theme Colors') + ',' + _('Back to Palette') + ',' + _('History') + ',' + _('No history yet.')
-			});
-			$('#fontColorPicker').colorpicker('showPalette');
-			$('.ui-widget-content').addClass('loleaflet-font');
-		}, 0);
-	}
-	else if (id === 'backcolor') {
-		// absolutely no idea why, but without the timeout, the popup is
-		// closed as soon as it is opend
-		setTimeout(function () {
-			$('#backColorPicker').colorpicker({
-				strings: _('Theme Colors') + ',' + _('Standard Colors') + ',' + _('Web Colors') + ',' + _('Theme Colors') + ',' + _('Back to Palette') + ',' + _('History') + ',' + _('No history yet.')
-			});
-			$('#backColorPicker').colorpicker('showPalette');
-			$('.ui-widget-content').addClass('loleaflet-font');
-		}, 0);
+	else if (id === 'backcolor' && e.color) {
+		onColorPick(id, e.color)
 	}
 	else if (id === 'sum') {
 		map.sendUnoCommand('.uno:AutoSum');
@@ -348,31 +239,14 @@ function onClick(id, item, subItem) {
 		w2ui['formulabar'].hide('acceptformula', 'cancelformula');
 		w2ui['formulabar'].show('sum', 'function');
 	}
-	else if (id === 'more') {
-		$('#toolbar-up-more').toggle();
-		if ($('#toolbar-up-more').is(':visible')) {
-			toolbar.check('more');
-		}
-		else {
-			toolbar.uncheck('more');
-		}
-		w2ui['toolbar-up-more'].render();
-		resizeToolbar();
-	}
 }
 
 function insertTable() {
 	var rows = 10;
 	var cols = 10;
 	var $grid = $('.inserttable-grid');
-	var $popup = $('#inserttable-popup');
 	var $status = $('#inserttable-status');
 
-	// Return if already initialized
-	if ($grid.children().length) {
-		return;
-	}
-
 	// init
 	for (var r = 0; r < rows; r++) {
 		var $row = $('<div/>').addClass('row');
@@ -397,7 +271,6 @@ function insertTable() {
 		click: function() {
 			var col = $(this).index() + 1;
 			var row = $(this).parent().index() + 1;
-			$popup.toggle();
 			$('.col').removeClass('bright');
 			$status.html('<br/>');
 			var msg = 'uno .uno:InsertTable {' +
@@ -405,21 +278,19 @@ function insertTable() {
 				+ col +
 				' }, "Rows": { "type": "long","value": '
 				+ row + ' }}';
+
+			if ($('#w2ui-overlay-toolbar-up').length > 0) {
+				$('#w2ui-overlay-toolbar-up').removeData('keepOpen')[0].hide();
+			}
+
 			map._socket.sendMessage(msg);
 			// refocus map due popup
 			map.focus();
 		}
 	}, '.col');
-
-	// close dialog on mouseleave
-	$popup.mouseleave(function() {
-		$(this).hide();
-		$('.col').removeClass('bright');
-		$status.html('<br/>');
-	});
 }
 
-function onColorPick(e, color) {
+function onColorPick(id, color) {
 	if (map.getPermission() !== 'edit' || color === undefined) {
 		return;
 	}
@@ -427,7 +298,7 @@ function onColorPick(e, color) {
 	color = parseInt(color.replace('#', ''), 16);
 	var command = {};
 	var fontcolor, backcolor;
-	if (e.target.id === 'fontColorPicker') {
+	if (id === 'fontcolor') {
 		fontcolor = {'text': 'FontColor',
 					 'spreadsheet': 'Color',
 					 'presentation': 'Color'}[map.getDocType()];
@@ -436,7 +307,7 @@ function onColorPick(e, color) {
 		command[fontcolor].value = color;
 		var uno = '.uno:' + fontcolor;
 	}
-	else if (e.target.id === 'backColorPicker') {
+	else if (id === 'backcolor') {
 		backcolor = {'text': 'BackColor',
 					 'spreadsheet': 'BackgroundColor',
 					 'presentation': 'CharBackColor'}[map.getDocType()];
@@ -454,15 +325,6 @@ var fontsSelectValue;
 var fontsizesSelectValue;
 
 $(function () {
-	$('#toolbar-up-more').w2toolbar({
-		name: 'toolbar-up-more',
-		items: [
-		],
-		onClick: function (e) {
-			onClick(e.target);
-		}
-	});
-
 	$('#toolbar-up').w2toolbar({
 		name: 'toolbar-up',
 		items: [
@@ -473,19 +335,17 @@ $(function () {
 			{type: 'button',  id: 'redo',  img: 'redo', hint: _UNO('.uno:Redo'), uno: 'Redo', disabled: true},
 			{type: 'button',  id: 'repair', img: 'repair', hint: _('Document repair'), disabled: true},
 			{type: 'break'},
-			{type: 'html',   id: 'styles', html: '<select class="styles-select"></select>'},
-			{type: 'html',   id: 'fonts', html: '<select class="fonts-select"></select>'},
-			{type: 'html',   id: 'fontsizes', html: '<select class="fontsizes-select"></select>'},
-			{type: 'break'},
+			{type: 'html',   id: 'styles', html: '<select class="styles-select"></select>', mobile: false},
+			{type: 'html',   id: 'fonts', html: '<select class="fonts-select"></select>', mobile: false},
+			{type: 'html',   id: 'fontsizes', html: '<select class="fontsizes-select"></select>', mobile: false},
+			{type: 'break', mobile: false},
 			{type: 'button',  id: 'bold',  img: 'bold', hint: _UNO('.uno:Bold'), uno: 'Bold', disabled: true},
 			{type: 'button',  id: 'italic', img: 'italic', hint: _UNO('.uno:Italic'), uno: 'Italic', disabled: true},
 			{type: 'button',  id: 'underline',  img: 'underline', hint: _UNO('.uno:Underline'), uno: 'Underline', disabled: true},
 			{type: 'button',  id: 'strikeout', img: 'strikeout', hint: _UNO('.uno:Strikeout'), uno: 'Strikeout', disabled: true},
 			{type: 'break', id: 'formatbreak'},
-			{type: 'html',  id: 'fontcolor-html', html: '<div id="fontcolor-wrapper"><input id="fontColorPicker" style="display:none;"></div>'},
-			{type: 'button',  id: 'fontcolor', img: 'color', hint: _UNO('.uno:FontColor')},
-			{type: 'html',  id: 'backcolor-html', html: '<div id="backcolor-wrapper"><input id="backColorPicker" style="display:none;"></div>'},
-			{type: 'button',  id: 'backcolor', img: 'backcolor', hint: _UNO('.uno:BackgroundColor')},
+			{type: 'text-color',  id: 'fontcolor', hint: _UNO('.uno:FontColor')},
+			{type: 'color',  id: 'backcolor', hint: _UNO('.uno:BackgroundColor')},
 			{type: 'break'},
 			{type: 'button',  id: 'leftpara',  img: 'alignleft', hint: _UNO('.uno:LeftPara', '', true), uno: 'LeftPara', unosheet: 'AlignLeft', disabled: true},
 			{type: 'button',  id: 'centerpara',  img: 'alignhorizontal', hint: _UNO('.uno:CenterPara', '', true), uno: 'CenterPara', unosheet: 'AlignHorizontalCenter', disabled: true},
@@ -511,80 +371,17 @@ $(function () {
 			{type: 'button',  id: 'incrementindent',  img: 'incrementindent', hint: _UNO('.uno:IncrementIndent', '', true), uno: 'IncrementIndent', disabled: true},
 			{type: 'button',  id: 'decrementindent',  img: 'decrementindent', hint: _UNO('.uno:DecrementIndent', '', true), uno: 'DecrementIndent', disabled: true},
 			{type: 'break', id: 'incdecindent'},
-			{type: 'html',  id: 'inserttable-html', html: '<div id="inserttable-wrapper"><div id="inserttable-popup" class="inserttable-pop ui-widget ui-widget-content ui-corner-all" style="position: absolute; display: none;"><div class="inserttable-grid"></div><div id="inserttable-status" class="loleaflet-font" style="padding: 5px;"><br/></div></div>'},
-			{type: 'button',  id: 'inserttable',  img: 'inserttable', hint: _('Insert table')},
+			{type: 'drop',  id: 'inserttable',  img: 'inserttable', hint: _('Insert table'), overlay: {onShow: insertTable},
+			 html: '<div id="inserttable-wrapper"><div id="inserttable-popup" class="inserttable-pop ui-widget ui-widget-content ui-corner-all"><div class="inserttable-grid"></div><div id="inserttable-status" class="loleaflet-font" style="padding: 5px;"><br/></div></div></div>'},
 			{type: 'button',  id: 'insertobjectchart',  img: 'insertobjectchart', hint: _UNO('.uno:InsertObjectChart', '', true), uno: 'InsertObjectChart'},
 			{type: 'button',  id: 'insertannotation', img: 'annotation', hint: _UNO('.uno:InsertAnnotation', '', true)},
 			{type: 'button',  id: 'insertgraphic',  img: 'insertgraphic', hint: _UNO('.uno:InsertGraphic', '', true)},
-			{type: 'button',  id: 'specialcharacter', img: 'specialcharacter', hint: _UNO('.uno:InsertSymbol', '', true), uno: '.uno:InsertSymbol'},
-			{type: 'html', id: 'right'},
-			{type: 'button',  id: 'more', img: 'more', hint: _('More')},
-			{type: 'html', id: 'rightmenupadding'}
+			{type: 'button',  id: 'specialcharacter', img: 'specialcharacter', hint: _UNO('.uno:InsertSymbol', '', true), uno: '.uno:InsertSymbol'}
 		],
 		onClick: function (e) {
-			onClick(e.target);
+			onClick(e, e.target);
 		},
 		onRefresh: function() {
-			if (!L.DomUtil.get('fontcolorindicator')) {
-				var fontColorIndicator = L.DomUtil.create('div', 'font-color-indicator', L.DomUtil.get('tb_toolbar-up_item_fontcolor'));
-				fontColorIndicator.id = 'fontcolorindicator';
-				L.DomEvent.on(fontColorIndicator, 'mouseover', function () {
-					var button = fontColorIndicator.parentNode.firstChild;
-					$(button).addClass('over');
-				});
-				L.DomEvent.on(fontColorIndicator, 'mouseout', function () {
-					var button = fontColorIndicator.parentNode.firstChild;
-					$(button).removeClass('over');
-				});
-				L.DomEvent.on(fontColorIndicator, 'mousedown', function () {
-					var button = fontColorIndicator.parentNode.firstChild;
-					$(button).addClass('down');
-				});
-				L.DomEvent.on(fontColorIndicator, 'mouseup', function () {
-					var button = fontColorIndicator.parentNode.firstChild;
-					$(button).removeClass('down');
-				});
-				fontColorIndicator.addEventListener('click', function () {
-					var toolbar = w2ui['toolbar-up'];
-					if (toolbar) {
-						toolbar.click('fontcolor', window.event);
-					}
-				}, false);
-
-				$('#fontColorPicker').colorpicker({showOn:'none', hideButton:true});
-				$('#fontColorPicker').on('change.color', onColorPick);
-			}
-
-			if (!L.DomUtil.get('backcolorindicator')) {
-				var backColorIndicator = L.DomUtil.create('div', 'back-color-indicator', L.DomUtil.get('tb_toolbar-up_item_backcolor'));
-				backColorIndicator.id = 'backcolorindicator';
-				L.DomEvent.on(backColorIndicator, 'mouseover', function () {
-					var button = backColorIndicator.parentNode.firstChild;
-					$(button).addClass('over');
-				});
-				L.DomEvent.on(backColorIndicator, 'mouseout', function () {
-					var button = backColorIndicator.parentNode.firstChild;
-					$(button).removeClass('over');
-				});
-				L.DomEvent.on(backColorIndicator, 'mousedown', function () {
-					var button = backColorIndicator.parentNode.firstChild;
-					$(button).addClass('down');
-				});
-				L.DomEvent.on(backColorIndicator, 'mouseup', function () {
-					var button = backColorIndicator.parentNode.firstChild;
-					$(button).removeClass('down');
-				});
-				backColorIndicator.addEventListener('click', function () {
-					var toolbar = w2ui['toolbar-up'];
-					if (toolbar) {
-						toolbar.click('backcolor', window.event);
-					}
-				}, false);
-
-				$('#backColorPicker').colorpicker({showOn:'none', hideButton:true});
-				$('#backColorPicker').on('change.color', onColorPick);
-			}
-
 			if (map.getDocType() === 'presentation') {
 				// Fill the style select box if not yet filled
 				if ($('.styles-select')[0] && $('.styles-select')[0].length === 0) {
@@ -623,7 +420,7 @@ $(function () {
 			{type: 'html', id: 'formula', html: '<input id="formulaInput" type="text">'}
 		],
 		onClick: function (e) {
-			onClick(e.target);
+			onClick(e, e.target);
 		},
 		onRefresh: function(e) {
 			$('#addressInput').off('keyup', onAddressInput).on('keyup', onAddressInput);
@@ -642,7 +439,7 @@ $(function () {
 			{type: 'button',  id: 'insertsheet', img: 'insertsheet', hidden:true, hint: _('Insert sheet')}
 		],
 		onClick: function (e) {
-			onClick(e.target);
+			onClick(e, e.target);
 		}
 	});
 	$('#presentation-toolbar').w2toolbar({
@@ -657,7 +454,7 @@ $(function () {
 			{type: 'html',  id: 'right'}
 		],
 		onClick: function (e) {
-			onClick(e.target);
+			onClick(e, e.target);
 		}
 	});
 
@@ -676,8 +473,8 @@ $(function () {
 			{type: 'button',  id: 'cancelsearch', img: 'cancel', hint: _('Cancel the search'), hidden: true},
 			{type: 'html',  id: 'left'},
 			{type: 'html',  id: 'right'},
-			{type: 'html',    id: 'modifiedstatuslabel', html: '<div id="modifiedstatuslabel" class="loleaflet-font"></div>'},
-			{type: 'break', id: 'modifiedstatuslabelbreak'},
+			{type: 'html',    id: 'modifiedstatuslabel', html: '<div id="modifiedstatuslabel" class="loleaflet-font"></div>', mobile:false},
+			{type: 'break', id: 'modifiedstatuslabelbreak', mobile:false},
 			{type: 'drop', id: 'userlist', text: _('No users'), html: '<div id="userlist_container"><table id="userlist_table"><tbody></tbody></table>' +
 				'<hr><table class="loleaflet-font" id="editor-btn">' +
 				'<tr>' +
@@ -694,7 +491,7 @@ $(function () {
 			{type: 'break', id: 'prevnextbreak'},
 			{type: 'button',  id: 'zoomreset', img: 'zoomreset', hint: _('Reset zoom')},
 			{type: 'button',  id: 'zoomout', img: 'zoomout', hint: _UNO('.uno:ZoomMinus')},
-			{type: 'html',    id: 'zoomlevel', html: '<div id="zoomlevel" class="loleaflet-font">100%</div>'},
+			{type: 'html',    id: 'zoomlevel', html: '<div id="zoomlevel" class="loleaflet-font">100%</div>', mobile: false},
 			{type: 'button',  id: 'zoomin', img: 'zoomin', hint: _UNO('.uno:ZoomPlus')}
 		],
 		onClick: function (e) {
@@ -714,7 +511,7 @@ $(function () {
 				}, 100);
 				return;
 			}
-			onClick(e.target, e.item, e.subItem);
+			onClick(e, e.target, e.item, e.subItem);
 		},
 		onRefresh: function(e) {
 			$('#tb_toolbar-down_item_userlist .w2ui-tb-caption').addClass('loleaflet-font');
@@ -1057,14 +854,12 @@ map.on('wopiprops', function(e) {
 
 map.on('doclayerinit', function () {
 	var toolbarUp = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
 	var statusbar = w2ui['toolbar-down'];
 	var docType = map.getDocType();
 
 	switch (docType) {
 	case 'spreadsheet':
 		toolbarUp.remove('inserttable', 'styles', 'justifypara', 'defaultbullet', 'defaultnumbering', 'break-numbering');
-		toolbarUpMore.remove('inserttable', 'styles', 'justifypara', 'defaultbullet', 'defaultnumbering', 'break-numbering');
 		statusbar.disable('zoomreset', 'zoomout', 'zoomin', 'zoomlevel');
 		statusbar.insert('left', [
 			{type: 'break', id:'break1'},
@@ -1074,13 +869,13 @@ map.on('doclayerinit', function () {
 			{type: 'html',  id: 'RowColSelCount',
 				html: '<div id="RowColSelCount" class="loleaflet-font" title="'+_('Selected range of cells')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
 			{type: 'break', id:'break3'},
-			{type: 'html',  id: 'InsertMode',
+			{type: 'html',  id: 'InsertMode', mobile: false,
 				html: '<div id="InsertMode" class="loleaflet-font" title="'+_('Entering text mode')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
 			{type: 'break', id:'break4'},
-			{type: 'html',  id: 'LanguageStatus',
+			{type: 'html',  id: 'LanguageStatus', mobile: false,
 				html: '<div id="LanguageStatus" class="loleaflet-font" title="'+_('Text Language')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
 			{type: 'break', id:'break5'},
-			{type: 'html',  id: 'StatusSelectionMode',
+			{type: 'html',  id: 'StatusSelectionMode', mobile: false,
 				html: '<div id="StatusSelectionMode" class="loleaflet-font" title="'+_('Selection Mode')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
 			{type: 'break', id:'break8'},
 			{type: 'html',  id: 'StateTableCell',
@@ -1103,22 +898,21 @@ map.on('doclayerinit', function () {
 		break;
 	case 'text':
 		toolbarUp.remove('wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
-		toolbarUpMore.remove('wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
 		statusbar.insert('left', [
 			{type: 'break', id: 'break1'},
 			{type: 'html',  id: 'StatePageNumber',
 				html: '<div id="StatePageNumber" class="loleaflet-font" title="'+_('Number of Pages')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
 			{type: 'break', id:'break2'},
-			{type: 'html',  id: 'StateWordCount',
+			{type: 'html',  id: 'StateWordCount', mobile: false,
 				html: '<div id="StateWordCount" class="loleaflet-font" title="'+_('Word Counter')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
-			{type: 'break', id:'break5'},
-			{type: 'html',  id: 'InsertMode',
+			{type: 'break', id:'break5', mobile: false},
+			{type: 'html',  id: 'InsertMode', mobile: false,
 				html: '<div id="InsertMode" class="loleaflet-font" title="'+_('Entering text mode')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
-			{type: 'break', id:'break6'},
-			{type: 'html',  id: 'StatusSelectionMode',
+			{type: 'break', id:'break6', mobile:false},
+			{type: 'html',  id: 'StatusSelectionMode', mobile: false,
 				html: '<div id="StatusSelectionMode" class="loleaflet-font" title="'+_('Selection Mode')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
-			{type: 'break', id:'break7'},
-			{type: 'html',  id: 'LanguageStatus',
+			{type: 'break', id:'break7', mobile:false},
+			{type: 'html',  id: 'LanguageStatus', mobile: false,
 				html: '<div id="LanguageStatus" class="loleaflet-font" title="'+_('Text Language')+ '" style="padding: 5px 5px;">    &nbsp</div>' }
 		]);
 
@@ -1135,13 +929,12 @@ map.on('doclayerinit', function () {
 			presentationToolbar.show('presentation', 'presentationbreak');
 		}
 		toolbarUp.remove('insertannotation', 'wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
-		toolbarUpMore.remove('insertannotation', 'wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
 		statusbar.insert('left', [
 			{type: 'break', id:'break1'},
 			{type: 'html',  id: 'PageStatus',
 				html: '<div id="PageStatus" class="loleaflet-font" title="'+_('Number of Slides')+ '" style="padding: 5px 5px;">    &nbsp</div>' },
-			{type: 'break', id:'break2'},
-			{type: 'html',  id: 'LanguageStatus',
+			{type: 'break', id:'break2', mobile:false},
+			{type: 'html',  id: 'LanguageStatus', mobile: false,
 				html: '<div id="LanguageStatus" class="loleaflet-font" title="'+_('Text Language')+ '" style="padding: 5px 5px;">    &nbsp</div>' }
 		]);
 
@@ -1152,7 +945,6 @@ map.on('doclayerinit', function () {
 		break;
 	case 'drawing':
 		toolbarUp.remove('insertannotation', 'wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
-		toolbarUpMore.remove('insertannotation', 'wraptextseparator', 'wraptext', 'togglemergecells', 'break-toggle', 'numberformatcurrency', 'numberformatpercent', 'numberformatdecimal', 'numberformatdate', 'numberformatincdecimals', 'numberformatdecdecimals', 'break-number', 'sortascending', 'sortdescending');
 
 		// Remove irrelevant toolbars
 		$('#formulabar').hide();
@@ -1161,7 +953,6 @@ map.on('doclayerinit', function () {
 		break;
 	}
 	toolbarUp.refresh();
-	toolbarUpMore.refresh();
 	statusbar.refresh();
 	resizeToolbar();
 });
@@ -1169,7 +960,6 @@ map.on('doclayerinit', function () {
 
 map.on('commandstatechanged', function (e) {
 	var toolbar = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
 	var statusbar = w2ui['toolbar-down'];
 	var commandName = e.commandName;
 	var state = e.state;
@@ -1343,25 +1133,19 @@ map.on('commandstatechanged', function (e) {
 	if (state === 'true') {
 		toolbar.enable(id);
 		toolbar.check(id);
-		toolbarUpMore.check(id);
 	}
 	else if (state === 'false') {
 		toolbar.enable(id);
 		toolbar.uncheck(id);
-		toolbarUpMore.uncheck(id);
 	}
 	// Change the toolbar button states if we are in editmode
 	// If in non-edit mode, will be taken care of when permission is changed to 'edit'
 	else if (map._permission === 'edit' && (state === 'enabled' || state === 'disabled')) {
-		// in case some buttons are in toolbar-up-more, find
-		// them and en/dis-able them.
 		if (state === 'enabled') {
 			toolbar.enable(id);
-			toolbarUpMore.enable(id);
 		} else {
 			toolbar.uncheck(id);
 			toolbar.disable(id);
-			toolbarUpMore.disable(id);
 		}
 	}
 });
@@ -1600,22 +1384,18 @@ map.on('hyperlinkclicked', function (e) {
 
 map.on('updatepermission', function (e) {
 	var toolbar = w2ui['toolbar-up'];
-	var toolbarUpMore = w2ui['toolbar-up-more'];
 
 	// copy the first array
 	var items = toolbar.items.slice();
-	items.concat(toolbarUpMore.items);
 	for (var idx in items) {
 		var unoCmd = map.getDocType() === 'spreadsheet' ? items[idx].unosheet : items[idx].uno;
 		var keepDisabled = map['stateChangeHandler'].getItemValue(unoCmd) === 'disabled';
 		if (e.perm === 'edit') {
 			if (!keepDisabled) {
 				toolbar.enable(items[idx].id);
-				toolbarUpMore.enable(items[idx].id);
 			}
 		} else {
 			toolbar.disable(items[idx].id);
-			toolbarUpMore.disable(items[idx].id);
 		}
 	}
 
diff --git a/loleaflet/dist/toolbar/w2ui-1.5.rc1.js b/loleaflet/dist/toolbar/w2ui-1.5.rc1.js
new file mode 100644
index 000000000..89f54de62
--- /dev/null
+++ b/loleaflet/dist/toolbar/w2ui-1.5.rc1.js
@@ -0,0 +1,18953 @@
+/* w2ui 1.5.rc1 (nightly) (c) http://w2ui.com, vitmalina at gmail.com */
+var w2ui  = w2ui  || {};
+var w2obj = w2obj || {}; // expose object to be able to overwrite default functions
+
+/************************************************
+*  Library: Web 2.0 UI for jQuery
+*  - Following objects are defines
+*        - w2ui             - object that will contain all widgets
+*        - w2obj            - object with widget prototypes
+*        - w2utils          - basic utilities
+*        - $().w2render     - common render
+*        - $().w2destroy    - common destroy
+*        - $().w2marker     - marker plugin
+*        - $().w2tag        - tag plugin
+*        - $().w2overlay    - overlay plugin
+*        - $().w2menu       - menu plugin
+*        - w2utils.event    - generic event object
+*  - Dependencies: jQuery
+*
+* == NICE TO HAVE ==
+*   - overlay should be displayed where more space (on top or on bottom)
+*   - write and article how to replace certain framework functions
+*   - add maxHeight for the w2menu
+*   - add time zone
+*   - TEST On IOS
+*   - $().w2marker() -- only unmarks first instance
+*   - subitems for w2menus()
+*   - add w2utils.lang wrap for all captions in all buttons.
+*   - $().w2date(), $().w2dateTime()
+*
+************************************************/
+
+var w2utils = (function ($) {
+    var tmp = {}; // for some temp variables
+    var obj = {
+        version  : '1.5.RC1',
+        settings : {
+            "locale"            : "en-us",
+            "dateFormat"        : "m/d/yyyy",
+            "timeFormat"        : "hh:mi pm",
+            "datetimeFormat"    : "m/d/yyyy|hh:mi pm",
+            "currencyPrefix"    : "$",
+            "currencySuffix"    : "",
+            "currencyPrecision" : 2,
+            "groupSymbol"       : ",",
+            "decimalSymbol"     : ".",
+            "shortmonths"       : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+            "fullmonths"        : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+            "shortdays"         : ["M", "T", "W", "T", "F", "S", "S"],
+            "fulldays"          : ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+            "weekStarts"        : "M",        // can be "M" for Monday or "S" for Sunday
+            "dataType"          : 'HTTPJSON', // can be HTTP, HTTPJSON, RESTFULL, RESTFULLJSON, JSON (case sensitive)
+            "phrases"           : {},         // empty object for english phrases
+            "dateStartYear"     : 1950,       // start year for date-picker
+            "dateEndYear"       : 2020        // end year for date picker
+        },
+        isBin           : isBin,
+        isInt           : isInt,
+        isFloat         : isFloat,
+        isMoney         : isMoney,
+        isHex           : isHex,
+        isAlphaNumeric  : isAlphaNumeric,
+        isEmail         : isEmail,
+        isDate          : isDate,
+        isTime          : isTime,
+        isDateTime      : isDateTime,
+        age             : age,
+        interval        : interval,
+        date            : date,
+        formatSize      : formatSize,
+        formatNumber    : formatNumber,
+        formatDate      : formatDate,
+        formatTime      : formatTime,
+        formatDateTime  : formatDateTime,
+        stripTags       : stripTags,
+        encodeTags      : encodeTags,
+        decodeTags      : decodeTags,
+        escapeId        : escapeId,
+        base64encode    : base64encode,
+        base64decode    : base64decode,
+        md5             : md5,
+        transition      : transition,
+        lock            : lock,
+        unlock          : unlock,
+        message         : message,
+        lang            : lang,
+        locale          : locale,
+        getSize         : getSize,
+        getStrWidth     : getStrWidth,
+        scrollBarSize   : scrollBarSize,
+        checkName       : checkName,
+        checkUniqueId   : checkUniqueId,
+        parseRoute      : parseRoute,
+        cssPrefix       : cssPrefix,
+        getCursorPosition : getCursorPosition,
+        setCursorPosition : setCursorPosition,
+        testLocalStorage  : testLocalStorage,
+        hasLocalStorage   : testLocalStorage(),
+        // some internal variables
+        isIOS : ((navigator.userAgent.toLowerCase().indexOf('iphone') != -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('ipod') != -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('ipad') != -1)
+                 ? true : false),
+        isIE : ((navigator.userAgent.toLowerCase().indexOf('msie') != -1 ||
+                 navigator.userAgent.toLowerCase().indexOf('trident') != -1 )
+                 ? true : false)
+    };
+    return obj;
+
+    function isBin (val) {
+        var re = /^[0-1]+$/;
+        return re.test(val);
+    }
+
+    function isInt (val) {
+        var re = /^[-+]?[0-9]+$/;
+        return re.test(val);
+    }
+
+    function isFloat (val) {
+        if (typeof val == 'string') val = val.replace(/\s+/g, '').replace(w2utils.settings.groupSymbol, '').replace(w2utils.settings.decimalSymbol, '.');
+        return (typeof val === 'number' || (typeof val === 'string' && val !== '')) && !isNaN(Number(val));
+    }
+
+    function isMoney (val) {
+        var se = w2utils.settings;
+        var re = new RegExp('^'+ (se.currencyPrefix ? '\\' + se.currencyPrefix + '?' : '') +
+                            '[-+]?'+ (se.currencyPrefix ? '\\' + se.currencyPrefix + '?' : '') +
+                            '[0-9]*[\\'+ se.decimalSymbol +']?[0-9]+'+ (se.currencySuffix ? '\\' + se.currencySuffix + '?' : '') +'$', 'i');
+        if (typeof val === 'string') {
+            val = val.replace(new RegExp(se.groupSymbol, 'g'), '');
+        }
+        if (typeof val === 'object' || val === '') return false;
+        return re.test(val);
+    }
+
+    function isHex (val) {
+        var re = /^[a-fA-F0-9]+$/;
+        return re.test(val);
+    }
+
+    function isAlphaNumeric (val) {
+        var re = /^[a-zA-Z0-9_-]+$/;
+        return re.test(val);
+    }
+
+    function isEmail (val) {
+        var email = /^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
+        return email.test(val);
+    }
+
+    function isDate (val, format, retDate) {
+        if (!val) return false;
+
+        var dt   = 'Invalid Date';
+        var month, day, year;
+
+        if (format == null) format = w2utils.settings.dateFormat;
+
+        if (typeof val.getUTCFullYear === 'function') { // date object
+            year  = val.getUTCFullYear();
+            month = val.getUTCMonth() + 1;
+            day   = val.getUTCDate();
+        } else if (parseInt(val) == val && parseInt(val) > 0) {
+            val = new Date(parseInt(val));
+            year  = val.getUTCFullYear();
+            month = val.getUTCMonth() + 1;
+            day   = val.getUTCDate();
+        } else {
+            val = String(val);
+            // convert month formats
+            if (new RegExp('mon', 'ig').test(format)) {
+                format = format.replace(/month/ig, 'm').replace(/mon/ig, 'm').replace(/dd/ig, 'd').replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
+                val    = val.replace(/[, ]/ig, '/').replace(/\/\//g, '/').toLowerCase();
+                for (var m = 0, len = w2utils.settings.fullmonths.length; m < len; m++) {
+                    var t = w2utils.settings.fullmonths[m];
+                    val = val.replace(new RegExp(t, 'ig'), (parseInt(m) + 1)).replace(new RegExp(t.substr(0, 3), 'ig'), (parseInt(m) + 1));
+                }
+            }
+            // format date
+            var tmp  = val.replace(/-/g, '/').replace(/\./g, '/').toLowerCase().split('/');
+            var tmp2 = format.replace(/-/g, '/').replace(/\./g, '/').toLowerCase();
+            if (tmp2 === 'mm/dd/yyyy') { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+            if (tmp2 === 'm/d/yyyy')   { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+            if (tmp2 === 'dd/mm/yyyy') { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
+            if (tmp2 === 'd/m/yyyy')   { month = tmp[1]; day = tmp[0]; year = tmp[2]; }
+            if (tmp2 === 'yyyy/dd/mm') { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
+            if (tmp2 === 'yyyy/d/m')   { month = tmp[2]; day = tmp[1]; year = tmp[0]; }
+            if (tmp2 === 'yyyy/mm/dd') { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
+            if (tmp2 === 'yyyy/m/d')   { month = tmp[1]; day = tmp[2]; year = tmp[0]; }
+            if (tmp2 === 'mm/dd/yy')   { month = tmp[0]; day = tmp[1]; year = tmp[2]; }
+            if (tmp2 === 'm/d/yy')     { month = tmp[0]; day = tmp[1]; year = parseInt(tmp[2]) + 1900; }
+            if (tmp2 === 'dd/mm/yy')   { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
+            if (tmp2 === 'd/m/yy')     { month = tmp[1]; day = tmp[0]; year = parseInt(tmp[2]) + 1900; }
+            if (tmp2 === 'yy/dd/mm')   { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
+            if (tmp2 === 'yy/d/m')     { month = tmp[2]; day = tmp[1]; year = parseInt(tmp[0]) + 1900; }
+            if (tmp2 === 'yy/mm/dd')   { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
+            if (tmp2 === 'yy/m/d')     { month = tmp[1]; day = tmp[2]; year = parseInt(tmp[0]) + 1900; }
+        }
+        if (!isInt(year)) return false;
+        if (!isInt(month)) return false;
+        if (!isInt(day)) return false;
+        year  = +year;
+        month = +month;
+        day   = +day;
+        dt    = new Date(year, month - 1, day);
+        // do checks
+        if (month == null) return false;
+        if (String(dt) == 'Invalid Date') return false;
+        if ((dt.getMonth() + 1 !== month) || (dt.getDate() !== day) || (dt.getFullYear() !== year)) return false;
+        if (retDate === true) return dt; else return true;
+    }
+
+    function isTime (val, retTime) {
+        // Both formats 10:20pm and 22:20
+        if (val == null) return false;
+        var max, am, pm;
+        // -- process american format
+        val = String(val);
+        val = val.toUpperCase();
+        am = val.indexOf('AM') >= 0;
+        pm = val.indexOf('PM') >= 0;
+        var ampm = (pm || am);
+        if (ampm) max = 12; else max = 24;
+        val = val.replace('AM', '').replace('PM', '');
+        val = $.trim(val);
+        // ---
+        var tmp = val.split(':');
+        var h = parseInt(tmp[0] || 0), m = parseInt(tmp[1] || 0), s = parseInt(tmp[2] || 0);
+        // accept edge case: 3PM is a good timestamp, but 3 (without AM or PM) is NOT:
+        if ((!ampm || tmp.length !== 1) && tmp.length !== 2 && tmp.length !== 3) { return false; }
+        if (tmp[0] === '' || h < 0 || h > max || !this.isInt(tmp[0]) || tmp[0].length > 2) { return false; }
+        if (tmp.length > 1 && (tmp[1] === '' || m < 0 || m > 59 || !this.isInt(tmp[1]) || tmp[1].length !== 2)) { return false; }
+        if (tmp.length > 2 && (tmp[2] === '' || s < 0 || s > 59 || !this.isInt(tmp[2]) || tmp[2].length !== 2)) { return false; }
+        // check the edge cases: 12:01AM is ok, as is 12:01PM, but 24:01 is NOT ok while 24:00 is (midnight; equivalent to 00:00).
+        // meanwhile, there is 00:00 which is ok, but 0AM nor 0PM are okay, while 0:01AM and 0:00AM are.
+        if (!ampm && max === h && (m !== 0 || s !== 0)) { return false; }
+        if (ampm && tmp.length === 1 && h === 0) { return false; }
+
+        if (retTime === true) {
+            if (pm && h !== 12) h += 12;   // 12:00pm - is noon
+            if (am && h === 12) h += 12;   // 12:00am - is midnight
+            return {
+                hours: h,
+                minutes: m,
+                seconds: s
+            };
+        }
+        return true;
+    }
+
+    function isDateTime (val, format, retDate) {
+        if (format == null) format = w2utils.settings.datetimeFormat;
+        var formats = format.split('|');
+        if (typeof val.getUTCFullYear === 'function') { // date object
+            if (retDate !== true) return true;
+            return val;
+        } else if (parseInt(val) == val && parseInt(val) > 0) {
+            val = new Date(parseInt(val));
+            if (retDate !== true) return true;
+            return val;
+        } else {
+            var tmp = String(val).indexOf(' ');
+            var values  = [val.substr(0, tmp), val.substr(tmp).trim()];
+            formats[0] = formats[0].trim();
+            if (formats[1]) formats[1] = formats[1].trim();
+            // check
+            var tmp1 = w2utils.isDate(values[0], formats[0], true);
+            var tmp2 = w2utils.isTime(values[1], true);
+            if (tmp1 !== false && tmp2 !== false) {
+                if (retDate !== true) return true;
+                tmp1.setHours(tmp2.hours);
+                tmp1.setMinutes(tmp2.minutes);
+                tmp1.setSeconds(tmp2.seconds);
+                return tmp1;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    function age(dateStr) {
+        var d1;
+        if (dateStr === '' || dateStr == null) return '';
+        if (typeof dateStr.getUTCFullYear === 'function') { // date object
+            d1 = dateStr;
+        } else if (parseInt(dateStr) == dateStr && parseInt(dateStr) > 0) {
+            d1 = new Date(parseInt(dateStr));
+        } else {
+            d1 = new Date(dateStr);
+        }
+        if (String(d1) == 'Invalid Date') return '';
+
+        var d2  = new Date();
+        var sec = (d2.getTime() - d1.getTime()) / 1000;
+        var amount = '';
+        var type   = '';
+        if (sec < 0) {
+            amount = 0;
+            type   = 'sec';
+        } else if (sec < 60) {
+            amount = Math.floor(sec);
+            type   = 'sec';
+            if (sec < 0) { amount = 0; type = 'sec'; }
+        } else if (sec < 60*60) {
+            amount = Math.floor(sec/60);
+            type   = 'min';
+        } else if (sec < 24*60*60) {
+            amount = Math.floor(sec/60/60);
+            type   = 'hour';
+        } else if (sec < 30*24*60*60) {
+            amount = Math.floor(sec/24/60/60);
+            type   = 'day';
+        } else if (sec < 365*24*60*60) {
+            amount = Math.floor(sec/30/24/60/60*10)/10;
+            type   = 'month';
+        } else if (sec < 365*4*24*60*60) {
+            amount = Math.floor(sec/365/24/60/60*10)/10;
+            type   = 'year';
+        } else if (sec >= 365*4*24*60*60) {
+            // factor in leap year shift (only older then 4 years)
+            amount = Math.floor(sec/365.25/24/60/60*10)/10;
+            type   = 'year';
+        }
+        return amount + ' ' + type + (amount > 1 ? 's' : '');
+    }
+
+    function interval (value) {
+        var ret = '';
+        if (value < 1000) {
+            ret = "< 1 sec";
+        } else if (value < 60000) {
+            ret = Math.floor(value / 1000) + " secs";
+        } else if (value < 3600000) {
+            ret = Math.floor(value / 60000) + " mins";
+        } else if (value < 86400000) {
+            ret = Math.floor(value / 3600000 * 10) / 10 + " hours";
+        } else if (value < 2628000000) {
+            ret = Math.floor(value / 86400000 * 10) / 10 + " days";
+        } else if (value < 3.1536e+10) {
+            ret = Math.floor(value / 2628000000 * 10) / 10 + " months";
+        } else {
+            ret = Math.floor(value / 3.1536e+9) / 10 + " years";
+        }
+        return ret;
+    }
+
+    function date (dateStr) {
+        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' && !dateStr.getMonth)) return '';
+        var d1 = new Date(dateStr);
+        if (w2utils.isInt(dateStr)) d1 = new Date(Number(dateStr)); // for unix timestamps
+        if (String(d1) == 'Invalid Date') return '';
+
+        var months = w2utils.settings.shortmonths;
+        var d2   = new Date(); // today
+        var d3   = new Date();
+        d3.setTime(d3.getTime() - 86400000); // yesterday
+
+        var dd1  = months[d1.getMonth()] + ' ' + d1.getDate() + ', ' + d1.getFullYear();
+        var dd2  = months[d2.getMonth()] + ' ' + d2.getDate() + ', ' + d2.getFullYear();
+        var dd3  = months[d3.getMonth()] + ' ' + d3.getDate() + ', ' + d3.getFullYear();
+
+        var time = (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
+        var time2= (d1.getHours() - (d1.getHours() > 12 ? 12 :0)) + ':' + (d1.getMinutes() < 10 ? '0' : '') + d1.getMinutes() + ':' + (d1.getSeconds() < 10 ? '0' : '') + d1.getSeconds() + ' ' + (d1.getHours() >= 12 ? 'pm' : 'am');
+        var dsp = dd1;
+        if (dd1 === dd2) dsp = time;
+        if (dd1 === dd3) dsp = w2utils.lang('Yesterday');
+
+        return '<span title="'+ dd1 +' ' + time2 +'">'+ dsp +'</span>';
+    }
+
+    function formatSize (sizeStr) {
+        if (!w2utils.isFloat(sizeStr) || sizeStr === '') return '';
+        sizeStr = parseFloat(sizeStr);
+        if (sizeStr === 0) return 0;
+        var sizes = ['Bt', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB'];
+        var i = parseInt( Math.floor( Math.log(sizeStr) / Math.log(1024) ) );
+        return (Math.floor(sizeStr / Math.pow(1024, i) * 10) / 10).toFixed(i === 0 ? 0 : 1) + ' ' + (sizes[i] || '??');
+    }
+
+    function formatNumber (val, fraction, useGrouping) {
+        if (val == null || val === '' || typeof val == 'object') return '';
+        var options = {
+            minimumFractionDigits : fraction,
+            maximumFractionDigits : fraction,
+            useGrouping : useGrouping
+        };
+        if (fraction == null || fraction < 0) {
+            options.minimumFractionDigits = 0;
+            options.maximumFractionDigits = 20;
+        }
+        return parseFloat(val).toLocaleString(w2utils.settings.locale, options);
+    }
+
+    function formatDate (dateStr, format) { // IMPORTANT dateStr HAS TO BE valid JavaScript Date String
+        if (!format) format = this.settings.dateFormat;
+        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' && !dateStr.getMonth)) return '';
+
+        var dt = new Date(dateStr);
+        if (w2utils.isInt(dateStr)) dt = new Date(Number(dateStr)); // for unix timestamps
+        if (String(dt) == 'Invalid Date') return '';
+
+        var year  = dt.getFullYear();
+        var month = dt.getMonth();
+        var date  = dt.getDate();
+        return format.toLowerCase()
+            .replace('month', w2utils.settings.fullmonths[month])
+            .replace('mon', w2utils.settings.shortmonths[month])
+            .replace(/yyyy/g, year)
+            .replace(/yyy/g, year)
+            .replace(/yy/g, year > 2000 ? 100 + parseInt(String(year).substr(2)) : String(year).substr(2))
+            .replace(/(^|[^a-z$])y/g, '$1' + year)            // only y's that are not preceded by a letter
+            .replace(/mm/g, (month + 1 < 10 ? '0' : '') + (month + 1))
+            .replace(/dd/g, (date < 10 ? '0' : '') + date)
+            .replace(/th/g, (date == 1 ? 'st' : 'th'))
+            .replace(/th/g, (date == 2 ? 'nd' : 'th'))
+            .replace(/th/g, (date == 3 ? 'rd' : 'th'))
+            .replace(/(^|[^a-z$])m/g, '$1' + (month + 1))     // only y's that are not preceded by a letter
+            .replace(/(^|[^a-z$])d/g, '$1' + date);           // only y's that are not preceded by a letter
+    }
+
+    function formatTime (dateStr, format) { // IMPORTANT dateStr HAS TO BE valid JavaScript Date String
+        var months = w2utils.settings.shortmonths;
+        var fullMonths = w2utils.settings.fullmonths;
+        if (!format) format = this.settings.timeFormat;
+        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' && !dateStr.getMonth)) return '';
+
+        var dt = new Date(dateStr);
+        if (w2utils.isInt(dateStr)) dt  = new Date(Number(dateStr)); // for unix timestamps
+        if (w2utils.isTime(dateStr)) {
+            var tmp = w2utils.isTime(dateStr, true);
+            dt = new Date();
+            dt.setHours(tmp.hours);
+            dt.setMinutes(tmp.minutes);
+        }
+        if (String(dt) == 'Invalid Date') return '';
+
+        var type = 'am';
+        var hour = dt.getHours();
+        var h24  = dt.getHours();
+        var min  = dt.getMinutes();
+        var sec  = dt.getSeconds();
+        if (min < 10) min = '0' + min;
+        if (sec < 10) sec = '0' + sec;
+        if (format.indexOf('am') !== -1 || format.indexOf('pm') !== -1) {
+            if (hour >= 12) type = 'pm';
+            if (hour > 12)  hour = hour - 12;
+        }
+        return format.toLowerCase()
+            .replace('am', type)
+            .replace('pm', type)
+            .replace('hhh', (hour < 10 ? '0' + hour : hour))
+            .replace('hh24', (h24 < 10 ? '0' + h24 : h24))
+            .replace('h24', h24)
+            .replace('hh', hour)
+            .replace('mm', min)
+            .replace('mi', min)
+            .replace('ss', sec)
+            .replace(/(^|[^a-z$])h/g, '$1' + hour)    // only y's that are not preceded by a letter
+            .replace(/(^|[^a-z$])m/g, '$1' + min)     // only y's that are not preceded by a letter
+            .replace(/(^|[^a-z$])s/g, '$1' + sec);    // only y's that are not preceded by a letter
+    }
+
+    function formatDateTime(dateStr, format) {
+        var fmt;
+        if (dateStr === '' || dateStr == null || (typeof dateStr == 'object' && !dateStr.getMonth)) return '';
+        if (typeof format !== 'string') {
+            fmt = [this.settings.dateFormat, this.settings.timeFormat];
+        } else {
+            fmt = format.split('|');
+            fmt[0] = fmt[0].trim();
+            fmt[1] = fmt[1].trim();
+        }
+        // older formats support
+        if (fmt[1] == 'h12') fmt[1] = 'h:m pm';
+        if (fmt[1] == 'h24') fmt[1] = 'h24:m';
+        return this.formatDate(dateStr, fmt[0]) + ' ' + this.formatTime(dateStr, fmt[1]);
+    }
+
+    function stripTags (html) {
+        if (html == null) return html;
+        switch (typeof html) {
+            case 'number':
+                break;
+            case 'string':
+                html = String(html).replace(/(<([^>]+)>)/ig, "");
+                break;
+            case 'object':
+                // does not modify original object, but creates a copy
+                if (Array.isArray(html)) {
+                    html = $.extend(true, [], html);
+                    for (var i = 0; i < html.length; i++) html[i] = this.stripTags(html[i]);
+                }  else {
+                    html = $.extend(true, {}, html);
+                    for (var i in html) html[i] = this.stripTags(html[i]);
+                }
+                break;
+        }
+        return html;
+    }
+
+    function encodeTags (html) {
+        if (html == null) return html;
+        switch (typeof html) {
+            case 'number':
+                break;
+            case 'string':
+                html = String(html).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
+                break;
+            case 'object':
+                // does not modify original object, but creates a copy
+                if (Array.isArray(html)) {
+                    html = $.extend(true, [], html);
+                    for (var i = 0; i < html.length; i++) html[i] = this.encodeTags(html[i]);
+                }  else {
+                    html = $.extend(true, {}, html);
+                    for (var i in html) html[i] = this.encodeTags(html[i]);
+                }
+                break;
+        }
+        return html;
+    }
+
+    function decodeTags (html) {
+        if (html == null) return html;
+        switch (typeof html) {
+            case 'number':
+                break;
+            case 'string':
+                html = String(html).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
+                break;
+            case 'object':
+                // does not modify original object, but creates a copy
+                if (Array.isArray(html)) {
+                    html = $.extend(true, [], html);
+                    for (var i = 0; i < html.length; i++) html[i] = this.decodeTags(html[i]);
+                }  else {
+                    html = $.extend(true, {}, html);
+                    for (var i in html) html[i] = this.decodeTags(html[i]);
+                }
+                break;
+        }
+        return html;
+    }
+
+    function escapeId (id) {
+        if (id === '' || id == null) return '';
+        return String(id).replace(/([;&,\.\+\*\~'`:"\!\^#$%@\[\]\(\)=<>\|\/? {}\\])/g, '\\$1');
+    }
+
+    function base64encode (input) {
+        var output = "";
+        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+        var i = 0;
+        var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+        input = utf8_encode(input);
+
+        while (i < input.length) {
+            chr1 = input.charCodeAt(i++);
+            chr2 = input.charCodeAt(i++);
+            chr3 = input.charCodeAt(i++);
+            enc1 = chr1 >> 2;
+            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+            enc4 = chr3 & 63;
+            if (isNaN(chr2)) {
+                enc3 = enc4 = 64;
+            } else if (isNaN(chr3)) {
+                enc4 = 64;
+            }
+            output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
+        }
+
+        function utf8_encode (string) {
+            string = String(string).replace(/\r\n/g,"\n");
+            var utftext = "";
+
+            for (var n = 0; n < string.length; n++) {
+                var c = string.charCodeAt(n);
+                if (c < 128) {
+                    utftext += String.fromCharCode(c);
+                }
+                else if((c > 127) && (c < 2048)) {
+                    utftext += String.fromCharCode((c >> 6) | 192);
+                    utftext += String.fromCharCode((c & 63) | 128);
+                }
+                else {
+                    utftext += String.fromCharCode((c >> 12) | 224);
+                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                    utftext += String.fromCharCode((c & 63) | 128);
+                }
+            }
+            return utftext;
+        }
+
+        return output;
+    }
+
+    function base64decode (input) {
+        var output = "";
+        var chr1, chr2, chr3;
+        var enc1, enc2, enc3, enc4;
+        var i = 0;
+        var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+        while (i < input.length) {
+            enc1 = keyStr.indexOf(input.charAt(i++));
+            enc2 = keyStr.indexOf(input.charAt(i++));
+            enc3 = keyStr.indexOf(input.charAt(i++));
+            enc4 = keyStr.indexOf(input.charAt(i++));
+            chr1 = (enc1 << 2) | (enc2 >> 4);
+            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+            chr3 = ((enc3 & 3) << 6) | enc4;
+            output = output + String.fromCharCode(chr1);
+            if (enc3 !== 64) {
+                output = output + String.fromCharCode(chr2);
+            }
+            if (enc4 !== 64) {
+                output = output + String.fromCharCode(chr3);
+            }
+        }
+        output = utf8_decode(output);
+
+        function utf8_decode (utftext) {
+            var string = "";
+            var i = 0;
+            var c = 0, c2, c3;
+
+            while ( i < utftext.length ) {
+                c = utftext.charCodeAt(i);
+                if (c < 128) {
+                    string += String.fromCharCode(c);
+                    i++;
+                }
+                else if((c > 191) && (c < 224)) {
+                    c2 = utftext.charCodeAt(i+1);
+                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+                    i += 2;
+                }
+                else {
+                    c2 = utftext.charCodeAt(i+1);
+                    c3 = utftext.charCodeAt(i+2);
+                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+                    i += 3;
+                }
+            }
+
+            return string;
+        }
+
+        return output;
+    }
+
+    function md5(input) {
+        /*
+         * Based on http://pajhome.org.uk/crypt/md5
+         */
+
+        var hexcase = 0;
+        var b64pad = "";
+
+        function __pj_crypt_hex_md5(s) {
+            return __pj_crypt_rstr2hex(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)));
+        }
+        function __pj_crypt_b64_md5(s) {
+            return __pj_crypt_rstr2b64(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)));
+        }
+        function __pj_crypt_any_md5(s, e) {
+            return __pj_crypt_rstr2any(__pj_crypt_rstr_md5(__pj_crypt_str2rstr_utf8(s)), e);
+        }
+        function __pj_crypt_hex_hmac_md5(k, d)
+        {
+            return __pj_crypt_rstr2hex(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)));
+        }
+        function __pj_crypt_b64_hmac_md5(k, d)
+        {
+            return __pj_crypt_rstr2b64(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)));
+        }
+        function __pj_crypt_any_hmac_md5(k, d, e)
+        {
+            return __pj_crypt_rstr2any(__pj_crypt_rstr_hmac_md5(__pj_crypt_str2rstr_utf8(k), __pj_crypt_str2rstr_utf8(d)), e);
+        }
+
+        /*
+         * Calculate the MD5 of a raw string
+         */
+        function __pj_crypt_rstr_md5(s)
+        {
+            return __pj_crypt_binl2rstr(__pj_crypt_binl_md5(__pj_crypt_rstr2binl(s), s.length * 8));
+        }
+
+        /*
+         * Calculate the HMAC-MD5, of a key and some data (raw strings)
+         */
+        function __pj_crypt_rstr_hmac_md5(key, data)
+        {
+            var bkey = __pj_crypt_rstr2binl(key);
+            if (bkey.length > 16)
+                bkey = __pj_crypt_binl_md5(bkey, key.length * 8);
+
+            var ipad = Array(16), opad = Array(16);
+            for (var i = 0; i < 16; i++)
+            {
+                ipad[i] = bkey[i] ^ 0x36363636;
+                opad[i] = bkey[i] ^ 0x5C5C5C5C;
+            }
+
+            var hash = __pj_crypt_binl_md5(ipad.concat(__pj_crypt_rstr2binl(data)), 512 + data.length * 8);
+            return __pj_crypt_binl2rstr(__pj_crypt_binl_md5(opad.concat(hash), 512 + 128));
+        }
+
+        /*
+         * Convert a raw string to a hex string
+         */
+        function __pj_crypt_rstr2hex(input)
+        {
+            try {
+                hexcase
+            } catch (e) {
+                hexcase = 0;
+            }
+            var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+            var output = "";
+            var x;
+            for (var i = 0; i < input.length; i++)
+            {
+                x = input.charCodeAt(i);
+                output += hex_tab.charAt((x >>> 4) & 0x0F)
+                        + hex_tab.charAt(x & 0x0F);
+            }
+            return output;
+        }
+
+        /*
+         * Convert a raw string to a base-64 string
+         */
+        function __pj_crypt_rstr2b64(input)
+        {
+            try {
+                b64pad
+            } catch (e) {
+                b64pad = '';
+            }
+            var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+            var output = "";
+            var len = input.length;
+            for (var i = 0; i < len; i += 3)
+            {
+                var triplet = (input.charCodeAt(i) << 16)
+                        | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0)
+                        | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
+                for (var j = 0; j < 4; j++)
+                {
+                    if (i * 8 + j * 6 > input.length * 8)
+                        output += b64pad;
+                    else
+                        output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
+                }
+            }
+            return output;
+        }
+
+        /*
+         * Convert a raw string to an arbitrary string encoding
+         */
+        function __pj_crypt_rstr2any(input, encoding)
+        {
+            var divisor = encoding.length;
+            var i, j, q, x, quotient;
+
+            /* Convert to an array of 16-bit big-endian values, forming the dividend */
+            var dividend = Array(Math.ceil(input.length / 2));
+            for (i = 0; i < dividend.length; i++)
+            {
+                dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
+            }
+
+            /*
+             * Repeatedly perform a long division. The binary array forms the dividend,
+             * the length of the encoding is the divisor. Once computed, the quotient
+             * forms the dividend for the next step. All remainders are stored for later
+             * use.
+             */
+            var full_length = Math.ceil(input.length * 8 /
+                    (Math.log(encoding.length) / Math.log(2)));
+            var remainders = Array(full_length);
+            for (j = 0; j < full_length; j++)
+            {
+                quotient = Array();
+                x = 0;
+                for (i = 0; i < dividend.length; i++)
+                {
+                    x = (x << 16) + dividend[i];
+                    q = Math.floor(x / divisor);
+                    x -= q * divisor;
+                    if (quotient.length > 0 || q > 0)
+                        quotient[quotient.length] = q;
+                }
+                remainders[j] = x;
+                dividend = quotient;
+            }
+
+            /* Convert the remainders to the output string */
+            var output = "";
+            for (i = remainders.length - 1; i >= 0; i--)
+                output += encoding.charAt(remainders[i]);
+
+            return output;
+        }
+
+        /*
+         * Encode a string as utf-8.
+         * For efficiency, this assumes the input is valid utf-16.
+         */
+        function __pj_crypt_str2rstr_utf8(input)
+        {
+            var output = "";
+            var i = -1;
+            var x, y;
+
+            while (++i < input.length)
+            {
+                /* Decode utf-16 surrogate pairs */
+                x = input.charCodeAt(i);
+                y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
+                if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
+                {
+                    x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
+                    i++;
+                }
+
+                /* Encode output as utf-8 */
+                if (x <= 0x7F)
+                    output += String.fromCharCode(x);
+                else if (x <= 0x7FF)
+                    output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
+                            0x80 | (x & 0x3F));
+                else if (x <= 0xFFFF)
+                    output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
+                            0x80 | ((x >>> 6) & 0x3F),
+                            0x80 | (x & 0x3F));
+                else if (x <= 0x1FFFFF)
+                    output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
+                            0x80 | ((x >>> 12) & 0x3F),
+                            0x80 | ((x >>> 6) & 0x3F),
+                            0x80 | (x & 0x3F));
+            }
+            return output;
+        }
+
+        /*
+         * Encode a string as utf-16
+         */
+        function __pj_crypt_str2rstr_utf16le(input)
+        {
+            var output = "";
+            for (var i = 0; i < input.length; i++)
+                output += String.fromCharCode(input.charCodeAt(i) & 0xFF,
+                        (input.charCodeAt(i) >>> 8) & 0xFF);
+            return output;
+        }
+
+        function __pj_crypt_str2rstr_utf16be(input)
+        {
+            var output = "";
+            for (var i = 0; i < input.length; i++)
+                output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
+                        input.charCodeAt(i) & 0xFF);
+            return output;
+        }
+
+        /*
+         * Convert a raw string to an array of little-endian words
+         * Characters >255 have their high-byte silently ignored.
+         */
+        function __pj_crypt_rstr2binl(input)
+        {
+            var output = Array(input.length >> 2);
+            for (var i = 0; i < output.length; i++)
+                output[i] = 0;
+            for (var i = 0; i < input.length * 8; i += 8)
+                output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
+            return output;
+        }
+
+        /*
+         * Convert an array of little-endian words to a string
+         */
+        function __pj_crypt_binl2rstr(input)
+        {
+            var output = "";
+            for (var i = 0; i < input.length * 32; i += 8)
+                output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
+            return output;
+        }
+
+        /*
+         * Calculate the MD5 of an array of little-endian words, and a bit length.
+         */
+        function __pj_crypt_binl_md5(x, len)
+        {
+            /* append padding */
+            x[len >> 5] |= 0x80 << ((len) % 32);
+            x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+            var a = 1732584193;
+            var b = -271733879;
+            var c = -1732584194;
+            var d = 271733878;
+
+            for (var i = 0; i < x.length; i += 16)
+            {
+                var olda = a;
+                var oldb = b;
+                var oldc = c;
+                var oldd = d;
+
+                a = __pj_crypt_md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
+                d = __pj_crypt_md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+                c = __pj_crypt_md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+                b = __pj_crypt_md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+                a = __pj_crypt_md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+                d = __pj_crypt_md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+                c = __pj_crypt_md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+                b = __pj_crypt_md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+                a = __pj_crypt_md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+                d = __pj_crypt_md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+                c = __pj_crypt_md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+                b = __pj_crypt_md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+                a = __pj_crypt_md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+                d = __pj_crypt_md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+                c = __pj_crypt_md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+                b = __pj_crypt_md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+                a = __pj_crypt_md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+                d = __pj_crypt_md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+                c = __pj_crypt_md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+                b = __pj_crypt_md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
+                a = __pj_crypt_md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+                d = __pj_crypt_md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+                c = __pj_crypt_md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+                b = __pj_crypt_md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+                a = __pj_crypt_md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+                d = __pj_crypt_md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+                c = __pj_crypt_md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+                b = __pj_crypt_md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+                a = __pj_crypt_md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+                d = __pj_crypt_md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+                c = __pj_crypt_md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+                b = __pj_crypt_md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+                a = __pj_crypt_md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+                d = __pj_crypt_md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+                c = __pj_crypt_md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+                b = __pj_crypt_md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+                a = __pj_crypt_md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+                d = __pj_crypt_md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+                c = __pj_crypt_md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+                b = __pj_crypt_md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+                a = __pj_crypt_md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+                d = __pj_crypt_md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
+                c = __pj_crypt_md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+                b = __pj_crypt_md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+                a = __pj_crypt_md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+                d = __pj_crypt_md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+                c = __pj_crypt_md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+                b = __pj_crypt_md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+                a = __pj_crypt_md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
+                d = __pj_crypt_md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+                c = __pj_crypt_md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+                b = __pj_crypt_md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+                a = __pj_crypt_md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+                d = __pj_crypt_md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+                c = __pj_crypt_md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+                b = __pj_crypt_md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+                a = __pj_crypt_md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+                d = __pj_crypt_md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+                c = __pj_crypt_md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+                b = __pj_crypt_md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+                a = __pj_crypt_md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+                d = __pj_crypt_md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+                c = __pj_crypt_md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+                b = __pj_crypt_md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+                a = __pj_crypt_safe_add(a, olda);
+                b = __pj_crypt_safe_add(b, oldb);
+                c = __pj_crypt_safe_add(c, oldc);
+                d = __pj_crypt_safe_add(d, oldd);
+            }
+            return Array(a, b, c, d);
+        }
+
+        /*
+         * These functions implement the four basic operations the algorithm uses.
+         */
+        function __pj_crypt_md5_cmn(q, a, b, x, s, t)
+        {
+            return __pj_crypt_safe_add(__pj_crypt_bit_rol(__pj_crypt_safe_add(__pj_crypt_safe_add(a, q), __pj_crypt_safe_add(x, t)), s), b);
+        }
+        function __pj_crypt_md5_ff(a, b, c, d, x, s, t)
+        {
+            return __pj_crypt_md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+        }
+        function __pj_crypt_md5_gg(a, b, c, d, x, s, t)
+        {
+            return __pj_crypt_md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+        }
+        function __pj_crypt_md5_hh(a, b, c, d, x, s, t)
+        {
+            return __pj_crypt_md5_cmn(b ^ c ^ d, a, b, x, s, t);
+        }
+        function __pj_crypt_md5_ii(a, b, c, d, x, s, t)
+        {
+            return __pj_crypt_md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+        }
+
+        /*
+         * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+         * to work around bugs in some JS interpreters.
+         */
+        function __pj_crypt_safe_add(x, y)
+        {
+            var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+            var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+            return (msw << 16) | (lsw & 0xFFFF);
+        }
+
+        /*
+         * Bitwise rotate a 32-bit number to the left.
+         */
+        function __pj_crypt_bit_rol(num, cnt)
+        {
+            return (num << cnt) | (num >>> (32 - cnt));
+        }
+
+        return __pj_crypt_hex_md5(input);
+
+    }
+
+    function transition (div_old, div_new, type, callBack) {
+        var width  = $(div_old).width();
+        var height = $(div_old).height();
+        var time   = 0.5;
+
+        if (!div_old || !div_new) {
+            console.log('ERROR: Cannot do transition when one of the divs is null');
+            return;
+        }
+
+        div_old.parentNode.style.cssText += 'perspective: 900px; overflow: hidden;';
+        div_old.style.cssText += '; position: absolute; z-index: 1019; backface-visibility: hidden';
+        div_new.style.cssText += '; position: absolute; z-index: 1020; backface-visibility: hidden';
+
+        switch (type) {
+            case 'slide-left':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; transform: translate3d('+ width + 'px, 0, 0)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(-'+ width +'px, 0, 0)';
+                }, 1);
+                break;
+
+            case 'slide-right':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; transform: translate3d(-'+ width +'px, 0, 0)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0px, 0, 0)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d('+ width +'px, 0, 0)';
+                }, 1);
+                break;
+
+            case 'slide-down':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; z-index: 1; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; z-index: 0; transform: translate3d(0, 0, 0)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, '+ height +'px, 0)';
+                }, 1);
+                break;
+
+            case 'slide-up':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, '+ height +'px, 0)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: translate3d(0, 0, 0)';
+                }, 1);
+                break;
+
+            case 'flip-left':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: rotateY(0deg)';
+                div_new.style.cssText += 'overflow: hidden; transform: rotateY(-180deg)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: rotateY(0deg)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: rotateY(180deg)';
+                }, 1);
+                break;
+
+            case 'flip-right':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: rotateY(0deg)';
+                div_new.style.cssText += 'overflow: hidden; transform: rotateY(180deg)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: rotateY(0deg)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: rotateY(-180deg)';
+                }, 1);
+                break;
+
+            case 'flip-down':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: rotateX(0deg)';
+                div_new.style.cssText += 'overflow: hidden; transform: rotateX(180deg)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: rotateX(0deg)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: rotateX(-180deg)';
+                }, 1);
+                break;
+
+            case 'flip-up':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: rotateX(0deg)';
+                div_new.style.cssText += 'overflow: hidden; transform: rotateX(-180deg)';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: rotateX(0deg)';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: rotateX(180deg)';
+                }, 1);
+                break;
+
+            case 'pop-in':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(.8); opacity: 0;';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; transform: scale(1); opacity: 1;';
+                    div_old.style.cssText += 'transition: '+ time +'s;';
+                }, 1);
+                break;
+
+            case 'pop-out':
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); transform: scale(1); opacity: 1;';
+                div_new.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0); opacity: 0;';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; opacity: 1;';
+                    div_old.style.cssText += 'transition: '+ time +'s; transform: scale(1.7); opacity: 0;';
+                }, 1);
+                break;
+
+            default:
+                // init divs
+                div_old.style.cssText += 'overflow: hidden; transform: translate3d(0, 0, 0)';
+                div_new.style.cssText += 'overflow: hidden; translate3d(0, 0, 0); opacity: 0;';
+                $(div_new).show();
+                // -- need a timing function because otherwise not working
+                window.setTimeout(function() {
+                    div_new.style.cssText += 'transition: '+ time +'s; opacity: 1;';
+                    div_old.style.cssText += 'transition: '+ time +'s';
+                }, 1);
+                break;
+        }
+
+        setTimeout(function () {
+            if (type === 'slide-down') {
+                $(div_old).css('z-index', '1019');
+                $(div_new).css('z-index', '1020');
+            }
+            if (div_new) {
+                $(div_new).css({ 'opacity': '1' }).css(w2utils.cssPrefix({
+                    'transition': '',
+                    'transform' : ''
+                }));
+            }
+            if (div_old) {
+                $(div_old).css({ 'opacity': '1' }).css(w2utils.cssPrefix({
+                    'transition': '',
+                    'transform' : ''
+                }));
+            }
+            if (typeof callBack === 'function') callBack();
+        }, time * 1000);
+    }
+
+    function lock (box, msg, spinner) {
+        var options = {};
+        if (typeof msg === 'object') {
+            options = msg;
+        } else {
+            options.msg     = msg;
+            options.spinner = spinner;
+        }
+        if (!options.msg && options.msg !== 0) options.msg = '';
+        w2utils.unlock(box);
+        $(box).prepend(
+            '<div class="w2ui-lock"></div>'+
+            '<div class="w2ui-lock-msg"></div>'
+        );
+        var $lock = $(box).find('.w2ui-lock');
+        var mess = $(box).find('.w2ui-lock-msg');
+        if (!options.msg) mess.css({ 'background-color': 'transparent', 'border': '0px' });
+        if (options.spinner === true) options.msg = '<div class="w2ui-spinner" '+ (!options.msg ? 'style="width: 35px; height: 35px"' : '') +'></div>' + options.msg;
+        if (options.opacity != null) $lock.css('opacity', options.opacity);
+        if (typeof $lock.fadeIn == 'function') {
+            $lock.fadeIn(200);
+            mess.html(options.msg).fadeIn(200);
+        } else {
+            $lock.show();
+            mess.html(options.msg).show(0);
+        }
+    }
+
+    function unlock (box, speed) {
+        if (isInt(speed)) {
+            $(box).find('.w2ui-lock').fadeOut(speed);
+            setTimeout(function () {
+                $(box).find('.w2ui-lock').remove();
+                $(box).find('.w2ui-lock-msg').remove();
+            }, speed);
+        } else {
+            $(box).find('.w2ui-lock').remove();
+            $(box).find('.w2ui-lock-msg').remove();
+        }
+    }
+
+    /**
+    *  Used in w2popup, w2grid, w2form, w2layout
+    *  should be called with .call(...) method
+    */
+
+    function message(where, options) {
+        var obj = this, closeTimer, edata;
+        // var where.path    = 'w2popup';
+        // var where.title   = '.w2ui-popup-title';
+        // var where.body    = '.w2ui-box';
+        $().w2tag(); // hide all tags
+        if (!options) options = { width: 200, height: 100 };
+        if (options.on == null) $.extend(options, w2utils.event);
+        if (options.width == null) options.width = 200;
+        if (options.height == null) options.height = 100;
+        var pWidth  = parseInt($(where.box).width());
+        var pHeight = parseInt($(where.box).height());
+        var titleHeight = parseInt($(where.box).find(where.title).css('height') || 0);
+        if (options.width > pWidth) options.width = pWidth - 10;
+        if (options.height > pHeight - titleHeight) options.height = pHeight - 10 - titleHeight;
+        options.originalWidth  = options.width;
+        options.originalHeight = options.height;
+        if (parseInt(options.width) < 0)   options.width  = pWidth + options.width;
+        if (parseInt(options.width) < 10)  options.width  = 10;
+        if (parseInt(options.height) < 0)  options.height  = pHeight + options.height - titleHeight;
+        if (parseInt(options.height) < 10) options.height = 10;
+        if (options.hideOnClick == null) options.hideOnClick = false;
+        var poptions = $(where.box).data('options') || {};
+        if (options.width == null || options.width > poptions.width - 10) {
+            options.width = poptions.width - 10;
+        }
+        if (options.height == null || options.height > poptions.height - titleHeight - 5) {
+            options.height = poptions.height - titleHeight - 5; // need margin from bottom only
+        }
+        // negative value means margin
+        if (options.originalHeight < 0) options.height = pHeight + options.originalHeight - titleHeight;
+        if (options.originalWidth < 0) options.width = pWidth + options.originalWidth * 2; // x 2 because there is left and right margin
+        var head = $(where.box).find(where.title);
+
+        // if some messages are closing, insta close them
+        var $tmp = $(where.box).find('.w2ui-message.w2ui-closing');
+        if ($(where.box).find('.w2ui-message.w2ui-closing').length > 0) {
+            clearTimeout(closeTimer);
+            closeCB($tmp, $tmp.data('options') || {});
+        }
+        var msgCount = $(where.box).find('.w2ui-message').length;
+        // remove message
+        if ($.trim(options.html) === '' && $.trim(options.body) === '' && $.trim(options.buttons) === '') {
+            if (msgCount === 0) return; // no messages at all
+            var $msg = $(where.box).find('#w2ui-message'+ (msgCount-1));
+            var options = $msg.data('options') || {};
+            // before event
+            edata = options.trigger({ phase: 'before', type: 'close', target: 'self' });
+            if (edata.isCancelled === true) return;
+            // default behavior
+            $msg.css(w2utils.cssPrefix({
+                'transition': '0.15s',
+                'transform': 'translateY(-' + options.height + 'px)'
+            })).addClass('w2ui-closing');
+            if (msgCount == 1) {
+                if (this.unlock) {
+                    if (where.param) this.unlock(where.param, 150); else this.unlock(150);
+                }
+            } else {
+                $(where.box).find('#w2ui-message'+ (msgCount-2)).css('z-index', 1500);
+            }
+            closeTimer = setTimeout(function () { closeCB($msg, options) }, 150);
+
+        } else {
+
+            if ($.trim(options.body) !== '' || $.trim(options.buttons) !== '') {
+                options.html = '<div class="w2ui-message-body">'+ (options.body || '') +'</div>'+
+                    '<div class="w2ui-message-buttons">'+ (options.buttons || '') +'</div>';
+            }
+            // hide previous messages
+            $(where.box).find('.w2ui-message').css('z-index', 1390);
+            head.data('old-z-index', head.css('z-index'));
+            head.css('z-index', 1501);
+            // add message
+            $(where.box).find(where.body)
+                .before('<div id="w2ui-message' + msgCount + '" onmousedown="event.stopPropagation();" '+
+                        '   class="w2ui-message" style="display: none; z-index: 1500; ' +
+                            (head.length === 0 ? 'top: 0px;' : 'top: ' + w2utils.getSize(head, 'height') + 'px;') +
+                            (options.width  != null ? 'width: ' + options.width + 'px; left: ' + ((pWidth - options.width) / 2) + 'px;' : 'left: 10px; right: 10px;') +
+                            (options.height != null ? 'height: ' + options.height + 'px;' : 'bottom: 6px;') +
+                            w2utils.cssPrefix('transition', '.3s', true) + '"' +
+                            (options.hideOnClick === true
+                                ? where.param
+                                    ? 'onclick="'+ where.path +'.message(\''+ where.param +'\');"'
+                                    : 'onclick="'+ where.path +'.message();"'
+                                : '') + '>' +
+                        '</div>');
+            $(where.box).find('#w2ui-message'+ msgCount)
+                .data('options', options)
+                .data('prev_focus', $(':focus'));
+            var display = $(where.box).find('#w2ui-message'+ msgCount).css('display');
+            $(where.box).find('#w2ui-message'+ msgCount).css(w2utils.cssPrefix({
+                'transform': (display == 'none' ? 'translateY(-' + options.height + 'px)' : 'translateY(0px)')
+            }));
+            if (display == 'none') {
+                $(where.box).find('#w2ui-message'+ msgCount).show().html(options.html);
+                options.box = $(where.box).find('#w2ui-message'+ msgCount);
+                // before event
+                edata = options.trigger({ phase: 'before', type: 'open', target: 'self' });
+                if (edata.isCancelled === true) {
+                    head.css('z-index', head.data('old-z-index'));
+                    $(where.box).find('#w2ui-message'+ msgCount).remove();
+                    return;
+                }
+                // timer needs to animation
+                setTimeout(function () {
+                    $(where.box).find('#w2ui-message'+ msgCount).css(w2utils.cssPrefix({
+                        'transform': (display == 'none' ? 'translateY(0px)' : 'translateY(-' + options.height + 'px)')
+                    }));
+                }, 1);
+                // timer for lock
+                if (msgCount === 0 && this.lock) {
+                    if (where.param) this.lock(where.param); else this.lock();
+                }
+                setTimeout(function() {
+                    // has to be on top of lock
+                    $(where.box).find('#w2ui-message'+ msgCount).css(w2utils.cssPrefix({ 'transition': '0s' }));
+                    // event after
+                    options.trigger($.extend(edata, { phase: 'after' }));
+                }, 350);
+            }
+        }
+
+        function closeCB($msg, options) {
+            if (edata == null) {
+                // before event
+                edata = options.trigger({ phase: 'before', type: 'open', target: 'self' });
+                if (edata.isCancelled === true) {
+                    head.css('z-index', head.data('old-z-index'));
+                    $(where.box).find('#w2ui-message'+ msgCount).remove();
+                    return;
+                }
+            }
+            var $focus = $msg.data('prev_focus');
+            $msg.remove();
+            if ($focus && $focus.length > 0) {
+                $focus.focus();
+            } else {
+                if (obj && obj.focus) obj.focus();
+            }
+            head.css('z-index', head.data('old-z-index'));
+            // event after
+            options.trigger($.extend(edata, { phase: 'after' }));
+        }
+    }
+
+    function getSize (el, type) {
+        var $el = $(el);
+        var bwidth = {
+            left    : parseInt($el.css('border-left-width')) || 0,
+            right   : parseInt($el.css('border-right-width')) || 0,
+            top     : parseInt($el.css('border-top-width')) || 0,
+            bottom  : parseInt($el.css('border-bottom-width')) || 0
+        };
+        var mwidth = {
+            left    : parseInt($el.css('margin-left')) || 0,
+            right   : parseInt($el.css('margin-right')) || 0,
+            top     : parseInt($el.css('margin-top')) || 0,
+            bottom  : parseInt($el.css('margin-bottom')) || 0
+        };
+        var pwidth = {
+            left    : parseInt($el.css('padding-left')) || 0,
+            right   : parseInt($el.css('padding-right')) || 0,
+            top     : parseInt($el.css('padding-top')) || 0,
+            bottom  : parseInt($el.css('padding-bottom')) || 0
+        };
+        switch (type) {
+            case 'top'      : return bwidth.top + mwidth.top + pwidth.top;
+            case 'bottom'   : return bwidth.bottom + mwidth.bottom + pwidth.bottom;
+            case 'left'     : return bwidth.left + mwidth.left + pwidth.left;
+            case 'right'    : return bwidth.right + mwidth.right + pwidth.right;
+            case 'width'    : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right + parseInt($el.width());
+            case 'height'   : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom + parseInt($el.height());
+            case '+width'   : return bwidth.left + bwidth.right + mwidth.left + mwidth.right + pwidth.left + pwidth.right;
+            case '+height'  : return bwidth.top + bwidth.bottom + mwidth.top + mwidth.bottom + pwidth.top + pwidth.bottom;
+        }
+        return 0;
+    }
+
+    function getStrWidth (str, styles) {
+        var w, html = '<div id="_tmp_width" style="position: absolute; top: -900px;'+ (styles || '') +'">'+
+                        encodeTags(str) +
+                      '</div>';
+        $('body').append(html);
+        w = $('#_tmp_width').width();
+        $('#_tmp_width').remove();
+        return w;
+    }
+
+    function lang (phrase) {
+        var translation = this.settings.phrases[phrase];
+        if (translation == null) return phrase; else return translation;
+    }
+
+    function locale (locale) {
+        if (!locale) locale = 'en-us';
+
+        // if the locale is an object, not a string, than we assume it's a
+        if(typeof locale !== "string" ) {
+            w2utils.settings = $.extend(true, w2utils.settings, locale);
+            return;
+        }
+
+        if (locale.length === 5) locale = 'locale/'+ locale +'.json';
+
+        // clear phrases from language before
+        w2utils.settings.phrases = {};
+
+        // load from the file
+        $.ajax({
+            url      : locale,
+            type     : "GET",
+            dataType : "JSON",
+            async    : false,
+            success  : function (data, status, xhr) {
+                w2utils.settings = $.extend(true, w2utils.settings, data);
+            },
+            error    : function (xhr, status, msg) {

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list