loleaflet/js/w2ui-1.5.rc1.js |16475 +------------------------------------------
 1 file changed, 598 insertions(+), 15877 deletions(-)

New commits:
commit bb6f13d94dc6063df926e918868d144f36729c14
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Fri Feb 14 12:28:36 2020 -0400
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Mon Feb 17 14:57:19 2020 +0100

    w2ui: remove unused components
    
    w2ui is bundled with: w2grid, w2layout, w2popup,
    w2tabs, w2sidebar, w2fields, w2form
    
    But they are not used in desktop nor mobile and
    it is preferable to remove the dead code to reduce
    bundle size, parse with mobile devices.
    
    If in a near future, these components are used,
    still we can restore the components.
    
    Change-Id: I522f01a73217635acc9828a54006111fa196636d
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/88735
    Tested-by: Michael Meeks <michael.me...@collabora.com>
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/loleaflet/js/w2ui-1.5.rc1.js b/loleaflet/js/w2ui-1.5.rc1.js
index 687a00202..82243a0f0 100644
--- a/loleaflet/js/w2ui-1.5.rc1.js
+++ b/loleaflet/js/w2ui-1.5.rc1.js
@@ -2903,302 +2903,65 @@ w2utils.event = {
 /************************************************************************
 *   Library: Web 2.0 UI for jQuery (using prototypical inheritance)
 *   - Following objects defined
-*        - w2grid        - grid widget
-*        - $().w2grid    - jQuery wrapper
-*   - Dependencies: jQuery, w2utils, w2toolbar, w2fields
+*        - w2toolbar        - toolbar widget
+*        - $().w2toolbar    - jQuery wrapper
+*   - Dependencies: jQuery, w2utils, w2field
 *
 * == NICE TO HAVE ==
-*   - column autosize based on largest content
-*   - reorder columns/records
-*   - problem with .set() and arrays, array get extended too, but should be 
replaced
-*   - after edit stay on the same record option
-*   - if supplied array of ids, get should return array of records
-*   - allow functions in routeData (also add routeData to list/enum)
-*   - implement global routeData and all elements read from there
-*   - send parsed URL to the event if there is routeData
-*   - if you set searchData or sortData and call refresh() it should work
-*   - add selectType: 'none' so that no selection can be make but with mouse
-*   - reorder records with frozen columns
-*   - focus/blur for selectType = cell not display grayed out selection
-*   - frozen columns
-        - load more only on the right side
-        - scrolling on frozen columns is not working only on regular columns
-*   - copy or large number of records is slow
-*   - reusable search component (see 
https://github.com/vitmalina/w2ui/issues/914#issuecomment-107340524)
-*   - allow enum in inline edit (see 
https://github.com/vitmalina/w2ui/issues/911#issuecomment-107341193)
-*   - if record has no recid, then it should be index in the aray (should not 
be 0)
-*
-* == KNOWN ISSUES ==
-*   - bug: vs_start = 100 and more then 500 records, when scrolling empty sets
-*   - row drag and drop has bugs
-*   - Shift-click/Ctrl-click/Ctrl-Shift-Click selection is not as robust as it 
should be
-*
-* == 1.5 changes
-*   - $('#grid').w2grid() - if called w/o argument then it returns grid object
-*   - added statusRange     : true,
-*           statusBuffered  : false,
-*           statusRecordID  : true,
-*           statusSelection : true,
-*           statusResponse  : true,
-*           statusSort      : true,
-*           statusSearch    : true,
-*   - change selectAll() and selectNone() - return time it took
-*   - added vs_start and vs_extra
-*   - added update(cells) - updates only data in the grid (or cells)
-*   - add to docs onColumnDragStart, onColumnDragEnd
-*   - onSelect and onSelect should fire 1 time for selects with shift or 
selectAll(), selectNone()
-*   - record.w2ui.style[field_name]
-*   - use column field for style: { 1: 'color: red' }
-*   - added focus(), blur(), onFocus, onBlur
-*   - search.simple - if false, will not show up in simple search
-*   - search.operator - default operator to use with search field
-*   - search.operators - array of operators for the serach
-*   - search.hidden - could not be clearned by the user
-*   - search.value - only for hidden searches
-*   - if .search(val) - search all fields
-*   - refactor reorderRow (not finished)
-*   - return JSON can now have summary array
-*   - frozen columns
-*   - added selectionSave, selectionRestore - for internal use
-*   - added additional search filter options for int, float, date, time
-*   - added getLineHTML
-*   - added lineNumberWidth
-*   - add searches.style
-*   - getColumn without params returns fields of all columns
-*   - getSearch without params returns fields of all searches
-*   - added column.tooltip
-*   - added hasFocus, refactored w2utils.keyboard
-*   - do not clear selection when clicked and it was not in focus
-*   - added record.w2ui.colspan
-*   - editable area extends with typing
-*   - removed onSubmit and onDeleted - now it uses onSave and onDelete
-*   - column.seachable - can be an object, which will create search
-*   - added null, not null filters
-*   - update(cells) - added argument cells
-*   - scrollIntoView(..., ..., instant) - added third argument
-*   - added onResizeDblClick
-*   - added onColumnDblClick
-*   - implemented showBubble
-*   - added show.searchAll
-*   - added w2grid.operators
-*   - added w2grid.operatorsMap
-*   - move events into prototype
-*   - move rec.summary, rec.style, rec.editable -> into rec.w2ui.summary, 
rec.w2ui.style, rec.w2ui.editable
-*   - record: {
-        recid
-        field1
-        ...
-        fieldN
-        w2ui: {
-            colspan: { field: 5, ...}
-            editable: true/false
-            changes: {
-                field: chagned_value,
-                ....
-            },
-            children: [
-                // similar to records array
-                // can have sub children
-            ]
-            parent_recid: (internally set, id of the parent record, when 
children are copied to records array)
-            summary: true/false
-            style: 'string' - for entire row OR { field: 'string', ...} - per 
field
-            class: 'string' - for entire row OR { field: 'string', ...} - per 
field
-        }
-    }
-*   - added this.show.toolbarInput
-*   - disableCVS
-*   - grid.message
-*   - added noReset option to localSort()
-*   - onColumnSelect
-*   - need to update PHP example
-*   - added scrollToColumn(field)
-*   - textSearch: 'begins' (default), 'contains', 'is', ...
-*   - added refreshBody
-*   - added response.total = -1 (or not present) to indicate that number of 
records is unknown
-*   - message(.., callBack) - added callBack
-*   - grid.msgEmpty
-*   - field.render(..., data) -- added last argument which is what grid thinks 
should be there
-*   - onSearchOpen (onSearch will have mutli and reset flags)
-*   - added httpHeaders
-*   - col.editable can be a function which will be called with the same args 
as col.render()
-*   - getCellEditable(index, col_ind) -- return an 'editable' descriptor if 
cell is really editable
-*   - added stateId
-*   - rec.w2ui.class (and rec.w2ui.class { fname: '...' })
-*   - columnTooltip
-*   - expendable grids are still working
-*   - added search.type = 'color'
+*   - vertical toolbar
 *
 ************************************************************************/
 
 (function ($) {
-    var w2grid = function(options) {
-
-        // public properties
-        this.name         = null;
-        this.box          = null;     // HTML element that hold this element
-        this.header       = '';
-        this.url          = '';
-        this.routeData    = {};       // data for dynamic routes
-        this.columns      = [];       // { field, caption, size, attr, render, 
hidden, gridMinWidth, editable }
-        this.columnGroups = [];       // { span: int, caption: 'string', 
master: true/false }
-        this.records      = [];       // { recid: int(requied), field1: 
'value1', ... fieldN: 'valueN', style: 'string',  changes: object }
-        this.summary      = [];       // arry of summary records, same 
structure as records array
-        this.searches     = [];       // { type, caption, field, inTag, 
outTag, hidden }
-        this.searchData   = [];
-        this.sortData     = [];
-        this.postData     = {};
-        this.httpHeaders  = {};
-        this.toolbar      = {};       // if not empty object; then it is 
toolbar object
-        this.stateId      = null;     // Custom state name for satateSave, 
stateRestore and stateReset
-
-        this.show = {
-            header          : false,
-            toolbar         : false,
-            footer          : false,
-            columnHeaders   : true,
-            lineNumbers     : false,
-            expandColumn    : false,
-            selectColumn    : false,
-            emptyRecords    : true,
-            toolbarReload   : true,
-            toolbarColumns  : true,
-            toolbarSearch   : true,
-            toolbarInput    : true,
-            toolbarAdd      : false,
-            toolbarEdit     : false,
-            toolbarDelete   : false,
-            toolbarSave     : false,
-            searchAll       : true,
-            statusRange     : true,
-            statusBuffered  : false,
-            statusRecordID  : true,
-            statusSelection : true,
-            statusResponse  : true,
-            statusSort      : false,
-            statusSearch    : false,
-            recordTitles    : true,
-            selectionBorder : true,
-            skipRecords     : true,
-            saveRestoreState: true
-        };
-
-        this.hasFocus        = false;
-        this.autoLoad        = true;     // for infinite scroll
-        this.fixedBody       = true;     // if false; then grid grows with data
-        this.recordHeight    = 24;       // should be in prototype
-        this.lineNumberWidth = null;
-        this.vs_start        = 150;
-        this.vs_extra        = 15;
-        this.keyboard        = true;
-        this.selectType      = 'row';    // can be row|cell
-        this.multiSearch     = true;
-        this.multiSelect     = true;
-        this.multiSort       = true;
-        this.reorderColumns  = false;
-        this.reorderRows     = false;
-        this.markSearch      = true;
-        this.columnTooltip   = 'normal'; // can be normal, top, bottom, left, 
right
-        this.disableCVS      = false;    // disable Column Virtual Scroll
-        this.textSearch      = 'begins'; // default search type for text
-
-        this.total   = 0;     // server total
-        this.limit   = 100;
-        this.offset  = 0;     // how many records to skip (for infinite 
scroll) when pulling from server
-        this.style   = '';
-        this.ranges  = [];
-        this.menu    = [];
-        this.method  = null;  // if defined, then overwrited ajax method
-        this.recid   = null;
-        this.parser  = null;
-
-        // internal
-        this.last = {
-            field     : '',
-            caption   : '',
-            logic     : 'OR',
-            search    : '',
-            searchIds : [],
-            selection : {
-                indexes : [],
-                columns : {}
-            },
-            multi       : false,
-            scrollTop   : 0,
-            scrollLeft  : 0,
-            colStart    : 0,    // for column virtual scrolling
-            colEnd      : 0,
-            sortData    : null,
-            sortCount   : 0,
-            xhr         : null,
-            range_start : null,
-            range_end   : null,
-            sel_ind     : null,
-            sel_col     : null,
-            sel_type    : null,
-            edit_col    : null,
-            isSafari    : 
(/^((?!chrome|android).)*safari/i).test(navigator.userAgent)
-        };
+    var w2toolbar = function (options) {
+        this.box       = null;      // DOM Element that holds the element
+        this.name      = null;      // unique name for w2ui
+        this.routeData = {};        // data for dynamic routes
+        this.items     = [];
+        this.right     = '';        // HTML text on the right of toolbar
+        this.tooltip   = 'top|left';// can be top, bottom, left, right
 
-        $.extend(true, this, w2obj.grid, options);
+        $.extend(true, this, w2obj.toolbar, options);
     };
 
     // ====================================================
     // -- Registers as a jQuery plugin
 
