Sharable Link to current View
diff --git a/app/js/app.js b/app/js/app.js
index f77375a0..da882392 100755
--- a/app/js/app.js
+++ b/app/js/app.js
@@ -76,10 +76,36 @@ var ExplorerApp = Backbone.View.extend({
this.dataExplorer = null;
var $el = $('
');
$el.appendTo(this.explorerDiv);
+ var views = [
+ {
+ id: 'grid',
+ label: 'Grid',
+ view: new recline.View.SlickGrid({
+ model: dataset
+ })
+ },
+
+ {
+ id: 'graph',
+ label: 'Graph',
+ view: new recline.View.Graph({
+ model: dataset
+ })
+ },
+ {
+ id: 'map',
+ label: 'Map',
+ view: new recline.View.Map({
+ model: dataset
+ })
+ },
+ ];
+
this.dataExplorer = new recline.View.DataExplorer({
model: dataset,
el: $el,
- state: state
+ state: state,
+ views: views
});
this._setupPermaLink(this.dataExplorer);
this._setupEmbed(this.dataExplorer);
diff --git a/app/style/demo.css b/app/style/demo.css
index 60b761ec..ad690766 100644
--- a/app/style/demo.css
+++ b/app/style/demo.css
@@ -11,6 +11,10 @@ body {
height: 550px;
}
+.recline-slickgrid {
+ height: 550px;
+}
+
.recline-timeline .vmm-timeline {
height: 550px;
}
diff --git a/css/slickgrid.css b/css/slickgrid.css
new file mode 100644
index 00000000..204999df
--- /dev/null
+++ b/css/slickgrid.css
@@ -0,0 +1,161 @@
+/*
+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!
+*/
+
+.recline-slickgrid-container {
+ border-left: 1px solid #ccc;
+}
+
+.slick-header-columns .slick-header-column {
+ background-color: #e6e6e6;
+ background-repeat: no-repeat;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+ background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
+ background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+ background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+ background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ color: #333;
+ font-weight: bold;
+ border-right: 1px solid #ccc;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #bbb;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+
+.slick-header-column:hover, .slick-header-column-active {
+}
+
+.slick-headerrow {
+ background: #fafafa;
+}
+
+.slick-headerrow-column {
+ background: #fafafa;
+ border-bottom: 0;
+ height: 100%;
+}
+
+.slick-row.ui-state-active {
+ background: #F5F7D7;
+}
+
+.slick-row {
+ position: absolute;
+ background: white;
+ border: 0px;
+ line-height: 20px;
+}
+
+.slick-row.selected {
+ z-index: 10;
+ background: #DFE8F6;
+}
+
+.slick-cell {
+ padding-left: 4px;
+ padding-right: 4px;
+}
+
+.slick-group {
+ border-bottom: 2px solid silver;
+}
+
+.slick-group-toggle {
+ width: 9px;
+ height: 9px;
+ margin-right: 5px;
+}
+
+.slick-group-toggle.expanded {
+ background: url(../images/collapse.gif) no-repeat center center;
+}
+
+.slick-group-toggle.collapsed {
+ background: url(../images/expand.gif) no-repeat center center;
+}
+
+.slick-group-totals {
+ color: gray;
+ background: white;
+}
+
+.slick-cell.selected {
+ background-color: beige;
+}
+
+.slick-cell.active {
+ border-color: gray;
+ border-style: solid;
+}
+
+.slick-sortable-placeholder {
+ background: silver !important;
+}
+
+.slick-row[row$="1"], .slick-row[row$="3"], .slick-row[row$="5"], .slick-row[row$="7"], .slick-row[row$="9"] {
+ background: #fafafa;
+}
+
+.slick-row.ui-state-active {
+ background: #F5F7D7;
+}
+
+.slick-row.loading {
+ opacity: 0.5;
+ filter: alpha(opacity = 50);
+}
+
+.slick-cell.invalid {
+ border-color: red;
+}
+
+.slick-contextmenu {
+ border-radius: 5px
+}
+
+.slick-contextmenu li {
+ clear: both;
+ height: 24px;
+ cursor: pointer;
+}
+
+.slick-contextmenu .divider {
+ cursor: default;
+}
+
+.slick-contextmenu > li:hover {
+ background-color: #0088cc;
+}
+
+.slick-contextmenu .divider:hover {
+ background-color: #E5E5E5;
+}
+
+.slick-contextmenu li:hover > label {
+ color: white;
+}
+
+.slick-contextmenu input {
+ float: left;
+ margin-left: 15px;
+ margin-top: 5px;
+}
+
+.slick-contextmenu label {
+ float: left;
+ margin-right: 15px;
+ margin-left: 5px;
+ margin-top: 3px;
+ color: #555;
+ cursor: pointer;
+}
+
diff --git a/src/view-slickgrid.js b/src/view-slickgrid.js
new file mode 100644
index 00000000..6635e256
--- /dev/null
+++ b/src/view-slickgrid.js
@@ -0,0 +1,297 @@
+/*jshint multistr:true */
+
+this.recline = this.recline || {};
+this.recline.View = this.recline.View || {};
+
+(function($, my) {
+// ## SlickGrid Dataset View
+//
+// Provides a tabular view on a Dataset, based on SlickGrid.
+//
+// https://github.com/mleibman/SlickGrid
+//
+// Initialize it with a `recline.Model.Dataset`.
+my.SlickGrid = Backbone.View.extend({
+ tagName: "div",
+ className: "recline-slickgrid",
+
+ initialize: function(modelEtc) {
+ var self = this;
+ this.el = $(this.el);
+ _.bindAll(this, 'render');
+ this.model.currentDocuments.bind('add', this.render);
+ this.model.currentDocuments.bind('reset', this.render);
+ this.model.currentDocuments.bind('remove', this.render);
+
+ var state = _.extend({
+ hiddenColumns: [],
+ columnsOrder: [],
+ columnsSort: {},
+ columnsWidth: [],
+ fitColumns: false
+ }, modelEtc.state
+ );
+ this.state = new recline.Model.ObjectState(state);
+
+ this.bind('view:show',function(){
+ // If the div is hidden, SlickGrid will calculate wrongly some
+ // sizes so we must render it explicitly when the view is visible
+ if (!self.rendered){
+ if (!self.grid){
+ self.render();
+ }
+ self.grid.init();
+ self.rendered = true;
+ }
+ self.visible = true;
+ });
+ this.bind('view:hide',function(){
+ self.visible = false;
+ });
+
+ },
+
+ events: {
+ },
+
+ render: function() {
+ var self = this;
+ this.el = $(this.el);
+
+ var options = {
+ enableCellNavigation: true,
+ enableColumnReorder: true,
+ explicitInitialization: true,
+ syncColumnCellResize: true,
+ forceFitColumns: this.state.get('fitColumns')
+ };
+
+ // We need all columns, even the hidden ones, to show on the column picker
+ var columns = [];
+ _.each(this.model.fields.toJSON(),function(field){
+ var column = {id:field['id'],
+ name:field['label'],
+ field:field['id'],
+ sortable: true,
+ minWidth: 80};
+
+ var widthInfo = _.find(self.state.get('columnsWidth'),function(c){return c.column == field.id});
+ if (widthInfo){
+ column['width'] = widthInfo.width;
+ }
+
+ columns.push(column);
+ });
+
+ // Restrict the visible columns
+ var visibleColumns = columns.filter(function(column) {
+ return _.indexOf(self.state.get('hiddenColumns'), column.id) == -1;
+ });
+
+ // Order them if there is ordering info on the state
+ if (this.state.get('columnsOrder')){
+ visibleColumns = visibleColumns.sort(function(a,b){
+ return _.indexOf(self.state.get('columnsOrder'),a.id) > _.indexOf(self.state.get('columnsOrder'),b.id);
+ });
+ columns = columns.sort(function(a,b){
+ return _.indexOf(self.state.get('columnsOrder'),a.id) > _.indexOf(self.state.get('columnsOrder'),b.id);
+ });
+ }
+
+ // Move hidden columns to the end, so they appear at the bottom of the
+ // column picker
+ var tempHiddenColumns = [];
+ for (var i = columns.length -1; i >= 0; i--){
+ if (_.indexOf(_.pluck(visibleColumns,'id'),columns[i].id) == -1){
+ tempHiddenColumns.push(columns.splice(i,1)[0]);
+ }
+ }
+ columns = columns.concat(tempHiddenColumns);
+
+
+ var data = this.model.currentDocuments.toJSON();
+
+ this.grid = new Slick.Grid(this.el, data, visibleColumns, options);
+
+ // Column sorting
+ var gridSorter = function(field, ascending, grid, data){
+
+ data.sort(function(a, b){
+ var result =
+ a[field] > b[field] ? 1 :
+ a[field] < b[field] ? -1 :
+ 0
+ ;
+ return ascending ? result : -result;
+ });
+
+ grid.setData(data);
+ grid.updateRowCount();
+ grid.render();
+ }
+
+ var sortInfo = this.state.get('columnsSort');
+ if (sortInfo){
+ var sortAsc = !(sortInfo['direction'] == 'desc');
+ gridSorter(sortInfo.column, sortAsc, self.grid, data);
+ this.grid.setSortColumn(sortInfo.column, sortAsc);
+ }
+
+ this.grid.onSort.subscribe(function(e, args){
+ gridSorter(args.sortCol.field,args.sortAsc,self.grid,data);
+ self.state.set({columnsSort:{
+ column:args.sortCol,
+ direction: (args.sortAsc) ? 'asc':'desc'
+ }});
+ });
+
+ this.grid.onColumnsReordered.subscribe(function(e, args){
+ self.state.set({columnsOrder: _.pluck(self.grid.getColumns(),'id')});
+ });
+
+ this.grid.onColumnsResized.subscribe(function(e, args){
+ var columns = args.grid.getColumns();
+ var defaultColumnWidth = args.grid.getOptions().defaultColumnWidth;
+ var columnsWidth = [];
+ _.each(columns,function(column){
+ if (column.width != defaultColumnWidth){
+ columnsWidth.push({column:column.id,width:column.width});
+ }
+ });
+ self.state.set({columnsWidth:columnsWidth});
+ });
+
+ var columnpicker = new Slick.Controls.ColumnPicker(columns, this.grid,
+ _.extend(options,{state:this.state}));
+
+ if (self.visible){
+ self.grid.init();
+ self.rendered = true;
+ } else {
+ // Defer rendering until the view is visible
+ self.rendered = false;
+ }
+
+ return this;
+ }
+});
+
+})(jQuery, recline.View);
+
+/*
+* Context menu for the column picker, adapted from
+* http://mleibman.github.com/SlickGrid/examples/example-grouping
+*
+*/
+(function ($) {
+ function SlickColumnPicker(columns, grid, options) {
+ var $menu;
+ var columnCheckboxes;
+
+ var defaults = {
+ fadeSpeed:250
+ };
+
+ function init() {
+ grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu);
+ options = $.extend({}, defaults, options);
+
+ $menu = $('').appendTo(document.body);
+
+ $menu.bind('mouseleave', function (e) {
+ $(this).fadeOut(options.fadeSpeed)
+ });
+ $menu.bind('click', updateColumn);
+
+ }
+
+ function handleHeaderContextMenu(e, args) {
+ e.preventDefault();
+ $menu.empty();
+ columnCheckboxes = [];
+
+ var $li, $input;
+ for (var i = 0; i < columns.length; i++) {
+ $li = $('
').appendTo($menu);
+ $input = $('
').data('column-id', columns[i].id).attr('id','slick-column-vis-'+columns[i].id);
+ columnCheckboxes.push($input);
+
+ if (grid.getColumnIndex(columns[i].id) != null) {
+ $input.attr('checked', 'checked');
+ }
+ $input.appendTo($li);
+ $('
')
+ .text(columns[i].name)
+ .attr('for','slick-column-vis-'+columns[i].id)
+ .appendTo($li);
+ }
+ $('
').addClass('divider').appendTo($menu);
+ $li = $('
').data('option', 'autoresize').appendTo($menu);
+ $input = $('
').data('option', 'autoresize').attr('id','slick-option-autoresize');
+ $input.appendTo($li);
+ $('
')
+ .text('Force fit columns')
+ .attr('for','slick-option-autoresize')
+ .appendTo($li);
+ if (grid.getOptions().forceFitColumns) {
+ $input.attr('checked', 'checked');
+ }
+
+ $menu.css('top', e.pageY - 10)
+ .css('left', e.pageX - 10)
+ .fadeIn(options.fadeSpeed);
+ }
+
+ function updateColumn(e) {
+ if ($(e.target).data('option') == 'autoresize') {
+ var checked;
+ if ($(e.target).is('li')){
+ var checkbox = $(e.target).find('input').first();
+ checked = !checkbox.is(':checked');
+ checkbox.attr('checked',checked);
+ } else {
+ checked = e.target.checked;
+ }
+
+ if (checked) {
+ grid.setOptions({forceFitColumns:true});
+ grid.autosizeColumns();
+ } else {
+ grid.setOptions({forceFitColumns:false});
+ }
+ options.state.set({fitColumns:checked});
+ return;
+ }
+
+ if (($(e.target).is('li') && !$(e.target).hasClass('divider')) ||
+ $(e.target).is('input')) {
+ if ($(e.target).is('li')){
+ var checkbox = $(e.target).find('input').first();
+ checkbox.attr('checked',!checkbox.is(':checked'));
+ }
+ var visibleColumns = [];
+ var hiddenColumnsIds = [];
+ $.each(columnCheckboxes, function (i, e) {
+ if ($(this).is(':checked')) {
+ visibleColumns.push(columns[i]);
+ } else {
+ hiddenColumnsIds.push(columns[i].id);
+ }
+ });
+
+
+ if (!visibleColumns.length) {
+ $(e.target).attr('checked', 'checked');
+ return;
+ }
+
+ grid.setColumns(visibleColumns);
+ options.state.set({hiddenColumns:hiddenColumnsIds});
+ }
+ }
+ init();
+ }
+
+ // Slick.Controls.ColumnPicker
+ $.extend(true, window, { Slick:{ Controls:{ ColumnPicker:SlickColumnPicker }}});
+})(jQuery);
diff --git a/test/index.html b/test/index.html
index b5dac4c5..826971da 100644
--- a/test/index.html
+++ b/test/index.html
@@ -15,6 +15,9 @@
+
+
+
@@ -39,12 +42,14 @@
+
+
diff --git a/test/view-slickgrid.test.js b/test/view-slickgrid.test.js
new file mode 100644
index 00000000..d43d2d1b
--- /dev/null
+++ b/test/view-slickgrid.test.js
@@ -0,0 +1,64 @@
+(function ($) {
+
+module("View - SlickGrid");
+
+test('basic', function () {
+ var dataset = Fixture.getDataset();
+ var view = new recline.View.SlickGrid({
+ model: dataset
+ });
+ $('.fixtures .test-datatable').append(view.el);
+ view.render();
+
+ // Render the grid manually
+ view.grid.init();
+
+ assertPresent('.slick-header-column[title="x"]');
+ equal($('.slick-header-column').length,dataset.fields.length);
+
+ equal(dataset.currentDocuments.length,view.grid.getDataLength());
+
+ view.remove();
+});
+
+
+test('state', function () {
+ var dataset = Fixture.getDataset();
+ var view = new recline.View.SlickGrid({
+ model: dataset,
+ state: {
+ hiddenColumns:['x','lat','label'],
+ columnsOrder:['lon','id','z','date', 'y', 'country'],
+ columnsSort:{column:'country',direction:'desc'},
+ columnsWidth:[
+ {column:'id',width: 250}
+ ]
+ }
+ });
+ $('.fixtures .test-datatable').append(view.el);
+ view.render();
+ view.grid.init();
+
+ var visibleColumns = _.filter(_.pluck(dataset.fields.toArray(),'id'),function(f){
+ return (_.indexOf(view.state.get('hiddenColumns'),f) == -1)
+ });
+
+ // Hidden columns
+ assertPresent('.slick-header-column[title="y"]');
+ assertNotPresent('.slick-header-column[title="x"]');
+ var headers = $('.slick-header-column');
+ equal(headers.length,visibleColumns.length);
+
+ // Column order
+ deepEqual(_.pluck(headers,'title'),view.state.get('columnsOrder'));
+
+ // Column sorting
+ equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).text(),'US');
+
+ // Column width
+ equal($('.slick-header-column[title="id"]').width(),250);
+
+ view.remove();
+});
+
+})(this.jQuery);
diff --git a/vendor/slickgrid/2.0.1/MIT-LICENSE.txt b/vendor/slickgrid/2.0.1/MIT-LICENSE.txt
new file mode 100644
index 00000000..60f65425
--- /dev/null
+++ b/vendor/slickgrid/2.0.1/MIT-LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/slickgrid/2.0.1/README.txt b/vendor/slickgrid/2.0.1/README.txt
new file mode 100644
index 00000000..90448433
--- /dev/null
+++ b/vendor/slickgrid/2.0.1/README.txt
@@ -0,0 +1,16 @@
+This compiled version of SlickGrid has been obtained with the Google Closure
+Compiler, using the following command:
+
+java -jar compiler.jar --js=slick.core.js --js=slick.grid.js --js_output_file=slick.grid.min.js
+
+There are two other files required for the SlickGrid view to work properly:
+
+ * jquery-ui-1.8.16.custom.min.js
+ * jquery.event.drag-2.0.min.js
+
+These are included in the Recline source, but have not been included in the
+built file to make easier to handle compatibility problems.
+
+Please check SlickGrid license in the included MIT-LICENSE.txt file.
+
+[1] https://developers.google.com/closure/compiler/
diff --git a/vendor/slickgrid/2.0.1/images/sort-asc.gif b/vendor/slickgrid/2.0.1/images/sort-asc.gif
new file mode 100644
index 00000000..67a2a4c6
Binary files /dev/null and b/vendor/slickgrid/2.0.1/images/sort-asc.gif differ
diff --git a/vendor/slickgrid/2.0.1/images/sort-desc.gif b/vendor/slickgrid/2.0.1/images/sort-desc.gif
new file mode 100644
index 00000000..34db47c3
Binary files /dev/null and b/vendor/slickgrid/2.0.1/images/sort-desc.gif differ
diff --git a/vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js b/vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js
new file mode 100644
index 00000000..7e3093a5
--- /dev/null
+++ b/vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js
@@ -0,0 +1,611 @@
+/*!
+ * jQuery UI 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
+this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
+"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
+"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
+outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
+"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
+a.element[0].parentNode)for(var e=0;e
0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Position 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
+this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
+this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
+_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
+false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
+10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
+!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
+a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
+10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
+(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
+"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
+10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
+!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>=
+i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f ').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
+top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
+this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
+nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d