diff --git a/conf/index.html b/conf/index.html index 65cb220712..fae62bdc0f 100644 --- a/conf/index.html +++ b/conf/index.html @@ -26,6 +26,7 @@ } {% endif %} + diff --git a/css/legacy/body.css b/css/legacy/body.css index ed8686872f..df77e1fd41 100644 --- a/css/legacy/body.css +++ b/css/legacy/body.css @@ -235,4 +235,4 @@ div.std-footer-item { height: 24px; margin-bottom: -7px; max-width: 24px; -} +} \ No newline at end of file diff --git a/css/legacy/final.css b/css/legacy/final.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/css/ui/filter.css b/css/ui/list.css similarity index 78% rename from css/ui/filter.css rename to css/ui/list.css index d3ae112ad2..243d40399b 100644 --- a/css/ui/filter.css +++ b/css/ui/list.css @@ -1,3 +1,26 @@ +.hide { + display: none; +} + +.list-filters { + margin: 7px 0px; +} + +.wnlist .img-load { + display: none; + float: left; + margin-left: 11px; + margin-top: 8px; +} + +div.list-row { + border-bottom: 1px solid #eee; + padding: 3px 0px; +} +div.list-row:hover { + background-color: #eef +} + div.show_filters { display: none; } @@ -8,7 +31,7 @@ div.filter_list { div.show_filters.well { margin-top: 11px; - margin-bottom: 0px; + margin-bottom: 11px; } div.filter_list .run_btn { @@ -46,9 +69,3 @@ span.bar-inner { span.bar-complete { background-color: green; } -div.list-row { - border-bottom: 1px solid #eee; -} -div.list-row:hover { - background-color: #eef -} \ No newline at end of file diff --git a/js/legacy/widgets/listing.js b/js/legacy/widgets/listing.js index 7b7d4295cb..add4557c4d 100644 --- a/js/legacy/widgets/listing.js +++ b/js/legacy/widgets/listing.js @@ -23,7 +23,7 @@ // Listing // this listing object will soon be // deprecated because it has a very non standard way of creation -// the new listing object is at wn.widgets.Listing +// the new listing object is at wn.ui.Listing // ----------------------- list_opts = { diff --git a/js/lib/slickgrid/images/actions.gif b/js/lib/slickgrid/images/actions.gif new file mode 100644 index 0000000000..026dd108ed Binary files /dev/null and b/js/lib/slickgrid/images/actions.gif differ diff --git a/js/lib/slickgrid/images/ajax-loader-small.gif b/js/lib/slickgrid/images/ajax-loader-small.gif new file mode 100644 index 0000000000..5b33f7e54f Binary files /dev/null and b/js/lib/slickgrid/images/ajax-loader-small.gif differ diff --git a/js/lib/slickgrid/images/arrow_redo.png b/js/lib/slickgrid/images/arrow_redo.png new file mode 100644 index 0000000000..fdc394c7c5 Binary files /dev/null and b/js/lib/slickgrid/images/arrow_redo.png differ diff --git a/js/lib/slickgrid/images/arrow_right_peppermint.png b/js/lib/slickgrid/images/arrow_right_peppermint.png new file mode 100644 index 0000000000..1804fa9f90 Binary files /dev/null and b/js/lib/slickgrid/images/arrow_right_peppermint.png differ diff --git a/js/lib/slickgrid/images/arrow_right_spearmint.png b/js/lib/slickgrid/images/arrow_right_spearmint.png new file mode 100644 index 0000000000..298515ab6c Binary files /dev/null and b/js/lib/slickgrid/images/arrow_right_spearmint.png differ diff --git a/js/lib/slickgrid/images/arrow_undo.png b/js/lib/slickgrid/images/arrow_undo.png new file mode 100644 index 0000000000..6972c5e594 Binary files /dev/null and b/js/lib/slickgrid/images/arrow_undo.png differ diff --git a/js/lib/slickgrid/images/bullet_blue.png b/js/lib/slickgrid/images/bullet_blue.png new file mode 100644 index 0000000000..a7651ec8a0 Binary files /dev/null and b/js/lib/slickgrid/images/bullet_blue.png differ diff --git a/js/lib/slickgrid/images/bullet_star.png b/js/lib/slickgrid/images/bullet_star.png new file mode 100644 index 0000000000..3829023b8e Binary files /dev/null and b/js/lib/slickgrid/images/bullet_star.png differ diff --git a/js/lib/slickgrid/images/bullet_toggle_minus.png b/js/lib/slickgrid/images/bullet_toggle_minus.png new file mode 100644 index 0000000000..b47ce55f68 Binary files /dev/null and b/js/lib/slickgrid/images/bullet_toggle_minus.png differ diff --git a/js/lib/slickgrid/images/bullet_toggle_plus.png b/js/lib/slickgrid/images/bullet_toggle_plus.png new file mode 100644 index 0000000000..9ab4a89664 Binary files /dev/null and b/js/lib/slickgrid/images/bullet_toggle_plus.png differ diff --git a/js/lib/slickgrid/images/calendar.gif b/js/lib/slickgrid/images/calendar.gif new file mode 100644 index 0000000000..90fd2e17fe Binary files /dev/null and b/js/lib/slickgrid/images/calendar.gif differ diff --git a/js/lib/slickgrid/images/collapse.gif b/js/lib/slickgrid/images/collapse.gif new file mode 100644 index 0000000000..01e691450c Binary files /dev/null and b/js/lib/slickgrid/images/collapse.gif differ diff --git a/js/lib/slickgrid/images/comment_yellow.gif b/js/lib/slickgrid/images/comment_yellow.gif new file mode 100644 index 0000000000..df7158a477 Binary files /dev/null and b/js/lib/slickgrid/images/comment_yellow.gif differ diff --git a/js/lib/slickgrid/images/down.gif b/js/lib/slickgrid/images/down.gif new file mode 100644 index 0000000000..9bd9447552 Binary files /dev/null and b/js/lib/slickgrid/images/down.gif differ diff --git a/js/lib/slickgrid/images/drag-handle.png b/js/lib/slickgrid/images/drag-handle.png new file mode 100644 index 0000000000..86727b194f Binary files /dev/null and b/js/lib/slickgrid/images/drag-handle.png differ diff --git a/js/lib/slickgrid/images/editor-helper-bg.gif b/js/lib/slickgrid/images/editor-helper-bg.gif new file mode 100644 index 0000000000..2daa973bc5 Binary files /dev/null and b/js/lib/slickgrid/images/editor-helper-bg.gif differ diff --git a/js/lib/slickgrid/images/expand.gif b/js/lib/slickgrid/images/expand.gif new file mode 100644 index 0000000000..1b24ef1248 Binary files /dev/null and b/js/lib/slickgrid/images/expand.gif differ diff --git a/js/lib/slickgrid/images/header-bg.gif b/js/lib/slickgrid/images/header-bg.gif new file mode 100644 index 0000000000..fe7dd1c1eb Binary files /dev/null and b/js/lib/slickgrid/images/header-bg.gif differ diff --git a/js/lib/slickgrid/images/header-columns-bg.gif b/js/lib/slickgrid/images/header-columns-bg.gif new file mode 100644 index 0000000000..8d459a304e Binary files /dev/null and b/js/lib/slickgrid/images/header-columns-bg.gif differ diff --git a/js/lib/slickgrid/images/header-columns-over-bg.gif b/js/lib/slickgrid/images/header-columns-over-bg.gif new file mode 100644 index 0000000000..f9c07af134 Binary files /dev/null and b/js/lib/slickgrid/images/header-columns-over-bg.gif differ diff --git a/js/lib/slickgrid/images/help.png b/js/lib/slickgrid/images/help.png new file mode 100644 index 0000000000..0eff0a5cb5 Binary files /dev/null and b/js/lib/slickgrid/images/help.png differ diff --git a/js/lib/slickgrid/images/info.gif b/js/lib/slickgrid/images/info.gif new file mode 100644 index 0000000000..5769434fb2 Binary files /dev/null and b/js/lib/slickgrid/images/info.gif differ diff --git a/js/lib/slickgrid/images/listview.gif b/js/lib/slickgrid/images/listview.gif new file mode 100644 index 0000000000..3ec25ca719 Binary files /dev/null and b/js/lib/slickgrid/images/listview.gif differ diff --git a/js/lib/slickgrid/images/pencil.gif b/js/lib/slickgrid/images/pencil.gif new file mode 100644 index 0000000000..29f78f433d Binary files /dev/null and b/js/lib/slickgrid/images/pencil.gif differ diff --git a/js/lib/slickgrid/images/row-over-bg.gif b/js/lib/slickgrid/images/row-over-bg.gif new file mode 100644 index 0000000000..b288e38739 Binary files /dev/null and b/js/lib/slickgrid/images/row-over-bg.gif differ diff --git a/js/lib/slickgrid/images/sort-asc.gif b/js/lib/slickgrid/images/sort-asc.gif new file mode 100644 index 0000000000..67a2a4c669 Binary files /dev/null and b/js/lib/slickgrid/images/sort-asc.gif differ diff --git a/js/lib/slickgrid/images/sort-asc.png b/js/lib/slickgrid/images/sort-asc.png new file mode 100644 index 0000000000..e6b6264fcc Binary files /dev/null and b/js/lib/slickgrid/images/sort-asc.png differ diff --git a/js/lib/slickgrid/images/sort-desc.gif b/js/lib/slickgrid/images/sort-desc.gif new file mode 100644 index 0000000000..34db47c3b1 Binary files /dev/null and b/js/lib/slickgrid/images/sort-desc.gif differ diff --git a/js/lib/slickgrid/images/sort-desc.png b/js/lib/slickgrid/images/sort-desc.png new file mode 100644 index 0000000000..74ad1540e3 Binary files /dev/null and b/js/lib/slickgrid/images/sort-desc.png differ diff --git a/js/lib/slickgrid/images/stripes.png b/js/lib/slickgrid/images/stripes.png new file mode 100644 index 0000000000..0c293a927c Binary files /dev/null and b/js/lib/slickgrid/images/stripes.png differ diff --git a/js/lib/slickgrid/images/tag_red.png b/js/lib/slickgrid/images/tag_red.png new file mode 100644 index 0000000000..6ebb37d25f Binary files /dev/null and b/js/lib/slickgrid/images/tag_red.png differ diff --git a/js/lib/slickgrid/images/tick.png b/js/lib/slickgrid/images/tick.png new file mode 100644 index 0000000000..a9925a06ab Binary files /dev/null and b/js/lib/slickgrid/images/tick.png differ diff --git a/js/lib/slickgrid/images/user_identity.gif b/js/lib/slickgrid/images/user_identity.gif new file mode 100644 index 0000000000..095831ba42 Binary files /dev/null and b/js/lib/slickgrid/images/user_identity.gif differ diff --git a/js/lib/slickgrid/images/user_identity_plus.gif b/js/lib/slickgrid/images/user_identity_plus.gif new file mode 100644 index 0000000000..b276a81c78 Binary files /dev/null and b/js/lib/slickgrid/images/user_identity_plus.gif differ diff --git a/js/lib/slickgrid/jquery.event.drag.min.js b/js/lib/slickgrid/jquery.event.drag.min.js new file mode 100644 index 0000000000..2cb7fee0e2 --- /dev/null +++ b/js/lib/slickgrid/jquery.event.drag.min.js @@ -0,0 +1,6 @@ +/*! + * jquery.event.drag - v 2.0.0 + * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com + * Open Source MIT License - http://threedubmedia.com/code/license + */ +;(function(f){f.fn.drag=function(b,a,d){var e=typeof b=="string"?b:"",k=f.isFunction(b)?b:f.isFunction(a)?a:null;if(e.indexOf("drag")!==0)e="drag"+e;d=(b==k?a:d)||{};return k?this.bind(e,d,k):this.trigger(e)};var i=f.event,h=i.special,c=h.drag={defaults:{which:1,distance:0,not:":input",handle:null,relative:false,drop:true,click:false},datakey:"dragdata",livekey:"livedrag",add:function(b){var a=f.data(this,c.datakey),d=b.data||{};a.related+=1;if(!a.live&&b.selector){a.live=true;i.add(this,"draginit."+ c.livekey,c.delegate)}f.each(c.defaults,function(e){if(d[e]!==undefined)a[e]=d[e]})},remove:function(){f.data(this,c.datakey).related-=1},setup:function(){if(!f.data(this,c.datakey)){var b=f.extend({related:0},c.defaults);f.data(this,c.datakey,b);i.add(this,"mousedown",c.init,b);this.attachEvent&&this.attachEvent("ondragstart",c.dontstart)}},teardown:function(){if(!f.data(this,c.datakey).related){f.removeData(this,c.datakey);i.remove(this,"mousedown",c.init);i.remove(this,"draginit",c.delegate);c.textselect(true); this.detachEvent&&this.detachEvent("ondragstart",c.dontstart)}},init:function(b){var a=b.data,d;if(!(a.which>0&&b.which!=a.which))if(!f(b.target).is(a.not))if(!(a.handle&&!f(b.target).closest(a.handle,b.currentTarget).length)){a.propagates=1;a.interactions=[c.interaction(this,a)];a.target=b.target;a.pageX=b.pageX;a.pageY=b.pageY;a.dragging=null;d=c.hijack(b,"draginit",a);if(a.propagates){if((d=c.flatten(d))&&d.length){a.interactions=[];f.each(d,function(){a.interactions.push(c.interaction(this,a))})}a.propagates= a.interactions.length;a.drop!==false&&h.drop&&h.drop.handler(b,a);c.textselect(false);i.add(document,"mousemove mouseup",c.handler,a);return false}}},interaction:function(b,a){return{drag:b,callback:new c.callback,droppable:[],offset:f(b)[a.relative?"position":"offset"]()||{top:0,left:0}}},handler:function(b){var a=b.data;switch(b.type){case !a.dragging&&"mousemove":if(Math.pow(b.pageX-a.pageX,2)+Math.pow(b.pageY-a.pageY,2)This is pretty much identical to how W3C and jQuery implement events.

+ * @class EventData + * @constructor + */ + function EventData() { + var isPropagationStopped = false; + var isImmediatePropagationStopped = false; + + /*** + * Stops event from propagating up the DOM tree. + * @method stopPropagation + */ + this.stopPropagation = function () { + isPropagationStopped = true; + }; + + /*** + * Returns whether stopPropagation was called on this event object. + * @method isPropagationStopped + * @return {Boolean} + */ + this.isPropagationStopped = function () { + return isPropagationStopped; + }; + + /*** + * Prevents the rest of the handlers from being executed. + * @method stopImmediatePropagation + */ + this.stopImmediatePropagation = function () { + isImmediatePropagationStopped = true; + }; + + /*** + * Returns whether stopImmediatePropagation was called on this event object.\ + * @method isImmediatePropagationStopped + * @return {Boolean} + */ + this.isImmediatePropagationStopped = function () { + return isImmediatePropagationStopped; + } + } + + /*** + * A simple publisher-subscriber implementation. + * @class Event + * @constructor + */ + function Event() { + var handlers = []; + + /*** + * Adds an event handler to be called when the event is fired. + *

Event handler will receive two arguments - an EventData and the data + * object the event was fired with.

+ * @method subscribe + * @param fn {Function} Event handler. + */ + this.subscribe = function (fn) { + handlers.push(fn); + }; + + /*** + * Removes an event handler added with subscribe(fn). + * @method unsubscribe + * @param fn {Function} Event handler to be removed. + */ + this.unsubscribe = function (fn) { + for (var i = handlers.length - 1; i >= 0; i--) { + if (handlers[i] === fn) { + handlers.splice(i, 1); + } + } + }; + + /*** + * Fires an event notifying all subscribers. + * @method notify + * @param args {Object} Additional data object to be passed to all handlers. + * @param e {EventData} + * Optional. + * An EventData object to be passed to all handlers. + * For DOM events, an existing W3C/jQuery event object can be passed in. + * @param scope {Object} + * Optional. + * The scope ("this") within which the handler will be executed. + * If not specified, the scope will be set to the Event instance. + */ + this.notify = function (args, e, scope) { + e = e || new EventData(); + scope = scope || this; + + var returnValue; + for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { + returnValue = handlers[i].call(scope, e, args); + } + + return returnValue; + }; + } + + function EventHandler() { + var handlers = []; + + this.subscribe = function (event, handler) { + handlers.push({ + event: event, + handler: handler + }); + event.subscribe(handler); + }; + + this.unsubscribe = function (event, handler) { + var i = handlers.length; + while (i--) { + if (handlers[i].event === event && + handlers[i].handler === handler) { + handlers.splice(i, 1); + event.unsubscribe(handler); + return; + } + } + }; + + this.unsubscribeAll = function () { + var i = handlers.length; + while (i--) { + handlers[i].event.unsubscribe(handlers[i].handler); + } + handlers = []; + } + } + + /*** + * A structure containing a range of cells. + * @class Range + * @constructor + * @param fromRow {Integer} Starting row. + * @param fromCell {Integer} Starting cell. + * @param toRow {Integer} Optional. Ending row. Defaults to fromRow. + * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell. + */ + function Range(fromRow, fromCell, toRow, toCell) { + if (toRow === undefined && toCell === undefined) { + toRow = fromRow; + toCell = fromCell; + } + + /*** + * @property fromRow + * @type {Integer} + */ + this.fromRow = Math.min(fromRow, toRow); + + /*** + * @property fromCell + * @type {Integer} + */ + this.fromCell = Math.min(fromCell, toCell); + + /*** + * @property toRow + * @type {Integer} + */ + this.toRow = Math.max(fromRow, toRow); + + /*** + * @property toCell + * @type {Integer} + */ + this.toCell = Math.max(fromCell, toCell); + + /*** + * Returns whether a range represents a single row. + * @method isSingleRow + * @return {Boolean} + */ + this.isSingleRow = function () { + return this.fromRow == this.toRow; + }; + + /*** + * Returns whether a range represents a single cell. + * @method isSingleCell + * @return {Boolean} + */ + this.isSingleCell = function () { + return this.fromRow == this.toRow && this.fromCell == this.toCell; + }; + + /*** + * Returns whether a range contains a given cell. + * @method contains + * @param row {Integer} + * @param cell {Integer} + * @return {Boolean} + */ + this.contains = function (row, cell) { + return row >= this.fromRow && row <= this.toRow && + cell >= this.fromCell && cell <= this.toCell; + }; + + /*** + * Returns a readable representation of a range. + * @method toString + * @return {String} + */ + this.toString = function () { + if (this.isSingleCell()) { + return "(" + this.fromRow + ":" + this.fromCell + ")"; + } + else { + return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; + } + } + } + + + /*** + * A base class that all special / non-data rows (like Group and GroupTotals) derive from. + * @class NonDataItem + * @constructor + */ + function NonDataItem() { + this.__nonDataRow = true; + } + + + /*** + * Information about a group of rows. + * @class Group + * @extends Slick.NonDataItem + * @constructor + */ + function Group() { + this.__group = true; + this.__updated = false; + + /*** + * Number of rows in the group. + * @property count + * @type {Integer} + */ + this.count = 0; + + /*** + * Grouping value. + * @property value + * @type {Object} + */ + this.value = null; + + /*** + * Formatted display value of the group. + * @property title + * @type {String} + */ + this.title = null; + + /*** + * Whether a group is collapsed. + * @property collapsed + * @type {Boolean} + */ + this.collapsed = false; + + /*** + * GroupTotals, if any. + * @property totals + * @type {GroupTotals} + */ + this.totals = null; + } + + Group.prototype = new NonDataItem(); + + /*** + * Compares two Group instances. + * @method equals + * @return {Boolean} + * @param group {Group} Group instance to compare to. + */ + Group.prototype.equals = function (group) { + return this.value === group.value && + this.count === group.count && + this.collapsed === group.collapsed; + }; + + /*** + * Information about group totals. + * An instance of GroupTotals will be created for each totals row and passed to the aggregators + * so that they can store arbitrary data in it. That data can later be accessed by group totals + * formatters during the display. + * @class GroupTotals + * @extends Slick.NonDataItem + * @constructor + */ + function GroupTotals() { + this.__groupTotals = true; + + /*** + * Parent Group. + * @param group + * @type {Group} + */ + this.group = null; + } + + GroupTotals.prototype = new NonDataItem(); + + /*** + * A locking helper to track the active edit controller and ensure that only a single controller + * can be active at a time. This prevents a whole class of state and validation synchronization + * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress + * and attempt a commit or cancel before proceeding. + * @class EditorLock + * @constructor + */ + function EditorLock() { + var activeEditController = null; + + /*** + * Returns true if a specified edit controller is active (has the edit lock). + * If the parameter is not specified, returns true if any edit controller is active. + * @method isActive + * @param editController {EditController} + * @return {Boolean} + */ + this.isActive = function (editController) { + return (editController ? activeEditController === editController : activeEditController !== null); + }; + + /*** + * Sets the specified edit controller as the active edit controller (acquire edit lock). + * If another edit controller is already active, and exception will be thrown. + * @method activate + * @param editController {EditController} edit controller acquiring the lock + */ + this.activate = function (editController) { + if (editController === activeEditController) { // already activated? + return; + } + if (activeEditController !== null) { + throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; + } + if (!editController.commitCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; + } + if (!editController.cancelCurrentEdit) { + throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; + } + activeEditController = editController; + }; + + /*** + * Unsets the specified edit controller as the active edit controller (release edit lock). + * If the specified edit controller is not the active one, an exception will be thrown. + * @method deactivate + * @param editController {EditController} edit controller releasing the lock + */ + this.deactivate = function (editController) { + if (activeEditController !== editController) { + throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; + } + activeEditController = null; + }; + + /*** + * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit + * controller and returns whether the commit attempt was successful (commit may fail due to validation + * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded + * and false otherwise. If no edit controller is active, returns true. + * @method commitCurrentEdit + * @return {Boolean} + */ + this.commitCurrentEdit = function () { + return (activeEditController ? activeEditController.commitCurrentEdit() : true); + }; + + /*** + * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit + * controller and returns whether the edit was successfully cancelled. If no edit controller is + * active, returns true. + * @method cancelCurrentEdit + * @return {Boolean} + */ + this.cancelCurrentEdit = function cancelCurrentEdit() { + return (activeEditController ? activeEditController.cancelCurrentEdit() : true); + }; + } +})(jQuery); + + diff --git a/js/lib/slickgrid/slick.grid.css b/js/lib/slickgrid/slick.grid.css new file mode 100644 index 0000000000..e5ef44cd57 --- /dev/null +++ b/js/lib/slickgrid/slick.grid.css @@ -0,0 +1,158 @@ +/* +IMPORTANT: +In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. +No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS +classes should alter those! +*/ + +.slick-header.ui-state-default, .slick-headerrow.ui-state-default { + width: 100%; + overflow: hidden; + border-left: 0px; +} + +.slick-header-columns, .slick-headerrow-columns { + width: 999999px; + position: relative; + white-space: nowrap; + cursor: default; + overflow: hidden; +} + +.slick-header-column.ui-state-default { + position: relative; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + height: 16px; + line-height: 16px; + margin: 0; + padding: 4px; + border-right: 1px solid silver; + border-left: 0px; + border-top: 0px; + border-bottom: 0px; + float: left; +} + +.slick-headerrow-column.ui-state-default { + padding: 4px; +} + +.slick-header-column-sorted { + font-style: italic; +} + +.slick-sort-indicator { + display: inline-block; + width: 8px; + height: 5px; + margin-left: 4px; +} + +.slick-sort-indicator-desc { + background: url(images/sort-desc.gif); +} + +.slick-sort-indicator-asc { + background: url(images/sort-asc.gif); +} + +.slick-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + cursor: col-resize; + width: 4px; + right: 0px; + top: 0; + height: 100%; +} + +.slick-sortable-placeholder { + background: silver; +} + +.grid-canvas { + position: relative; + outline: 0; +} + +.slick-row.ui-widget-content, .slick-row.ui-state-active { + position: absolute; + border: 0px; + width: 100%; +} + +.slick-cell, .slick-headerrow-column { + position: absolute; + + border: 1px solid transparent; + border-right: 1px dotted silver; + border-bottom-color: silver; + + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: middle; + z-index: 1; + padding: 1px 2px 2px 1px; + margin: 0; + + white-space: nowrap; + + cursor: default; +} + +.slick-group { +} + +.slick-group-toggle { + display: inline-block; +} + +.slick-cell.highlighted { + background: lightskyblue; + background: rgba(0, 0, 255, 0.2); + -webkit-transition: all 0.5s; + -moz-transition: all 0.5s; + transition: all 0.5s; +} + +.slick-cell.flashing { + border: 1px solid red !important; +} + +.slick-cell.editable { + z-index: 11; + overflow: visible; + background: white; + border-color: black; + border-style: solid; +} + +.slick-cell:focus { + outline: none; +} + +.slick-reorder-proxy { + display: inline-block; + background: blue; + opacity: 0.15; + filter: alpha(opacity = 15); + cursor: move; +} + +.slick-reorder-guide { + display: inline-block; + height: 2px; + background: blue; + opacity: 0.7; + filter: alpha(opacity = 70); +} + +.slick-selection { + z-index: 10; + position: absolute; + border: 2px dashed black; +} \ No newline at end of file diff --git a/js/lib/slickgrid/slick.grid.js b/js/lib/slickgrid/slick.grid.js new file mode 100644 index 0000000000..c9a3b0e811 --- /dev/null +++ b/js/lib/slickgrid/slick.grid.js @@ -0,0 +1,2799 @@ +/** + * @license + * (c) 2009-2012 Michael Leibman + * michael{dot}leibman{at}gmail{dot}com + * http://github.com/mleibman/slickgrid + * + * Distributed under MIT license. + * All rights reserved. + * + * SlickGrid v2.0 + * + * NOTES: + * Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods. + * This increases the speed dramatically, but can only be done safely because there are no event handlers + * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy() + * and do proper cleanup. + */ + +// make sure required JavaScript modules are loaded +if (typeof jQuery === "undefined") { + throw "SlickGrid requires jquery module to be loaded"; +} +if (!jQuery.fn.drag) { + throw "SlickGrid requires jquery.event.drag module to be loaded"; +} +if (typeof Slick === "undefined") { + throw "slick.core.js not loaded"; +} + + +(function ($) { + // Slick.Grid + $.extend(true, window, { + Slick: { + Grid: SlickGrid + } + }); + + // shared across all grids on the page + var scrollbarDimensions; + var maxSupportedCssHeight; // browser's breaking point + + ////////////////////////////////////////////////////////////////////////////////////////////// + // SlickGrid class implementation (available as Slick.Grid) + + /** + * Creates a new instance of the grid. + * @class SlickGrid + * @constructor + * @param {Node} container Container node to create the grid in. + * @param {Array,Object} data An array of objects for databinding. + * @param {Array} columns An array of column definitions. + * @param {Object} options Grid options. + **/ + function SlickGrid(container, data, columns, options) { + // settings + var defaults = { + explicitInitialization: false, + rowHeight: 25, + defaultColumnWidth: 80, + enableAddRow: false, + leaveSpaceForNewRows: false, + editable: false, + autoEdit: true, + enableCellNavigation: true, + enableColumnReorder: true, + asyncEditorLoading: false, + asyncEditorLoadDelay: 100, + forceFitColumns: false, + enableAsyncPostRender: false, + asyncPostRenderDelay: 60, + autoHeight: false, + editorLock: Slick.GlobalEditorLock, + showHeaderRow: false, + headerRowHeight: 25, + showTopPanel: false, + topPanelHeight: 25, + formatterFactory: null, + editorFactory: null, + cellFlashingCssClass: "flashing", + selectedCellCssClass: "selected", + multiSelect: true, + enableTextSelectionOnCells: false, + dataItemColumnValueExtractor: null, + fullWidthRows: false, + multiColumnSort: false, + defaultFormatter: defaultFormatter + }; + + var columnDefaults = { + name: "", + resizable: true, + sortable: false, + minWidth: 30, + rerenderOnResize: false, + headerCssClass: null + }; + + // scroller + var th; // virtual height + var h; // real scrollable height + var ph; // page height + var n; // number of pages + var cj; // "jumpiness" coefficient + + var page = 0; // current page + var offset = 0; // current page offset + var scrollDir = 1; + + // private + var initialized = false; + var $container; + var uid = "slickgrid_" + Math.round(1000000 * Math.random()); + var self = this; + var $headerScroller; + var $headers; + var $headerRow, $headerRowScroller; + var $topPanelScroller; + var $topPanel; + var $viewport; + var $canvas; + var $style; + var stylesheet, columnCssRulesL = [], columnCssRulesR = []; + var viewportH, viewportW; + var canvasWidth; + var viewportHasHScroll, viewportHasVScroll; + var headerColumnWidthDiff = 0, headerColumnHeightDiff = 0, // border+padding + cellWidthDiff = 0, cellHeightDiff = 0; + var absoluteColumnMinWidth; + var numberOfRows = 0; + + var activePosX; + var activeRow, activeCell; + var activeCellNode = null; + var currentEditor = null; + var serializedEditorValue; + var editController; + + var rowsCache = {}; + var renderedRows = 0; + var numVisibleRows; + var prevScrollTop = 0; + var scrollTop = 0; + var lastRenderedScrollTop = 0; + var prevScrollLeft = 0; + var avgRowRenderTime = 10; + + var selectionModel; + var selectedRows = []; + + var plugins = []; + var cellCssClasses = {}; + + var columnsById = {}; + var sortColumns = []; + + + // async call handles + var h_editorLoader = null; + var h_render = null; + var h_postrender = null; + var postProcessedRows = {}; + var postProcessToRow = null; + var postProcessFromRow = null; + + // perf counters + var counter_rows_rendered = 0; + var counter_rows_removed = 0; + + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Initialization + + function init() { + $container = $(container); + if ($container.length < 1) { + throw new Error("SlickGrid requires a valid container, " + container + " does not exist in the DOM."); + } + + // calculate these only once and share between grid instances + maxSupportedCssHeight = maxSupportedCssHeight || getMaxSupportedCssHeight(); + scrollbarDimensions = scrollbarDimensions || measureScrollbar(); + + options = $.extend({}, defaults, options); + columnDefaults.width = options.defaultColumnWidth; + + // validate loaded JavaScript modules against requested options + if (options.enableColumnReorder && !$.fn.sortable) { + throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded"); + } + + editController = { + "commitCurrentEdit": commitCurrentEdit, + "cancelCurrentEdit": cancelCurrentEdit + }; + + $container + .empty() + .attr("tabIndex", 0) + .attr("hideFocus", true) + .css("overflow", "hidden") + .css("outline", 0) + .addClass(uid) + .addClass("ui-widget"); + + // set up a positioning container if needed + if (!/relative|absolute|fixed/.test($container.css("position"))) { + $container.css("position", "relative"); + } + + $headerScroller = $("

").appendTo($container); + $headers = $("
").appendTo($headerScroller); + + $headerRowScroller = $("
").appendTo($container); + $headerRow = $("
").appendTo($headerRowScroller); + + $topPanelScroller = $("
").appendTo($container); + $topPanel = $("
").appendTo($topPanelScroller); + + if (!options.showTopPanel) { + $topPanelScroller.hide(); + } + + if (!options.showHeaderRow) { + $headerRowScroller.hide(); + } + + $viewport = $("
").appendTo($container); + $viewport.css("overflow-y", options.autoHeight ? "hidden" : "auto"); + + $canvas = $("
").appendTo($viewport); + + if (!options.explicitInitialization) { + finishInitialization(); + } + } + + function finishInitialization() { + if (!initialized) { + initialized = true; + + viewportW = parseFloat($.css($container[0], "width", true)); + + // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?) + // calculate the diff so we can set consistent sizes + measureCellPaddingAndBorder(); + + // for usability reasons, all text selection in SlickGrid is disabled + // with the exception of input and textarea elements (selection must + // be enabled there so that editors work as expected); note that + // selection in grid cells (grid body) is already unavailable in + // all browsers except IE + disableSelection($headers); // disable all text selection in header (including input and textarea) + + if (!options.enableTextSelectionOnCells) { + // disable text selection in grid cells except in input and textarea elements + // (this is IE-specific, because selectstart event will only fire in IE) + $viewport.bind("selectstart.ui", function (event) { + return $(event.target).is("input,textarea"); + }); + } + + createColumnHeaders(); + setupColumnSort(); + createCssRules(); + resizeCanvas(); + bindAncestorScrollEvents(); + + $container + .bind("resize.slickgrid", resizeCanvas); + $viewport + .bind("scroll.slickgrid", handleScroll); + $headerScroller + .bind("contextmenu.slickgrid", handleHeaderContextMenu) + .bind("click.slickgrid", handleHeaderClick); + $canvas + .bind("keydown.slickgrid", handleKeyDown) + .bind("click.slickgrid", handleClick) + .bind("dblclick.slickgrid", handleDblClick) + .bind("contextmenu.slickgrid", handleContextMenu) + .bind("draginit", handleDragInit) + .bind("dragstart", handleDragStart) + .bind("drag", handleDrag) + .bind("dragend", handleDragEnd) + .delegate(".slick-cell", "mouseenter", handleMouseEnter) + .delegate(".slick-cell", "mouseleave", handleMouseLeave); + } + } + + function registerPlugin(plugin) { + plugins.unshift(plugin); + plugin.init(self); + } + + function unregisterPlugin(plugin) { + for (var i = plugins.length; i >= 0; i--) { + if (plugins[i] === plugin) { + if (plugins[i].destroy) { + plugins[i].destroy(); + } + plugins.splice(i, 1); + break; + } + } + } + + function setSelectionModel(model) { + if (selectionModel) { + selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged); + if (selectionModel.destroy) { + selectionModel.destroy(); + } + } + + selectionModel = model; + if (selectionModel) { + selectionModel.init(self); + selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged); + } + } + + function getSelectionModel() { + return selectionModel; + } + + function getCanvasNode() { + return $canvas[0]; + } + + function measureScrollbar() { + var $c = $("
").appendTo("body"); + var dim = { + width: $c.width() - $c[0].clientWidth, + height: $c.height() - $c[0].clientHeight + }; + $c.remove(); + return dim; + } + + function getCanvasWidth() { + var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW; + var rowWidth = 0; + var i = columns.length; + while (i--) { + rowWidth += (columns[i].width || columnDefaults.width); + } + return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth; + } + + function updateCanvasWidth(forceColumnWidthsUpdate) { + var oldCanvasWidth = canvasWidth; + canvasWidth = getCanvasWidth(); + + if (canvasWidth != oldCanvasWidth) { + $canvas.width(canvasWidth); + $headerRow.width(canvasWidth); + viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width); + } + + if (canvasWidth != oldCanvasWidth || forceColumnWidthsUpdate) { + applyColumnWidths(); + } + } + + function disableSelection($target) { + if ($target && $target.jquery) { + $target + .attr("unselectable", "on") + .css("MozUserSelect", "none") + .bind("selectstart.ui", function () { + return false; + }); // from jquery:ui.core.js 1.7.2 + } + } + + function getMaxSupportedCssHeight() { + var increment = 1000000; + var supportedHeight = increment; + // FF reports the height back but still renders blank after ~6M px + var testUpTo = ($.browser.mozilla) ? 5000000 : 1000000000; + var div = $("
").appendTo(document.body); + + while (supportedHeight <= testUpTo) { + div.css("height", supportedHeight + increment); + if (div.height() !== supportedHeight + increment) { + break; + } else { + supportedHeight += increment; + } + } + + div.remove(); + return supportedHeight; + } + + // TODO: this is static. need to handle page mutation. + function bindAncestorScrollEvents() { + var elem = $canvas[0]; + while ((elem = elem.parentNode) != document.body && elem != null) { + // bind to scroll containers only + if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight) { + $(elem).bind("scroll.slickgrid", handleActiveCellPositionChange); + } + } + } + + function unbindAncestorScrollEvents() { + $canvas.parents().unbind("scroll.slickgrid"); + } + + function updateColumnHeader(columnId, title, toolTip) { + if (!initialized) { return; } + var idx = getColumnIndex(columnId); + var $header = $headers.children().eq(idx); + if ($header) { + columns[idx].name = title; + columns[idx].toolTip = toolTip; + $header + .attr("title", toolTip || title || "") + .children().eq(0).html(title); + } + } + + function getHeaderRow() { + return $headerRow[0]; + } + + function getHeaderRowColumn(columnId) { + var idx = getColumnIndex(columnId); + var $header = $headerRow.children().eq(idx); + return $header && $header[0]; + } + + function createColumnHeaders() { + function hoverBegin() { + $(this).addClass("ui-state-hover"); + } + + function hoverEnd() { + $(this).removeClass("ui-state-hover"); + } + + $headers.empty(); + $headerRow.empty(); + columnsById = {}; + + for (var i = 0; i < columns.length; i++) { + var m = columns[i] = $.extend({}, columnDefaults, columns[i]); + columnsById[m.id] = i; + + var header = $("
") + .html("" + m.name + "") + .width(m.width - headerColumnWidthDiff) + .attr("title", m.toolTip || m.name || "") + .data("fieldId", m.id) + .addClass(m.headerCssClass || "") + .appendTo($headers); + + if (options.enableColumnReorder || m.sortable) { + header.hover(hoverBegin, hoverEnd); + } + + if (m.sortable) { + header.append(""); + } + + if (options.showHeaderRow) { + $("
") + .appendTo($headerRow); + } + } + + if (options.showHeaderRow) { + // add a spacer to let the container scroll beyond the header row columns width + $("
") + .appendTo($headerRowScroller); + } + + setSortColumns(sortColumns); + setupColumnResize(); + if (options.enableColumnReorder) { + setupColumnReorder(); + } + } + + function setupColumnSort() { + $headers.click(function (e) { + // temporary workaround for a bug in jQuery 1.7.1 (http://bugs.jquery.com/ticket/11328) + e.metaKey = e.metaKey || e.ctrlKey; + + if ($(e.target).hasClass("slick-resizable-handle")) { + return; + } + + var $col = $(e.target).closest(".slick-header-column"); + if (!$col.length) { + return; + } + + var column = columns[getColumnIndex($col.data("fieldId"))]; + if (column.sortable) { + if (!getEditorLock().commitCurrentEdit()) { + return; + } + + var sortOpts = null; + var i = 0; + for (; i < sortColumns.length; i++) { + if (sortColumns[i].columnId == column.id) { + sortOpts = sortColumns[i]; + sortOpts.sortAsc = !sortOpts.sortAsc; + break; + } + } + + if (e.metaKey && options.multiColumnSort) { + if (sortOpts) { + sortColumns.splice(i, 1); + } + } + else { + if ((!e.shiftKey && !e.metaKey) || !options.multiColumnSort) { + sortColumns = []; + } + + if (!sortOpts) { + sortOpts = { columnId: column.id, sortAsc: true }; + sortColumns.push(sortOpts); + } else if (sortColumns.length == 0) { + sortColumns.push(sortOpts); + } + } + + setSortColumns(sortColumns); + + if (!options.multiColumnSort) { + trigger(self.onSort, { + multiColumnSort: false, + sortCol: column, + sortAsc: sortOpts.sortAsc}, e); + } else { + trigger(self.onSort, { + multiColumnSort: true, + sortCols: $.map(sortColumns, function(col) { + return {sortCol: columns[getColumnIndex(col.columnId)], sortAsc: col.sortAsc }; + })}, e); + } + } + }); + } + + function setupColumnReorder() { + $headers.sortable({ + containment: "parent", + axis: "x", + cursor: "default", + tolerance: "intersection", + helper: "clone", + placeholder: "slick-sortable-placeholder ui-state-default slick-header-column", + forcePlaceholderSize: true, + start: function (e, ui) { + $(ui.helper).addClass("slick-header-column-active"); + }, + beforeStop: function (e, ui) { + $(ui.helper).removeClass("slick-header-column-active"); + }, + stop: function (e) { + if (!getEditorLock().commitCurrentEdit()) { + $(this).sortable("cancel"); + return; + } + + var reorderedIds = $headers.sortable("toArray"); + var reorderedColumns = []; + for (var i = 0; i < reorderedIds.length; i++) { + reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid, ""))]); + } + setColumns(reorderedColumns); + + trigger(self.onColumnsReordered, {}); + e.stopPropagation(); + setupColumnResize(); + } + }); + } + + function setupColumnResize() { + var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable; + columnElements = $headers.children(); + columnElements.find(".slick-resizable-handle").remove(); + columnElements.each(function (i, e) { + if (columns[i].resizable) { + if (firstResizable === undefined) { + firstResizable = i; + } + lastResizable = i; + } + }); + if (firstResizable === undefined) { + return; + } + columnElements.each(function (i, e) { + if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) { + return; + } + $col = $(e); + $("
") + .appendTo(e) + .bind("dragstart", function (e, dd) { + if (!getEditorLock().commitCurrentEdit()) { + return false; + } + pageX = e.pageX; + $(this).parent().addClass("slick-header-column-active"); + var shrinkLeewayOnRight = null, stretchLeewayOnRight = null; + // lock each column's width option to current width + columnElements.each(function (i, e) { + columns[i].previousWidth = $(e).outerWidth(); + }); + if (options.forceFitColumns) { + shrinkLeewayOnRight = 0; + stretchLeewayOnRight = 0; + // colums on right affect maxPageX/minPageX + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnRight !== null) { + if (c.maxWidth) { + stretchLeewayOnRight += c.maxWidth - c.previousWidth; + } else { + stretchLeewayOnRight = null; + } + } + shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + } + } + var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0; + for (j = 0; j <= i; j++) { + // columns on left only affect minPageX + c = columns[j]; + if (c.resizable) { + if (stretchLeewayOnLeft !== null) { + if (c.maxWidth) { + stretchLeewayOnLeft += c.maxWidth - c.previousWidth; + } else { + stretchLeewayOnLeft = null; + } + } + shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth); + } + } + if (shrinkLeewayOnRight === null) { + shrinkLeewayOnRight = 100000; + } + if (shrinkLeewayOnLeft === null) { + shrinkLeewayOnLeft = 100000; + } + if (stretchLeewayOnRight === null) { + stretchLeewayOnRight = 100000; + } + if (stretchLeewayOnLeft === null) { + stretchLeewayOnLeft = 100000; + } + maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft); + minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight); + }) + .bind("drag", function (e, dd) { + var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x; + if (d < 0) { // shrink column + x = d; + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + + if (options.forceFitColumns) { + x = -d; + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + } + } else { // stretch column + x = d; + for (j = i; j >= 0; j--) { + c = columns[j]; + if (c.resizable) { + if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) { + x -= c.maxWidth - c.previousWidth; + c.width = c.maxWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + + if (options.forceFitColumns) { + x = -d; + for (j = i + 1; j < columnElements.length; j++) { + c = columns[j]; + if (c.resizable) { + actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth); + if (x && c.previousWidth + x < actualMinWidth) { + x += c.previousWidth - actualMinWidth; + c.width = actualMinWidth; + } else { + c.width = c.previousWidth + x; + x = 0; + } + } + } + } + } + applyColumnHeaderWidths(); + if (options.syncColumnCellResize) { + applyColumnWidths(); + } + }) + .bind("dragend", function (e, dd) { + var newWidth; + $(this).parent().removeClass("slick-header-column-active"); + for (j = 0; j < columnElements.length; j++) { + c = columns[j]; + newWidth = $(columnElements[j]).outerWidth(); + + if (c.previousWidth !== newWidth && c.rerenderOnResize) { + invalidateAllRows(); + } + } + updateCanvasWidth(true); + render(); + trigger(self.onColumnsResized, {}); + }); + }); + } + + function getVBoxDelta($el) { + var p = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + var delta = 0; + $.each(p, function (n, val) { + delta += parseFloat($el.css(val)) || 0; + }); + return delta; + } + + function measureCellPaddingAndBorder() { + var el; + var h = ["borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight"]; + var v = ["borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom"]; + + el = $("").appendTo($headers); + headerColumnWidthDiff = headerColumnHeightDiff = 0; + $.each(h, function (n, val) { + headerColumnWidthDiff += parseFloat(el.css(val)) || 0; + }); + $.each(v, function (n, val) { + headerColumnHeightDiff += parseFloat(el.css(val)) || 0; + }); + el.remove(); + + var r = $("
").appendTo($canvas); + el = $("").appendTo(r); + cellWidthDiff = cellHeightDiff = 0; + $.each(h, function (n, val) { + cellWidthDiff += parseFloat(el.css(val)) || 0; + }); + $.each(v, function (n, val) { + cellHeightDiff += parseFloat(el.css(val)) || 0; + }); + r.remove(); + + absoluteColumnMinWidth = Math.max(headerColumnWidthDiff, cellWidthDiff); + } + + function createCssRules() { + $style = $("