-    $.fn.w2grid = function(method) {
+    $.fn.w2toolbar = function(method) {
         if ($.isPlainObject(method)) {
             // check name parameter
-            if (!w2utils.checkName(method, 'w2grid')) return;
-            // remember items
-            var columns      = method.columns;
-            var columnGroups = method.columnGroups;
-            var records      = method.records;
-            var searches     = method.searches;
-            var searchData   = method.searchData;
-            var sortData     = method.sortData;
-            var postData     = method.postData;
-            var httpHeaders  = method.httpHeaders;
-            var toolbar      = method.toolbar;
+            if (!w2utils.checkName(method, 'w2toolbar')) return;
             // extend items
-            var object = new w2grid(method);
-            $.extend(object, { postData: {}, httpHeaders: {}, records: [], 
columns: [], searches: [], toolbar: {}, sortData: [], searchData: [], handlers: 
[] });
-            if (object.onExpand != null) object.show.expandColumn = true;
-            $.extend(true, object.toolbar, toolbar);
-            // reassign variables
-            var p;
-            if (columns)      for (p = 0; p < columns.length; p++)      
object.columns[p]       = $.extend(true, {}, columns[p]);
-            if (columnGroups) for (p = 0; p < columnGroups.length; p++) 
object.columnGroups[p]  = $.extend(true, {}, columnGroups[p]);
-            if (searches)     for (p = 0; p < searches.length; p++)     
object.searches[p]      = $.extend(true, {}, searches[p]);
-            if (searchData)   for (p = 0; p < searchData.length; p++)   
object.searchData[p]    = $.extend(true, {}, searchData[p]);
-            if (sortData)     for (p = 0; p < sortData.length; p++)     
object.sortData[p]      = $.extend(true, {}, sortData[p]);
-            object.postData = $.extend(true, {}, postData);
-            object.httpHeaders = $.extend(true, {}, httpHeaders);
-
-            // check if there are records without recid
-            if (records) for (var r = 0; r < records.length; r++) {
-                if (records[r].recid == null && records[r][object.recid] == 
null) {
-                    console.log('ERROR: Cannot add records without recid. 
(obj: '+ object.name +')');
-                    return;
+            var items = method.items || [];
+            var object = new w2toolbar(method);
+            $.extend(object, { items: [], handlers: [] });
+            for (var i = 0; i < items.length; i++) {
+                object.items[i] = $.extend({}, w2toolbar.prototype.item, 
items[i]);
+                // menus
+                if (object.items[i].type == 'menu-check') {
+                    var item = object.items[i];
+                    if (!Array.isArray(item.selected)) item.selected = [];
+                    if (Array.isArray(item.items)) {
+                        for (var j = 0; j < item.items.length; j++) {
+                            var it = item.items[j];
+                            if (it.checked && item.selected.indexOf(it.id) == 
-1) item.selected.push(it.id);
+                            if (!it.checked && item.selected.indexOf(it.id) != 
-1) it.checked = true;
+                            if (it.checked == null) it.checked = false;
+                        }
+                    }
                 }
-                object.records[r] = $.extend(true, {}, records[r]);
-            }
-            // add searches
-            for (var i = 0; i < object.columns.length; i++) {
-                var col = object.columns[i];
-                var search = col.searchable;
-                if (search == null || search === false || 
object.getSearch(col.field) != null) continue;
-                if ($.isPlainObject(search)) {
-                    object.addSearch($.extend({ field: col.field, caption: 
col.caption, type: 'text' }, search));
-                } else {
-                    var stype = col.searchable, attr  = '';
-                    if (col.searchable === true) { stype = 'text'; attr = 
'size="20"'; }
-                    object.addSearch({ field: col.field, caption: col.caption, 
type: stype, attr: attr });
+                else if (object.items[i].type == 'menu-radio') {
+                    var item = object.items[i];
+                    if (Array.isArray(item.items)) {
+                        for (var j = 0; j < item.items.length; j++) {
+                            var it = item.items[j];
+                            if (it.checked && item.selected == null) 
item.selected = it.id; else it.checked = false;
+                            if (!it.checked && item.selected == it.id) 
it.checked = true;
+                            if (it.checked == null) it.checked = false;
+                        }
+                    }
                 }
             }
-            // init toolbar
-            object.initToolbar();
-            // render if necessary
             if ($(this).length !== 0) {
                 object.render($(this)[0]);
             }
@@ -3221,15747 +2984,705 @@ w2utils.event = {
     // ====================================================
     // -- Implementation of core functionality
 
-    w2grid.prototype = {
-        msgDelete       : 'Are you sure you want to delete selected records?',
-        msgNotJSON      : 'Returned data is not in valid JSON format.',
-        msgAJAXerror    : 'AJAX error. See console for more details.',
-        msgRefresh      : 'Refreshing...',
-        msgNeedReload   : 'Your remote data source record count has changed, 
reloading from the first record.',
-        msgEmpty        : '', // if not blank, then it is message when server 
returns no records
-
-        buttons: {
-            'reload'   : { type: 'button', id: 'w2ui-reload', icon: 
'w2ui-icon-reload', tooltip: 'Reload data in the list' },
-            'columns'  : { type: 'drop', id: 'w2ui-column-on-off', icon: 
'w2ui-icon-columns', tooltip: 'Show/hide columns', arrow: false, html: '' },
-            'search'   : { type: 'html',   id: 'w2ui-search',
-                            html: '<div class="w2ui-icon icon-search-down 
w2ui-search-down" '+
-                                  'onclick="var obj = 
w2ui[jQuery(this).parents(\'div.w2ui-grid\').attr(\'name\')]; 
obj.searchShowFields();"></div>'
-                          },
-            'search-go': { type: 'drop',  id: 'w2ui-search-advanced', icon: 
'w2ui-icon-search', text: 'Search', tooltip: 'Open Search Fields' },
-            'add'      : { type: 'button', id: 'w2ui-add', text: 'Add New', 
tooltip: 'Add new record', icon: 'w2ui-icon-plus' },
-            'edit'     : { type: 'button', id: 'w2ui-edit', text: 'Edit', 
tooltip: 'Edit selected record', icon: 'w2ui-icon-pencil', disabled: true },
-            'delete'   : { type: 'button', id: 'w2ui-delete', text: 'Delete', 
tooltip: 'Delete selected records', icon: 'w2ui-icon-cross', disabled: true },
-            'save'     : { type: 'button', id: 'w2ui-save', text: 'Save', 
tooltip: 'Save changed records', icon: 'w2ui-icon-check' }
-        },
+    w2toolbar.prototype = {
+        onClick   : null,
+        onRender  : null,
+        onRefresh : null,
+        onResize  : null,
+        onDestroy : null,
 
-        operators: { // for search fields
-            "text"    : ['is', 'begins', 'contains', 'ends'],
-            "number"  : ['is', 'between', { oper: 'less', text: 'less than'}, 
{ oper: 'more', text: 'more than' }],
-            "date"    : ['is', 'between', { oper: 'less', text: 'before'}, { 
oper: 'more', text: 'after' }],
-            "list"    : ['is'],
-            "hex"     : ['is', 'between'],
-            "color"   : ['is', 'begins', 'contains', 'ends'],
-            "enum"    : ['in', 'not in']
-            // -- all posible
-            // "text"    : ['is', 'begins', 'contains', 'ends'],
-            // "number"  : ['is', 'between', 'less:less than', 'more:more 
than', 'null:is null', 'not null:is not null'],
-            // "list"    : ['is', 'null:is null', 'not null:is not null'],
-            // "enum"    : ['in', 'not in', 'null:is null', 'not null:is not 
null']
+        item: {
+            id          : null,        // command to be sent to all event 
handlers
+            type        : 'button',    // button, check, radio, drop, menu, 
menu-radio, menu-check, break, html, spacer
+            text        : null,
+            html        : '',
+            tooltip     : null,        // w2toolbar.tooltip should be
+            count       : null,
+            hidden      : false,
+            disabled    : false,
+            checked     : false,       // used for radio buttons
+            img         : null,
+            icon        : null,
+            route       : null,        // if not null, it is route to go
+            arrow       : true,        // arrow down for drop/menu types
+            style       : null,        // extre css style for caption
+            color       : null,        // color value - used in color pickers
+            transparent : null,        // transparent t/f - used in color 
pickers
+            group       : null,        // used for radio buttons
+            items       : null,        // for type menu* it is an array of 
items in the menu
+            selected    : null,        // used for menu-check, menu-radio
+            overlay     : {},
+            onClick     : null,
+            onRefresh   : null
         },
 
-        operatorsMap: {
-            "text"         : "text",
-            "int"          : "number",
-            "float"        : "number",
-            "money"        : "number",
-            "currency"     : "number",
-            "percent"      : "number",
-            "hex"          : "hex",
-            "alphanumeric" : "text",
-            "color"        : "color",
-            "date"         : "date",
-            "time"         : "date",
-            "datetime"     : "date",
-            "list"         : "list",
-            "combo"        : "text",
-            "enum"         : "enum",
-            "file"         : "enum",
-            "select"       : "list",
-            "radio"        : "list",
-            "checkbox"     : "list",
-            "toggle"       : "list"
+        add: function (items) {
+            this.insert(null, items);
         },
 
-        // events
-        onAdd              : null,
-        onEdit             : null,
-        onRequest          : null,        // called on any server event
-        onLoad             : null,
-        onDelete           : null,
-        onSave             : null,
-        onSelect           : null,
-        onUnselect         : null,
-        onClick            : null,
-        onDblClick         : null,
-        onContextMenu      : null,
-        onMenuClick        : null,        // when context menu item selected
-        onColumnClick      : null,
-        onColumnDblClick   : null,
-        onColumnResize     : null,
-        onSort             : null,
-        onSearch           : null,
-        onSearchOpen       : null,
-        onChange           : null,        // called when editable record is 
changed
-        onRestore          : null,        // called when editable record is 
restored
-        onExpand           : null,
-        onCollapse         : null,
-        onError            : null,
-        onKeydown          : null,
-        onToolbar          : null,        // all events from toolbar
-        onColumnOnOff      : null,
-        onCopy             : null,
-        onPaste            : null,
-        onSelectionExtend  : null,
-        onEditField        : null,
-        onRender           : null,
-        onRefresh          : null,
-        onReload           : null,
-        onResize           : null,
-        onDestroy          : null,
-        onStateSave        : null,
-        onStateRestore     : null,
-        onFocus            : null,
-        onBlur             : null,
-        onReorderRow       : null,
-
-        add: function (record, first) {
-            if (!$.isArray(record)) record = [record];
-            var added = 0;
-            for (var i = 0; i < record.length; i++) {
-                var rec = record[i];
-                if (rec.recid == null && rec[this.recid] == null) {
-                    console.log('ERROR: Cannot add record without recid. (obj: 
'+ this.name +')');
-                    continue;
-                }
-                if (rec.w2ui && rec.w2ui.summary === true) {
-                    if (first) this.summary.unshift(rec); else 
this.summary.push(rec);
-                } else {
-                    if (first) this.records.unshift(rec); else 
this.records.push(rec);
+        insert: function (id, items) {
+            if (!$.isArray(items)) items = [items];
+            for (var o = 0; o < items.length; o++) {
+                // checks
+                if (items[o].type == null) {
+                    console.log('ERROR: The parameter "type" is required but 
not supplied in w2toolbar.add() method.');
+                    return;
                 }
-                added++;
-            }
-            var url = (typeof this.url != 'object' ? this.url : this.url.get);
-            if (!url) {
-                this.total = this.records.length;
-                this.localSort(false, true);
-                this.localSearch();
-                // do not call this.refresh(), this is unnecessary, heavy, and 
messes with the toolbar.
-                this.refreshBody();
-                this.resizeRecords();
-                return added;
-            }
-            this.refresh(); // ??  should it be reload?
-            return added;
-        },
-
-        find: function (obj, returnIndex) {
-            if (obj == null) obj = {};
-            var recs    = [];
-            var hasDots = false;
-            // check if property is nested - needed for speed
-            for (var o in obj) if (String(o).indexOf('.') != -1) hasDots = 
true;
-            // look for an item
-            for (var i = 0; i < this.records.length; i++) {
-                var match = true;
-                for (var o in obj) {
-                    var val = this.records[i][o];
-                    if (hasDots && String(o).indexOf('.') != -1) val = 
this.parseField(this.records[i], o);
-                    if (obj[o] == 'not-null') {
-                        if (val == null || val === '') match = false;
-                    } else {
-                        if (obj[o] != val) match = false;
-                    }
+                if ($.inArray(String(items[o].type), ['button', 'check', 
'radio', 'drop', 'menu', 'menu-radio', 'menu-check', 'color', 'text-color', 
'break', 'html', 'spacer']) == -1) {
+                    console.log('ERROR: The parameter "type" should be one of 
the following [button, check, radio, drop, menu, break, html, spacer] '+
+                            'in w2toolbar.add() method.');
+                    return;
                 }
-                if (match && returnIndex !== true) 
recs.push(this.records[i].recid);
-                if (match && returnIndex === true) recs.push(i);
-            }
-            return recs;
-        },
-
-        set: function (recid, record, noRefresh) { // does not delete 
existing, but overrides on top of it
-            if (typeof recid == 'object') {
-                noRefresh = record;
-                record    = recid;
-                recid     = null;
-            }
-            // update all records
-            if (recid == null) {
-                for (var i = 0; i < this.records.length; i++) {
-                    $.extend(true, this.records[i], record); // recid is the 
whole record
+                if (items[o].id == null && items[o].type != 'break' && 
items[o].type != 'spacer') {
+                    console.log('ERROR: The parameter "id" is required but not 
supplied in w2toolbar.add() method.');
+                    return;
                 }
-                if (noRefresh !== true) this.refresh();
-            } else { // find record to update
-                var ind = this.get(recid, true);
-                if (ind == null) return false;
-                var isSummary = (this.records[ind] && this.records[ind].recid 
== recid ? false : true);
-                if (isSummary) {
-                    $.extend(true, this.summary[ind], record);
+                if (!w2utils.checkUniqueId(items[o].id, this.items, 'toolbar 
items', this.name)) return;
+                // add item
+                var it = $.extend({}, w2toolbar.prototype.item, items[o]);
+                if (id == null) {
+                    this.items.push(it);
                 } else {
-                    $.extend(true, this.records[ind], record);
-                }
-                if (noRefresh !== true) this.refreshRow(recid, ind); // 
refresh only that record
-            }
-            return true;
-        },
-
-        get: function (recid, returnIndex) {
-            // search records
-            if ($.isArray(recid)) {
-                var recs = [];
-                for (var i = 0; i < this.records.length; i++) {
-                    if ($.inArray(this.records[i].recid, recid) != -1) {
-                        if (returnIndex === true) {
-                            recs.push(i);
-                        } else {
-                            recs.push(this.records[i]);
-                        }
-                    }
-                }
-                for (var i = 0; i < this.summary.length; i++) {
-                    if ($.inArray(this.summary[i].recid, recid) != -1) {
-                        if (returnIndex === true) {
-                            recs.push(i);
-                        } else {
-                            recs.push(this.summary[i]);
-                        }
-                    }
-                }
-                return recs;
-            } else {
-                for (var i = 0; i < this.records.length; i++) {
-                    if (this.records[i].recid == recid) {
-                        if (returnIndex === true) return i; else return 
this.records[i];
-                    }
-                }
-                // search summary
-                for (var i = 0; i < this.summary.length; i++) {
-                    if (this.summary[i].recid == recid) {
-                        if (returnIndex === true) return i; else return 
this.summary[i];
-                    }
+                    var middle = this.get(id, true);
+                    this.items = this.items.slice(0, middle).concat([it], 
this.items.slice(middle));
                 }
-                return null;
+                this.refresh(it.id);
+                this.resize();
             }
         },
 
         remove: function () {
             var removed = 0;
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.records.length-1; r >= 0; r--) {
-                    if (this.records[r].recid == arguments[a]) { 
this.records.splice(r, 1); removed++; }
-                }
-                for (var r = this.summary.length-1; r >= 0; r--) {
-                    if (this.summary[r].recid == arguments[a]) { 
this.summary.splice(r, 1); removed++; }
-                }
-            }
-            var url = (typeof this.url != 'object' ? this.url : this.url.get);
-            if (!url) {
-                this.localSort(false, true);
-                this.localSearch();
+                var it = this.get(arguments[a]);
+                if (!it || String(arguments[a]).indexOf(':') != -1) continue;
+                removed++;
+                // remove from screen
+                $(this.box).find('#tb_'+ this.name +'_item_'+ 
w2utils.escapeId(it.id)).remove();
+                // remove from array
+                var ind = this.get(it.id, true);
+                if (ind != null) this.items.splice(ind, 1);
             }
-            this.refresh();
+            this.resize();
             return removed;
         },
 
-        addColumn: function (before, columns) {
-            var added = 0;
-            if (arguments.length == 1) {
-                columns = before;
-                before  = this.columns.length;
-            } else {
-                if (typeof before == 'string') before = this.getColumn(before, 
true);
-                if (before == null) before = this.columns.length;
-            }
-            if (!$.isArray(columns)) columns = [columns];
-            for (var i = 0; i < columns.length; i++) {
-                this.columns.splice(before, 0, columns[i]);
-                // if column is searchable, add search field
-                if (columns[i].searchable) {
-                    var stype = columns[i].searchable;
-                    var attr  = '';
-                    if (columns[i].searchable === true) { stype = 'text'; attr 
= 'size="20"'; }
-                    this.addSearch({ field: columns[i].field, caption: 
columns[i].caption, type: stype, attr: attr });
-                }
-                before++;
-                added++;
-            }
-            this.refresh();
-            return added;
-        },
-
-        removeColumn: function () {
-            var removed = 0;
-            for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.columns.length-1; r >= 0; r--) {
-                    if (this.columns[r].field == arguments[a]) {
-                        if (this.columns[r].searchable) 
this.removeSearch(arguments[a]);
-                        this.columns.splice(r, 1);
-                        removed++;
-                    }
-                }
-            }
-            this.refresh();
-            return removed;
+        set: function (id, newOptions) {
+            var item = this.get(id);
+            if (item == null) return false;
+            $.extend(item, newOptions);
+            this.refresh(String(id).split(':')[0]);
+            return true;
         },
 
-        getColumn: function (field, returnIndex) {
-            // no arguments - return fields of all columns
+        get: function (id, returnIndex) {
             if (arguments.length === 0) {
-                var ret = [];
-                for (var i = 0; i < this.columns.length; i++) 
ret.push(this.columns[i].field);
-                return ret;
+                var all = [];
+                for (var i1 = 0; i1 < this.items.length; i1++) if 
(this.items[i1].id != null) all.push(this.items[i1].id);
+                return all;
             }
-            // find column
-            for (var i = 0; i < this.columns.length; i++) {
-                if (this.columns[i].field == field) {
-                    if (returnIndex === true) return i; else return 
this.columns[i];
+            var tmp = String(id).split(':');
+            for (var i2 = 0; i2 < this.items.length; i2++) {
+                var it = this.items[i2];
+                // find a menu item
+                if (['menu', 'menu-radio', 'menu-check'].indexOf(it.type) != 
-1 && tmp.length == 2 && it.id == tmp[0]) {
+                    for (var i = 0; i < it.items.length; i++) {
+                        var item = it.items[i];
+                        if (item.id == tmp[1] || (item.id == null && item.text 
== tmp[1])) {
+                            if (returnIndex == true) return i; else return 
item;
+                        }
+                    }
+                } else if (it.id == tmp[0]) {
+                    if (returnIndex == true) return i2; else return it;
                 }
             }
             return null;
         },
 
-        toggleColumn: function () {
-            var effected = 0;
+        show: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.columns.length-1; r >= 0; r--) {
-                    var col = this.columns[r];
-                    if (col.field == arguments[a]) {
-                        col.hidden = !col.hidden;
-                        effected++;
-                    }
-                }
+                var it = this.get(arguments[a]);
+                if (!it) continue;
+                items++;
+                it.hidden = false;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.refreshBody();
-            this.resizeRecords();
-            return effected;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) 
obj.refresh(tmp[t]); obj.resize(); }, 15); // needs timeout
+            return items;
         },
 
-        showColumn: function () {
-            var shown = 0;
+        hide: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.columns.length-1; r >= 0; r--) {
-                    var col = this.columns[r];
-                    if (col.gridMinWidth) delete col.gridMinWidth;
-                    if (col.field == arguments[a] && col.hidden !== false) {
-                        col.hidden = false;
-                        shown++;
-                    }
-                }
+                var it = this.get(arguments[a]);
+                if (!it) continue;
+                items++;
+                it.hidden = true;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.refreshBody();
-            this.resizeRecords();
-            return shown;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) { 
obj.refresh(tmp[t]); obj.tooltipHide(tmp[t]); } obj.resize(); }, 15); // needs 
timeout
+            return items;
         },
 
-        hideColumn: function () {
-            var hidden = 0;
+        enable: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.columns.length-1; r >= 0; r--) {
-                    var col = this.columns[r];
-                    if (col.field == arguments[a] && col.hidden !== true) {
-                        col.hidden = true;
-                        hidden++;
-                    }
-                }
+                var it = this.get(arguments[a]);
+                if (!it) continue;
+                items++;
+                it.disabled = false;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.refreshBody();
-            this.resizeRecords();
-            return hidden;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) 
obj.refresh(tmp[t]); }, 15); // needs timeout
+            return items;
         },
 
-        addSearch: function (before, search) {
-            var added = 0;
-            if (arguments.length == 1) {
-                search = before;
-                before = this.searches.length;
-            } else {
-                if (typeof before == 'string') before = this.getSearch(before, 
true);
-                if (before == null) before = this.searches.length;
-            }
-            if (!$.isArray(search)) search = [search];
-            for (var i = 0; i < search.length; i++) {
-                this.searches.splice(before, 0, search[i]);
-                before++;
-                added++;
+        disable: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
+            for (var a = 0; a < arguments.length; a++) {
+                var it = this.get(arguments[a]);
+                if (!it) continue;
+                items++;
+                it.disabled = true;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.searchClose();
-            return added;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) { 
obj.refresh(tmp[t]); obj.tooltipHide(tmp[t]); } }, 15); // needs timeout
+            return items;
         },
 
-        removeSearch: function () {
-            var removed = 0;
+        check: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.searches.length-1; r >= 0; r--) {
-                    if (this.searches[r].field == arguments[a]) { 
this.searches.splice(r, 1); removed++; }
-                }
+                var it = this.get(arguments[a]);
+                if (!it || String(arguments[a]).indexOf(':') != -1) continue;
+                items++;
+                it.checked = true;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.searchClose();
-            return removed;
-        },
-
-        getSearch: function (field, returnIndex) {
-            // no arguments - return fields of all searches
-            if (arguments.length === 0) {
-                var ret = [];
-                for (var i = 0; i < this.searches.length; i++) 
ret.push(this.searches[i].field);
-                return ret;
-            }
-            // find search
-            for (var i = 0; i < this.searches.length; i++) {
-                if (this.searches[i].field == field) {
-                    if (returnIndex === true) return i; else return 
this.searches[i];
-                }
-            }
-            return null;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) 
obj.refresh(tmp[t]); }, 15); // needs timeout
+            return items;
         },
 
-        toggleSearch: function () {
-            var effected = 0;
+        uncheck: function () {
+            var obj   = this;
+            var items = 0;
+            var tmp   = [];
             for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.searches.length-1; r >= 0; r--) {
-                    if (this.searches[r].field == arguments[a]) {
-                        this.searches[r].hidden = !this.searches[r].hidden;
-                        effected++;
-                    }
+                var it = this.get(arguments[a]);
+                if (!it || String(arguments[a]).indexOf(':') != -1) continue;
+                // remove overlay
+                if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 
'text-color'].indexOf(it.type) != -1 && it.checked) {
+                    // hide overlay
+                    setTimeout(function () {
+                        var el = $('#tb_'+ obj.name +'_item_'+ 
w2utils.escapeId(it.id));
+                        el.w2overlay({ name: obj.name });
+                    }, 1);
                 }
+                items++;
+                it.checked = false;
+                tmp.push(String(arguments[a]).split(':')[0]);
             }
-            this.searchClose();
-            return effected;
+            setTimeout(function () { for (var t=0; t<tmp.length; t++) 
obj.refresh(tmp[t]); }, 15); // needs timeout
+            return items;
         },
 
-        showSearch: function () {
-            var shown = 0;
-            for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.searches.length-1; r >= 0; r--) {
-                    if (this.searches[r].field == arguments[a] && 
this.searches[r].hidden !== false) {
-                        this.searches[r].hidden = false;
-                        shown++;
-                    }
+        click: function (id, event) {
+            var obj = this;
+            // click on menu items
+            var tmp = String(id).split(':');
+            var it  = this.get(tmp[0]);
+            if (tmp.length > 1) {
+                var subItem = this.get(id);
+                if (subItem && !subItem.disabled) {
+                    obj.menuClick({ name: obj.name, item: it, subItem: 
subItem, originalEvent: event });
                 }
+                return;
             }
-            this.searchClose();
-            return shown;
-        },
-
-        hideSearch: function () {
-            var hidden = 0;
-            for (var a = 0; a < arguments.length; a++) {
-                for (var r = this.searches.length-1; r >= 0; r--) {
-                    if (this.searches[r].field == arguments[a] && 
this.searches[r].hidden !== true) {
-                        this.searches[r].hidden = true;
-                        hidden++;
-                    }
+            if (it && !it.disabled) {
+                // event before
+                var edata = this.trigger({ phase: 'before', type: 'click', 
target: (id != null ? id : this.name),
+                    item: it, object: it, originalEvent: event });
+                if (tmp[0] === 'zoomin' || tmp[0] === 'zoomout') {
+                    return;
                 }
-            }
-            this.searchClose();
-            return hidden;
-        },
+                if (edata.isCancelled === true) return;
 
-        getSearchData: function (field) {
-            for (var i = 0; i < this.searchData.length; i++) {
-                if (this.searchData[i].field == field) return 
this.searchData[i];
-            }
-            return null;
-        },
+                var btn = '#tb_'+ this.name +'_item_'+ w2utils.escapeId(it.id) 
+' table.w2ui-button';
+                $(btn).removeClass('down'); // need to requery at the moment 
-- as well as elsewhere in this function
 
-        localSort: function (silent, noResetRefresh) {
-            var url = (typeof this.url != 'object' ? this.url : this.url.get);
-            if (url) {
-                console.log('ERROR: grid.localSort can only be used on local 
data source, grid.url should be empty.');
-                return;
-            }
-            if ($.isEmptyObject(this.sortData)) return;
-            var time = (new Date()).getTime();
-            var obj  = this;
-            // process date fields
-            obj.selectionSave();
-            obj.prepareData();
-            if (!noResetRefresh) {
-                obj.reset();
-            }
-            // process sortData
-            for (var i = 0; i < this.sortData.length; i++) {
-                var column = this.getColumn(this.sortData[i].field);
-                if (!column) return;
-                if (typeof column.render == 'string') {
-                    if (['date', 'age'].indexOf(column.render.split(':')[0]) 
!= -1) {
-                        this.sortData[i]['field_'] = column.field + '_';
-                    }
-                    if (['time'].indexOf(column.render.split(':')[0]) != -1) {
-                        this.sortData[i]['field_'] = column.field + '_';
+                if (it.type == 'radio') {
+                    for (var i = 0; i < this.items.length; i++) {
+                        var itt = this.items[i];
+                        if (itt == null || itt.id == it.id || itt.type !== 
'radio') continue;
+                        if (itt.group == it.group && itt.checked) {
+                            itt.checked = false;
+                            this.refresh(itt.id);
+                        }
                     }
+                    it.checked = true;
+                    $(btn).addClass('checked');
                 }
-            }
 
-            // prepare paths and process sort
-            preparePaths();
-            this.records.sort(function (a, b) {
-                return compareRecordPaths(a, b);
-            });
-            cleanupPaths();
+                if (['menu', 'menu-radio', 'menu-check', 'drop', 'color', 
'text-color'].indexOf(it.type) != -1) {
+                    obj.tooltipHide(id);
+                    if (it.checked) {
+                        // if it was already checked, second click will hide it
+                        setTimeout(function () {
+                            // hide overlay
+                            var el = $('#tb_'+ obj.name +'_item_'+ 
w2utils.escapeId(it.id));
+                            el.w2overlay({ name: obj.name });
+                            // uncheck
+                            it.checked = false;
+                            obj.refresh(it.id);
+                        }, 1);
 
-            obj.selectionRestore(noResetRefresh);
-            time = (new Date()).getTime() - time;
-            if (silent !== true && obj.show.statusSort) {
-                setTimeout(function () {
-                    obj.status(w2utils.lang('Sorting took') + ' ' + time/1000 
+ ' ' + w2utils.lang('sec'));
-                }, 10);
-            }
-            return time;
+                    } else {
 
-            // grab paths before sorting for efficiency and because calling 
obj.get()
-            // while sorting 'obj.records' is unsafe, at least on webkit
-            function preparePaths() {
-                for (var i = 0; i < obj.records.length; i++) {
-                    var rec = obj.records[i];
-                    if (rec.w2ui && rec.w2ui.parent_recid != null)
-                        rec.w2ui._path = getRecordPath(rec);
+                        // show overlay
+                        setTimeout(function () {
+                            var el = $('#tb_'+ obj.name +'_item_'+ 
w2utils.escapeId(it.id));
+                            if (!$.isPlainObject(it.overlay)) it.overlay = {};
+                            var left = (el.width() - 50) / 2;
+                            if (left > 19) left = 19;
+                            if (it.type == 'drop') {
+                                el.w2overlay(it.html, $.extend({ name: 
obj.name, left: left, top: 3 }, it.overlay, {
+                                    onHide: function (event) {
+                                        hideDrop();
+                                    }
+                                }));
+                            }
+                            if (['menu', 'menu-radio', 
'menu-check'].indexOf(it.type) != -1) {
+                                var menuType = 'normal';
+                                if (it.type == 'menu-radio') {
+                                    menuType = 'radio';
+                                    it.items.forEach(function (item) {
+                                        if (it.selected == item.id) 
item.checked = true; else item.checked = false;
+                                    });
+                                }
+                                if (it.type == 'menu-check') {
+                                    menuType = 'check';
+                                    it.items.forEach(function (item) {
+                                        if ($.isArray(it.selected) && 
it.selected.indexOf(item.id) != -1) item.checked = true; else item.checked = 
false;
+                                    });
+                                }
+                                el.w2menu($.extend({ name: obj.name, items: 
it.items, left: left, top: 3 }, it.overlay, {
+                                    type: menuType,
+                                    select: function (event) {
+                                        obj.menuClick({ name: obj.name, item: 
it, subItem: event.item, originalEvent: event.originalEvent, keepOpen: 
event.keepOpen });
+                                    },
+                                    onHide: function (event) {
+                                        hideDrop();
+                                    }
+                                }));
+                            }
+                            if (['color', 'text-color'].indexOf(it.type) != 
-1) {
+                                if (it.transparent == null) it.transparent = 
true;
+                                $(el).w2color({ color: it.color, transparent: 
it.transparent }, function (color, index) {
+                                    if (color != null) {
+                                        obj.colorClick({ name: obj.name, item: 
it, color: color, originalEvent: event.originalEvent });
+                                    }
+                                    hideDrop();
+                                });
+                            }
+                            function hideDrop(event) {
+                                it.checked = false;
+                                $(btn).removeClass('checked');
+                            }
+                        }, 1);
+                    }
                 }
-            }
 
-            // cleanup and release memory allocated by preparePaths()
-            function cleanupPaths() {
-                for (var i = 0; i < obj.records.length; i++) {
-                    var rec = obj.records[i];
-                    if (rec.w2ui && rec.w2ui.parent_recid != null)
-                        rec.w2ui._path = null;
+                if (['check', 'menu', 'menu-radio', 'menu-check', 'drop', 
'color', 'text-color'].indexOf(it.type) != -1) {
+                    it.checked = !it.checked;
+                    if (it.checked) {
+                        $(btn).addClass('checked');
+                    } else {
+                        $(btn).removeClass('checked');
+                    }
                 }
-            }
-
-            // compare two paths, from root of tree to given records
-            function compareRecordPaths(a, b) {
-                if ((!a.w2ui || a.w2ui.parent_recid == null) && (!b.w2ui || 
b.w2ui.parent_recid == null)) {
-                    return compareRecords(a, b);    // no tree, fast path
+                // route processing
+                if (it.route) {
+                    var route = String('/'+ it.route).replace(/\/{2,}/g, '/');
+                    var info  = w2utils.parseRoute(route);
+                    if (info.keys.length > 0) {
+                        for (var k = 0; k < info.keys.length; k++) {
+                            route = route.replace((new RegExp(':'+ 
info.keys[k].name, 'g')), this.routeData[info.keys[k].name]);
+                        }
+                    }
+                    setTimeout(function () { window.location.hash = route; }, 
1);
                 }
-                var pa = getRecordPath(a);
-                var pb = getRecordPath(b);
-                for (var i = 0; i < Math.min(pa.length, pb.length); i++) {
-                    var diff = compareRecords(pa[i], pb[i]);
-                    if (diff !== 0) return diff;     // different subpath
+                if (event && ['button', 'check', 'radio'].indexOf(it.type) != 
-1) {
+                    // need to refresh toolbar as it might be dynamic
+                    this.tooltipShow(id, event, true);
                 }
-                if (pa.length > pb.length) return 1;
-                if (pa.length < pb.length) return -1;
-                console.log('ERROR: two paths should not be equal.');
-                return 0;
+                // event after
+                this.trigger($.extend(edata, { phase: 'after' }));
             }
+        },
 
-            // return an array of all records from root to and including 'rec'
-            function getRecordPath(rec) {
-                if (!rec.w2ui || rec.w2ui.parent_recid == null) return [rec];
-                if (rec.w2ui._path)
-                    return rec.w2ui._path;
-                // during actual sort, we should never reach this point
-                var subrec = obj.get(rec.w2ui.parent_recid);
-                if (!subrec) {
-                    console.log('ERROR: no parent record: 
'+rec.w2ui.parent_recid);
-                    return [rec];
-                }
-                return (getRecordPath(subrec).concat(rec));
-            }
+        scroll: function (direction) {
+            var box = $(this.box);
+            var obj = this;
+            var scrollBox  = box.find('.w2ui-scroll-wrapper');
+            var scrollLeft = scrollBox.scrollLeft();
+            var width1, width2, scroll;
 
-            // compare two records according to sortData and finally recid
-            function compareRecords(a, b) {
-                if (a === b) return 0; // optimize, same object
-                for (var i = 0; i < obj.sortData.length; i++) {
-                    var fld = obj.sortData[i].field;
-                    if (obj.sortData[i].field_) fld = obj.sortData[i].field_;
-                    var aa = a[fld];
-                    var bb = b[fld];
-                    if (String(fld).indexOf('.') != -1) {
-                        aa = obj.parseField(a, fld);
-                        bb = obj.parseField(b, fld);
-                    }
-                    var col = obj.getColumn(fld);
-                    if (col && col.editable != null) { // for drop editable 
fields and drop downs
-                        if ($.isPlainObject(aa) && aa.text) aa = aa.text;
-                        if ($.isPlainObject(bb) && bb.text) bb = bb.text;
-                    }
-                    var ret = compareCells(aa, bb, i, 
obj.sortData[i].direction);
-                    if (ret !== 0) return ret;
-                }
-                // break tie for similar records,
-                // required to have consistent ordering for tree paths
-                var ret = compareCells(a.recid, b.recid, -1, 'asc');
-                if (ret !== 0) return ret;
-                return 0;
-            }
+            switch (direction) {
+                case 'left':
+                    width1 = scrollBox.outerWidth();
+                    width2 = scrollBox.find(':first').outerWidth();
+                    scroll = scrollLeft - width1 + 50; // 35 is width of both 
button
+                    if (scroll <= 0) scroll = 0;
+                    scrollBox.animate({ scrollLeft: scroll }, 300);
+                    break;
 
-            // compare two values, aa and bb, producing consistent ordering
-            function compareCells(aa, bb, i, direction) {
-                // if both objects are strictly equal, we're done
-                if (aa === bb)
-                    return 0;
-                // all nulls, empty and undefined on bottom
-                if ((aa == null || aa === "") && (bb != null && bb !== ""))
-                    return 1;
-                if ((aa != null && aa !== "") && (bb == null || bb === ""))
-                    return -1;
-                var dir = (direction == 'asc') ? 1 : -1;
-                // for different kind of objects, sort by object type
-                if (typeof aa != typeof bb)
-                    return (typeof aa > typeof bb) ? dir : -dir;
-                // for different kind of classes, sort by classes
-                if (aa.constructor.name != bb.constructor.name)
-                    return (aa.constructor.name > bb.constructor.name) ? dir : 
-dir;
-                // if we're dealing with non-null objects, call valueOf().
-                // this mean that Date() or custom objects will compare 
properly.
-                if (aa && typeof aa == 'object')
-                    aa = aa.valueOf();
-                if (bb && typeof bb == 'object')
-                    bb = bb.valueOf();
-                // if we're still dealing with non-null objects that have
-                // a useful Object => String conversion, convert to string.
-                var defaultToString = {}.toString;
-                if (aa && typeof aa == 'object' && aa.toString != 
defaultToString)
-                    aa = String(aa);
-                if (bb && typeof bb == 'object' && bb.toString != 
defaultToString)
-                    bb = String(bb);
-                // do case-insensitive string comparaison
-                if (typeof aa == 'string')
-                    aa = $.trim(aa.toLowerCase());
-                if (typeof bb == 'string')
-                    bb = $.trim(bb.toLowerCase());
-                // compare both objects
-                if (aa > bb)
-                    return dir;
-                if (aa < bb)
-                    return -dir;
-                return 0;
+                case 'right':
+                    width1 = scrollBox.outerWidth();
+                    width2 = scrollBox.find(':first').outerWidth();
+                    scroll = scrollLeft + width1 - 50; // 35 is width of both 
button
+                    if (scroll >= width2 - width1) scroll = width2 - width1;
+                    scrollBox.animate({ scrollLeft: scroll }, 300);
+                    break;
             }
+            setTimeout(function () { obj.resize(); }, 350);
         },
 
-        localSearch: function (silent) {
-            var url = (typeof this.url != 'object' ? this.url : this.url.get);
-            if (url) {
-                console.log('ERROR: grid.localSearch can only be used on local 
data source, grid.url should be empty.');
-                return;
-            }
+        render: function (box) {
             var time = (new Date()).getTime();
-            var obj = this;
-            var defaultToString = {}.toString;
-            var duplicateMap = {};
-            this.total = this.records.length;
-            // mark all records as shown
-            this.last.searchIds = [];
-            // prepare date/time fields
-            this.prepareData();
-            // hide records that did not match
-            if (this.searchData.length > 0 && !url) {
-                this.total = 0;
-                for (var i = 0; i < this.records.length; i++) {
-                    var rec = this.records[i];
-                    var match = searchRecord(rec);
-                    if (match) {
-                        if (rec && rec.w2ui)
-                            addParent(rec.w2ui.parent_recid);
-                        this.last.searchIds.push(i);
-                    }
+            // event before
+            var edata = this.trigger({ phase: 'before', type: 'render', 
target: this.name, box: box });
+            if (edata.isCancelled === true) return;
+
+            if (box != null) {
+                if ($(this.box).find('> table #tb_'+ this.name + 
'_right').length > 0) {
+                    $(this.box)
+                        .removeAttr('name')
+                        .removeClass('w2ui-reset w2ui-toolbar')
+                        .html('');
                 }
-                this.total = this.last.searchIds.length;
+                this.box = box;
             }
-            time = (new Date()).getTime() - time;
-            if (silent !== true && obj.show.statusSearch) {
-                setTimeout(function () {
-                    obj.status(w2utils.lang('Search took') + ' ' + time/1000 + 
' ' + w2utils.lang('sec'));
-                }, 10);
+            if (!this.box) return;
+            // render all buttons
+            var html = '<div class="w2ui-scroll-wrapper" onmousedown="var 
el=w2ui[\''+ this.name +'\']; if (el) el.resize();" onscroll="var 
el=w2ui[\'editbar\']; if (el) el.resize();">'+
+                       '<table cellspacing="0" cellpadding="0" 
width="100%"><tbody>'+
+                       '<tr>';
+            for (var i = 0; i < this.items.length; i++) {
+                var it = this.items[i];
+                if (it == null)  continue;
+                if (it.id == null) it.id = "item_" + i;
+                if (it.type == 'spacer') {
+                    html += '<td width="100%" id="tb_'+ this.name +'_item_'+ 
it.id +'" align="right"></td>';
+                } else {
+                    html += '<td id="tb_'+ this.name + '_item_'+ it.id +'" 
style="'+ (it.hidden ? 'display: none' : '') +'" '+
+                            '    class="'+ (it.disabled ? 'disabled' : '') +'" 
valign="middle">'+
+                            '</td>';
+                }
             }
-            return time;
+            html += '<td width="100%" id="tb_'+ this.name +'_right" 
align="right">'+ this.right +'</td>';
+            html += '</tr>'+
+                    '</tbody></table></div>'+
+                    '<div class="w2ui-scroll-left" 
onmousedown="event.preventDefault()" 
onclick="if($(this).closest(\'#toolbar-up\').length>0 || 
$(\'#toolbar-down\').width() > 768){var el=w2ui[\''+ this.name +'\']; if (el) 
el.scroll(\'left\');}"></div>'+
+                    '<div class="w2ui-scroll-right" 
onmousedown="event.preventDefault()" 
onclick="if($(this).closest(\'#toolbar-up\').length>0 || 
$(\'#toolbar-down\').width() > 768){var el=w2ui[\''+ this.name +'\']; if (el) 
el.scroll(\'right\');}"></div>';
+            $(this.box)
+                .attr('name', this.name)
+                .addClass('w2ui-reset w2ui-toolbar')
+                .html(html);
+            if ($(this.box).length > 0) $(this.box)[0].style.cssText += 
this.style;
+            // refresh all
+            this.refresh();
+            this.resize();
+            // event after
+            this.trigger($.extend(edata, { phase: 'after' }));
+            return (new Date()).getTime() - time;
+        },
 
-            // check if a record (or one of its closed children) matches the 
search data
-            function searchRecord(rec) {
-                var fl  = 0;
-                for (var j = 0; j < obj.searchData.length; j++) {
-                    var sdata  = obj.searchData[j];
-                    var search = obj.getSearch(sdata.field);
-                    if (sdata  == null) continue;
-                    if (search == null) search = { field: sdata.field, type: 
sdata.type };
-                    var val1b = obj.parseField(rec, search.field);
-                    var val1 = (val1b !== null && val1b !== undefined &&
-                        (typeof val1b != "object" || val1b.toString != 
defaultToString)) ?
-                        String(val1b).toLowerCase() : "";  // do not match a 
bogus string
-                    if (sdata.value != null) {
-                        if (!$.isArray(sdata.value)) {
-                            var val2 = String(sdata.value).toLowerCase();
-                        } else {
-                            var val2 = sdata.value[0];
-                            var val3 = sdata.value[1];
-                        }
-                    }
-                    switch (sdata.operator) {
-                    case 'is':
-                        if (obj.parseField(rec, search.field) == sdata.value) 
fl++; // do not hide record
-                        else if (search.type == 'date') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
-                            var val2 = w2utils.formatDate(w2utils.isDate(val2, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
-                            if (val1 == val2) fl++;
-                        }
-                        else if (search.type == 'time') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatTime(tmp, 'hh24:mi');
-                            var val2 = w2utils.formatTime(val2, 'hh24:mi');
-                            if (val1 == val2) fl++;
-                        }
-                        else if (search.type == 'datetime') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDateTime(tmp, 
'yyyy-mm-dd|hh24:mm:ss');
-                            var val2 = 
w2utils.formatDateTime(w2utils.isDateTime(val2, 
w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
-                            if (val1 == val2) fl++;
-                        }
-                        break;
-                    case 'between':
-                        if (['int', 'float', 'money', 'currency', 
'percent'].indexOf(search.type) != -1) {
-                            if (parseFloat(obj.parseField(rec, search.field)) 
>= parseFloat(val2) && parseFloat(obj.parseField(rec, search.field)) <= 
parseFloat(val3)) fl++;
-                        }
-                        else if (search.type == 'date') {
-                            var val1 = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val2 = w2utils.isDate(val2, 
w2utils.settings.dateFormat, true);
-                            var val3 = w2utils.isDate(val3, 
w2utils.settings.dateFormat, true);
-                            if (val3 != null) val3 = new Date(val3.getTime() + 
86400000); // 1 day
-                            if (val1 >= val2 && val1 < val3) fl++;
-                        }
-                        else if (search.type == 'time') {
-                            var val1 = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val2 = w2utils.isTime(val2, true);
-                            var val3 = w2utils.isTime(val3, true);
-                            val2 = (new Date()).setHours(val2.hours, 
val2.minutes, val2.seconds ? val2.seconds : 0, 0);
-                            val3 = (new Date()).setHours(val3.hours, 
val3.minutes, val3.seconds ? val3.seconds : 0, 0);
-                            if (val1 >= val2 && val1 < val3) fl++;
-                        }
-                        else if (search.type == 'datetime') {
-                            var val1 = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val2 = w2utils.isDateTime(val2, 
w2utils.settings.datetimeFormat, true);
-                            var val3 = w2utils.isDateTime(val3, 
w2utils.settings.datetimeFormat, true);
-                            if (val3) val3 = new Date(val3.getTime() + 
86400000); // 1 day
-                            if (val1 >= val2 && val1 < val3) fl++;
-                        }
-                        break;
-                    case 'less':
-                        if (['int', 'float', 'money', 'currency', 
'percent'].indexOf(search.type) != -1) {
-                            if (parseFloat(obj.parseField(rec, search.field)) 
<= parseFloat(sdata.value)) fl++;
-                        }
-                        else if (search.type == 'date') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
-                            var val2 = w2utils.formatDate(w2utils.isDate(val2, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
-                            if (val1 <= val2) fl++;
-                        }
-                        else if (search.type == 'time') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatTime(tmp, 'hh24:mi');
-                            var val2 = w2utils.formatTime(val2, 'hh24:mi');
-                            if (val1 <= val2) fl++;
-                        }
-                        else if (search.type == 'datetime') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDateTime(tmp, 
'yyyy-mm-dd|hh24:mm:ss');
-                            var val2 = 
w2utils.formatDateTime(w2utils.isDateTime(val2, 
w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
-                            if ( (val1.length == val2.length) && (val1 <= 
val2) ) fl++;
-                        }
-                        break;
-                    case 'more':
-                        if (['int', 'float', 'money', 'currency', 
'percent'].indexOf(search.type) != -1) {
-                            if (parseFloat(obj.parseField(rec, search.field)) 
>= parseFloat(sdata.value)) fl++;
-                        }
-                        else if (search.type == 'date') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDate(tmp, 'yyyy-mm-dd');
-                            var val2 = w2utils.formatDate(w2utils.isDate(val2, 
w2utils.settings.dateFormat, true), 'yyyy-mm-dd');
-                            if (val1 >= val2) fl++;
-                        }
-                        else if (search.type == 'time') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatTime(tmp, 'hh24:mi');
-                            var val2 = w2utils.formatTime(val2, 'hh24:mi');
-                            if (val1 >= val2) fl++;
-                        }
-                        else if (search.type == 'datetime') {
-                            var tmp  = (obj.parseField(rec, search.field + 
'_') instanceof Date ? obj.parseField(rec, search.field + '_') : 
obj.parseField(rec, search.field));
-                            var val1 = w2utils.formatDateTime(tmp, 
'yyyy-mm-dd|hh24:mm:ss');
-                            var val2 = 
w2utils.formatDateTime(w2utils.isDateTime(val2, 
w2utils.settings.datetimeFormat, true), 'yyyy-mm-dd|hh24:mm:ss');
-                            if ( (val1.length == val2.length) && (val1 >= 
val2) ) fl++;
-                        }
-                        break;
-                    case 'in':
-                        var tmp = sdata.value;
-                        if (sdata.svalue) tmp = sdata.svalue;
-                        if (tmp.indexOf(w2utils.isFloat(val1) ? 
parseFloat(val1) : val1) !== -1) fl++;
-                        if (tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) !== -1) fl++;
-                        break;
-                    case 'not in':
-                        var tmp = sdata.value;
-                        if (sdata.svalue) tmp = sdata.svalue;
-                        if (tmp.indexOf(w2utils.isFloat(val1) ? 
parseFloat(val1) : val1) == -1) fl++;
-                        if (tmp.indexOf(w2utils.isFloat(val1b) ? 
parseFloat(val1b) : val1b) == -1) fl++;
-                        break;
-                    case 'begins':
-                    case 'begins with': // need for back compatib.
-                        if (val1.indexOf(val2) === 0) fl++; // do not hide 
record
-                        break;
-                    case 'contains':
-                        if (val1.indexOf(val2) >= 0) fl++; // do not hide 
record
-                        break;
-                    case 'null':
-                        if (obj.parseField(rec, search.field) == null) fl++; 
// do not hide record
-                        break;
-                    case 'not null':
-                        if (obj.parseField(rec, search.field) != null) fl++; 
// do not hide record
-                        break;
-                    case 'ends':
-                    case 'ends with': // need for back compatib.
-                        var lastIndex = val1.lastIndexOf(val2);
-                        if (lastIndex !== -1 && lastIndex == val1.length - 
val2.length) fl++; // do not hide record
-                        break;
-                    }
-                }
-                if ((obj.last.logic == 'OR' && fl !== 0) ||
-                    (obj.last.logic == 'AND' && fl == obj.searchData.length))
-                    return true;
-                if (rec.w2ui && rec.w2ui.children && rec.w2ui.expanded !== 
true) {
-                    // there are closed children, search them too.
-                    for (var r = 0; r < rec.w2ui.children.length; r++) {
-                        var subRec = rec.w2ui.children[r];
-                        if (searchRecord(subRec))
-                            return true;
-                    }
-                }
-                return false;
-            }
-
-            // add parents nodes recursively
-            function addParent(recid) {
-                if (recid === undefined)
-                    return;
-                if (duplicateMap[recid])
-                    return; // already visited
-                duplicateMap[recid] = true;
-                var i = obj.get(recid, true);
-                if (i == null)
-                    return;
-                if ($.inArray(i, obj.last.searchIds) != -1)
-                    return;
-                var rec = obj.records[i];
-                if (rec && rec.w2ui)
-                    addParent(rec.w2ui.parent_recid);
-                obj.last.searchIds.push(i);
-            }
-        },
-
-        getRangeData: function (range, extra) {
-            var rec1 = this.get(range[0].recid, true);
-            var rec2 = this.get(range[1].recid, true);
-            var col1 = range[0].column;
-            var col2 = range[1].column;
-
-            var res = [];
-            if (col1 == col2) { // one row
-                for (var r = rec1; r <= rec2; r++) {
-                    var record = this.records[r];
-                    var dt = record[this.columns[col1].field] || null;
-                    if (extra !== true) {
-                        res.push(dt);
-                    } else {
-                        res.push({ data: dt, column: col1, index: r, record: 
record });
-                    }
-                }
-            } else if (rec1 == rec2) { // one line
-                var record = this.records[rec1];
-                for (var i = col1; i <= col2; i++) {
-                    var dt = record[this.columns[i].field] || null;
-                    if (extra !== true) {
-                        res.push(dt);
-                    } else {
-                        res.push({ data: dt, column: i, index: rec1, record: 
record });
-                    }
-                }
-            } else {
-                for (var r = rec1; r <= rec2; r++) {
-                    var record = this.records[r];
-                    res.push([]);
-                    for (var i = col1; i <= col2; i++) {
-                        var dt = record[this.columns[i].field];
-                        if (extra !== true) {
-                            res[res.length-1].push(dt);
-                        } else {
-                            res[res.length-1].push({ data: dt, column: i, 
index: r, record: record });
-                        }
-                    }
-                }
-            }
-            return res;
-        },
-
-        addRange: function (ranges) {
-            var added = 0;
-            if (this.selectType == 'row') return added;
-            if (!$.isArray(ranges)) ranges = [ranges];
-            // if it is selection
-            for (var i = 0; i < ranges.length; i++) {
-                if (typeof ranges[i] != 'object') ranges[i] = { name: 
'selection' };
-                if (ranges[i].name == 'selection') {
-                    if (this.show.selectionBorder === false) continue;
-                    var sel = this.getSelection();
-                    if (sel.length === 0) {
-                        this.removeRange('selection');
-                        continue;
-                    } else {
-                        var first = sel[0];
-                        var last  = sel[sel.length-1];
-                    }
-                } else { // other range
-                    var first = ranges[i].range[0];
-                    var last  = ranges[i].range[1];
-                }
-                if (first) {
-                    var rg = {
-                        name: ranges[i].name,
-                        range: [{ recid: first.recid, column: first.column }, 
{ recid: last.recid, column: last.column }],
-                        style: ranges[i].style || ''
-                    };
-                    // add range
-                    var ind = false;
-                    for (var j = 0; j < this.ranges.length; j++) if 
(this.ranges[j].name == ranges[i].name) { ind = j; break; }
-                    if (ind !== false) {
-                        this.ranges[ind] = rg;
-                    } else {
-                        this.ranges.push(rg);
-                    }
-                    added++;
-                }
-            }
-            this.refreshRanges();
-            return added;
-        },
-
-        removeRange: function () {
-            var removed = 0;
-            for (var a = 0; a < arguments.length; a++) {
-                var name = arguments[a];
-                $('#grid_'+ this.name +'_'+ name).remove();
-                $('#grid_'+ this.name +'_f'+ name).remove();
-                for (var r = this.ranges.length-1; r >= 0; r--) {
-                    if (this.ranges[r].name == name) {
-                        this.ranges.splice(r, 1);
-                        removed++;
-                    }
-                }
-            }
-            return removed;
-        },
-
-        refreshRanges: function () {
-            if (this.ranges.length === 0) return;
-            var obj  = this;
+        refresh: function (id) {
             var time = (new Date()).getTime();
-            var rec1 = $('#grid_'+ this.name +'_frecords');
-            var rec2 = $('#grid_'+ this.name +'_records');
-            for (var i = 0; i < this.ranges.length; i++) {
-                var rg    = this.ranges[i];
-                var first = rg.range[0];
-                var last  = rg.range[1];
-                if (first.index == null) first.index = this.get(first.recid, 
true);
-                if (last.index == null) last.index = this.get(last.recid, 
true);
-                var td1   = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(first.recid) + ' td[col="'+ first.column +'"]');
-                var td2   = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(last.recid) + ' td[col="'+ last.column +'"]');
-                var td1f  = $('#grid_'+ this.name +'_frec_'+ 
w2utils.escapeId(first.recid) + ' td[col="'+ first.column +'"]');
-                var td2f  = $('#grid_'+ this.name +'_frec_'+ 
w2utils.escapeId(last.recid) + ' td[col="'+ last.column +'"]');
-                var _lastColumn = last.column;
-                // adjustment due to column virtual scroll
-                if (first.column < this.last.colStart && last.column > 
this.last.colStart) {
-                    td1 = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(first.recid) + ' td[col="start"]');
-                }
-                if (first.column < this.last.colEnd && last.column > 
this.last.colEnd) {
-                    _lastColumn = '"end"';
-                    td2 = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(last.recid) + ' td[col="end"]');
-                }
-                // if virtual scrolling kicked in
-                var index_top     = parseInt($('#grid_'+ this.name 
+'_rec_top').next().attr('index'));
-                var index_bottom  = parseInt($('#grid_'+ this.name 
+'_rec_bottom').prev().attr('index'));
-                var index_ftop    = parseInt($('#grid_'+ this.name 
+'_frec_top').next().attr('index'));
-                var index_fbottom = parseInt($('#grid_'+ this.name 
+'_frec_bottom').prev().attr('index'));
-                if (td1.length === 0 && first.index < index_top && last.index 
> index_top) {
-                    td1 = $('#grid_'+ this.name 
+'_rec_top').next().find('td[col='+ first.column +']');
-                }
-                if (td2.length === 0 && last.index > index_bottom && 
first.index < index_bottom) {
-                    td2 = $('#grid_'+ this.name 
+'_rec_bottom').prev().find('td[col='+ _lastColumn +']');
-                }
-                if (td1f.length === 0 && first.index < index_ftop && 
last.index > index_ftop) { // frozen
-                    td1f = $('#grid_'+ this.name 
+'_frec_top').next().find('td[col='+ first.column +']');
-                }
-                if (td2f.length === 0 && last.index > index_fbottom && 
first.index < index_fbottom) {  // frozen
-                    td2f = $('#grid_'+ this.name 
+'_frec_bottom').prev().find('td[col='+ last.column +']');
-                }
-
-                // do not show selection cell if it is editable
-                var edit  = $(this.box).find('#grid_'+ this.name + 
'_editable');
-                var tmp   = edit.find('.w2ui-input');
-                var tmp1  = tmp.attr('recid');
-                var tmp2  = tmp.attr('column');
-                if (rg.name == 'selection' && rg.range[0].recid == tmp1 && 
rg.range[0].column == tmp2) continue;
-
-                // frozen regular columns range
-                var $range = $('#grid_'+ this.name +'_f'+ rg.name);
-                if (td1f.length > 0 || td2f.length > 0) {
-                    if ($range.length === 0) {
-                        rec1.append('<div id="grid_'+ this.name +'_f' + 
rg.name +'" class="w2ui-selection" style="'+ rg.style +'">'+
-                                        (rg.name == 'selection' ?  '<div 
id="grid_'+ this.name +'_resizer" class="w2ui-selection-resizer"></div>' : '')+
-                                    '</div>');
-                        $range = $('#grid_'+ this.name +'_f'+ rg.name);
-                    } else {
-                        $range.attr('style', rg.style);
-                        $range.find('.w2ui-selection-resizer').show();
-                    }
-                    if (td2f.length === 0) {
-                        td2f  = $('#grid_'+ this.name +'_frec_'+ 
w2utils.escapeId(last.recid) +' td:last-child');
-                        if (td2f.length === 0) td2f = $('#grid_'+ this.name 
+'_frec_bottom td:first-child');
-                        $range.css('border-right', '0px');
-                        $range.find('.w2ui-selection-resizer').hide();
-                    }
-                    if (first.recid != null && last.recid != null && 
td1f.length > 0 && td2f.length > 0) {
-                        var _left = (td1f.position().left - 1 + 
rec1.scrollLeft());
-                        var _top  = (td1f.position().top - 1 + 
rec1.scrollTop());
-                        $range.show().css({
-                            left    : (_left > 0 ? _left : 0) + 'px',
-                            top     : (_top > 0 ? _top : 0) + 'px',
-                            width   : (td2f.position().left - 
td1f.position().left + td2f.width() + 3) + 'px',
-                            height  : (td2f.position().top - 
td1f.position().top + td2f.height() + 3) + 'px'
-                        });
-                    } else {
-                        $range.hide();
-                    }
-                } else {
-                    $range.hide();
-                }
-                // regular columns range
-                var $range = $('#grid_'+ this.name +'_'+ rg.name);
-                if (td1.length > 0 || td2.length > 0) {
-                    if ($range.length === 0) {
-                        rec2.append('<div id="grid_'+ this.name +'_' + rg.name 
+'" class="w2ui-selection" style="'+ rg.style +'">'+
-                                        (rg.name == 'selection' ?  '<div 
id="grid_'+ this.name +'_resizer" class="w2ui-selection-resizer"></div>' : '')+
-                                    '</div>');
-                        $range = $('#grid_'+ this.name +'_'+ rg.name);
-                    } else {
-                        $range.attr('style', rg.style);
-                    }
-                    if (td1.length === 0) {
-                        td1 = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(first.recid) +' td:first-child');
-                        if (td1.length === 0) td1 = $('#grid_'+ this.name 
+'_rec_top td:first-child');
-                    }
-                    if (td2f.length !== 0) {
-                        $range.css('border-left', '0px');
-                    }
-                    if (first.recid != null && last.recid != null && 
td1.length > 0 && td2.length > 0) {
-                        var _left = (td1.position().left - 1 + 
rec2.scrollLeft());
-                        var _top  = (td1.position().top - 1 + 
rec2.scrollTop());
-                        $range.show().css({
-                            left    : (_left > 0 ? _left : 0) + 'px',
-                            top     : (_top > 0 ? _top : 0) + 'px',
-                            width   : (td2.position().left - 
td1.position().left + td2.width() + 3) + 'px',
-                            height  : (td2.position().top - td1.position().top 
+ td2.height() + 3) + 'px'
-                        });
-                    } else {
-                        $range.hide();
-                    }
-                } else {
-                    $range.hide();
+            // event before
+            var edata = this.trigger({ phase: 'before', type: 'refresh', 
target: (id != null ? id : this.name), item: this.get(id) });
+            if (edata.isCancelled === true) return;
+            // refresh all
+            if (id == null) {
+                for (var i = 0; i < this.items.length; i++) {
+                    var it1 = this.items[i];
+                    if (it1.id == null) it1.id = "item_" + i;
+                    this.refresh(it1.id);
                 }
+                return;
             }
-
-            // add resizer events
-            $(this.box).find('.w2ui-selection-resizer')
-                .off('mousedown').on('mousedown', mouseStart)
-                .off('dblclick').on('dblclick', function (event) {
-                    var edata = obj.trigger({ phase: 'before', type: 
'resizerDblClick', target: obj.name, originalEvent: event });
-                    if (edata.isCancelled === true) return;
-                    obj.trigger($.extend(edata, { phase: 'after' }));
-                });
-            var edata = { phase: 'before', type: 'selectionExtend', target: 
obj.name, originalRange: null, newRange: null };
-
-            return (new Date()).getTime() - time;
-
-            function mouseStart (event) {
-                var sel = obj.getSelection();
-                obj.last.move = {
-                    type   : 'expand',
-                    x      : event.screenX,
-                    y      : event.screenY,
-                    divX   : 0,
-                    divY   : 0,
-                    recid  : sel[0].recid,
-                    column : sel[0].column,
-                    originalRange : [{ recid: sel[0].recid, column: 
sel[0].column }, { recid: sel[sel.length-1].recid, column: 
sel[sel.length-1].column }],
-                    newRange      : [{ recid: sel[0].recid, column: 
sel[0].column }, { recid: sel[sel.length-1].recid, column: 
sel[sel.length-1].column }]
-                };
-                $(document).off('mousemove', mouseMove).on('mousemove', 
mouseMove);
-                $(document).off('mouseup', mouseStop).on('mouseup', mouseStop);
-                // do not blur grid
-                event.preventDefault();
+            // create or refresh only one item
+            var it = this.get(id);
+            if (it == null) return false;
+            if (typeof it.onRefresh == 'function') {
+                var edata2 = this.trigger({ phase: 'before', type: 'refresh', 
target: id, item: it, object: it });
+                if (edata2.isCancelled === true) return;
             }
-
-            function mouseMove (event) {
-                var mv = obj.last.move;
-                if (!mv || mv.type != 'expand') return;
-                mv.divX = (event.screenX - mv.x);
-                mv.divY = (event.screenY - mv.y);
-                // find new cell
-                var recid, column;
-                var tmp = event.originalEvent.target;
-                if (tmp.tagName.toUpperCase() != 'TD') tmp = 
$(tmp).parents('td')[0];
-                if ($(tmp).attr('col') != null) column = 
parseInt($(tmp).attr('col'));
-                tmp = $(tmp).parents('tr')[0];
-                recid = $(tmp).attr('recid');
-                // new range
-                if (mv.newRange[1].recid == recid && mv.newRange[1].column == 
column) return;
-                var prevNewRange = $.extend({}, mv.newRange);
-                mv.newRange = [{ recid: mv.recid, column: mv.column }, { 
recid: recid, column: column }];
-                // event before
-                edata = obj.trigger($.extend(edata, { originalRange: 
mv.originalRange, newRange : mv.newRange }));
-                if (edata.isCancelled === true) {
-                    mv.newRange        = prevNewRange;
-                    edata.newRange = prevNewRange;
-                    return;
+            var el = $(this.box).find('#tb_'+ this.name +'_item_'+ 
w2utils.escapeId(it.id));
+            var html  = this.getItemHTML(it);
+            if (el.length === 0) {
+                // does not exist - create it
+                if (it.type == 'spacer') {
+                    html = '<td width="100%" id="tb_'+ this.name +'_item_'+ 
it.id +'" align="right"></td>';
                 } else {
-                    // default behavior
-                    obj.removeRange('grid-selection-expand');
-                    obj.addRange({
-                        name  : 'grid-selection-expand',
-                        range : edata.newRange,
-                        style : 'background-color: rgba(100,100,100,0.1); 
border: 2px dotted rgba(100,100,100,0.5);'
-                    });
+                    html = '<td id="tb_'+ this.name + '_item_'+ it.id +'" 
style="'+ (it.hidden ? 'display: none' : '') +'" '+
+                        '    class="'+ (it.disabled ? 'disabled' : '') +'" 
valign="middle">'+ html +
+                        '</td>';
                 }
-            }
-
-            function mouseStop (event) {
-                // default behavior
-                obj.removeRange('grid-selection-expand');
-                delete obj.last.move;
-                $(document).off('mousemove', mouseMove);
-                $(document).off('mouseup', mouseStop);
-                // event after
-                obj.trigger($.extend(edata, { phase: 'after' }));
-            }
-        },
-
-        select: function () {
-            if (arguments.length === 0) return 0;
-            var time = (new Date).getTime();
-            var selected = 0;
-            var sel = this.last.selection;
-            if (!this.multiSelect) this.selectNone();
-
-            // event before
-            var tmp = { phase: 'before', type: 'select', target: this.name };
-            if (arguments.length == 1) {
-                tmp.multiple = false;
-                if ($.isPlainObject(arguments[0])) {
-                    tmp.recid  = arguments[0].recid;
-                    tmp.column = arguments[0].column;
+                if (this.get(id, true) == this.items.length-1) {
+                    $(this.box).find('#tb_'+ this.name +'_right').before(html);
                 } else {
-                    tmp.recid = arguments[0];
-                }
-            } else {
-                tmp.multiple = true;
-                tmp.recids   = Array.prototype.slice.call(arguments, 0);
-            }
-            var edata = this.trigger(tmp);
-            if (edata.isCancelled === true) return 0;
-
-            // default action
-            if (this.selectType == 'row') {
-                for (var a = 0; a < arguments.length; a++) {
-                    var recid  = typeof arguments[a] == 'object' ? 
arguments[a].recid : arguments[a];
-                    var index = this.get(recid, true);
-                    if (index == null) continue;
-                    var recEl1 = null;
-                    var recEl2 = null;
-                    if (this.searchData.length !== 0 || (index + 1 >= 
this.last.range_start && index + 1 <= this.last.range_end)) {
-                        recEl1 = $('#grid_'+ this.name +'_frec_'+ 
w2utils.escapeId(recid));
-                        recEl2 = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(recid));
-                    }
-                    if (this.selectType == 'row') {
-                        if (sel.indexes.indexOf(index) != -1) continue;
-                        sel.indexes.push(index);
-                        if (recEl1 && recEl2) {
-                            recEl1.addClass('w2ui-selected').data('selected', 
'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
-                            recEl2.addClass('w2ui-selected').data('selected', 
'yes').find('.w2ui-col-number').addClass('w2ui-row-selected');
-                            
recEl1.find('.w2ui-grid-select-check').prop("checked", true);
-                        }
-                        selected++;
-                    }
+                    $(this.box).find('#tb_'+ this.name +'_item_'+ 
w2utils.escapeId(this.items[parseInt(this.get(id, true))+1].id)).before(html);
                 }
             } else {
-                // normalize for performance
-                var new_sel = {};
-                for (var a = 0; a < arguments.length; a++) {
-                    var recid  = typeof arguments[a] == 'object' ? 
arguments[a].recid : arguments[a];
-                    var column = typeof arguments[a] == 'object' ? 
arguments[a].column : null;
-                    new_sel[recid] = new_sel[recid] || [];
-                    if ($.isArray(column)) {
-                        new_sel[recid] = column;
-                    } else if (w2utils.isInt(column)) {
-                        new_sel[recid].push(column);
-                    } else {
-                        for (var i = 0; i < this.columns.length; i++) { if 
(this.columns[i].hidden) continue; new_sel[recid].push(parseInt(i)); }
-                    }
-                }
-                // add all
-                var col_sel = [];
-                for (var recid in new_sel) {
-                    var index = this.get(recid, true);
-                    if (index == null) continue;
-                    var recEl1 = null;
-                    var recEl2 = null;
-                    if (index + 1 >= this.last.range_start && index + 1 <= 
this.last.range_end) {
-                        recEl1 = $('#grid_'+ this.name +'_rec_'+ 
w2utils.escapeId(recid));
-                        recEl2 = $('#grid_'+ this.name +'_frec_'+ 
w2utils.escapeId(recid));
-                    }
-                    var s = sel.columns[index] || [];
-                    // default action
-                    if (sel.indexes.indexOf(index) == -1) {
-                        sel.indexes.push(index);
-                    }
-                    // anly only those that are new
-                    for (var t = 0; t < new_sel[recid].length; t++) {
-                        if (s.indexOf(new_sel[recid][t]) == -1) 
s.push(new_sel[recid][t]);
-                    }
-                    s.sort(function(a, b) { return a-b; }); // sort function 
must be for numerical sort
-                    for (var t = 0; t < new_sel[recid].length; t++) {
-                        var col = new_sel[recid][t];
-                        if (col_sel.indexOf(col) == -1) col_sel.push(col);

... etc. - the rest is truncated
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to