Merge branch 'master' into gh-pages

This commit is contained in:
Rufus Pollock 2014-08-16 17:04:36 +01:00
commit 7f1653c4fb
53 changed files with 15523 additions and 5299 deletions

View File

@ -27,6 +27,8 @@ See CONTRIBUTING.md.
* [Adrià Mercader](http://amercader.net/)
* [Dominik Moritz](https://github.com/domoritz)
* [Friedrich Lindenberg](http://pudo.org/)
* [Alioune Dia](http://https://github.com/aliounedia)
* [kielni](https://github.com/kielni)
* And [many more](https://github.com/okfn/recline/graphs/contributors)
## Changelog
@ -36,7 +38,7 @@ See CONTRIBUTING.md.
[v0.6 milestone](https://github.com/okfn/recline/issues?milestone=5)
Possible breaking changes
* Support for row/add/delete/Reorder for recline slickGrid check `_includes/recline-deps.html` for slcikGrid plugins required #396
* Many backends moved to their own repositories #314
* Updated Leaflet to latest version 0.4.4 #220
* Added marker clustering in map view to handle a large number of markers

View File

@ -20,11 +20,12 @@
<!-- /Recline CSS components -->
<!-- 3rd party JS libraries -->
<script type="text/javascript" src="{{page.root}}vendor/jquery/1.7.1/jquery.js"></script>
<!--script type="text/javascript" src="{{page.root}}vendor/jquery/1.7.1/jquery.js"></script-->
<script src="{{page.root}}vendor/slickgrid/2.0.1/jquery-1.7.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/underscore/1.4.4/underscore.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/mustache/0.5.0-dev/mustache.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/bootstrap/2.0.2/bootstrap.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/bootstrap/2.3.2/bootstrap.js"></script>
<!--[if lte IE 8]>
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/flot/excanvas.min.js"></script>
<![endif]-->
@ -33,8 +34,20 @@
<script type="text/javascript" src="{{page.root}}vendor/leaflet/0.4.4/leaflet.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/leaflet.markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script src="{{page.root}}vendor/slickgrid/2.0.1/jquery.event.drag-2.2.js"></script>
<script src="{{page.root}}vendor/slickgrid/2.0.1/jquery.event.drop-2.2.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<!--script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.min.js"></script-->
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.core.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.formatters.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.editors.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/plugins/slick.rowselectionmodel.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/plugins/slick.rowmovemanager.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/timeline/js/timeline.js"></script>
<!--[if lte IE 7]>

View File

@ -9,13 +9,13 @@
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="{{ page.root }}vendor/bootstrap/2.0.2/css/bootstrap.css" />
<link rel="stylesheet" href="{{ page.root }}vendor/bootstrap/2.3.2/css/bootstrap.css" />
{% if page.recline-deps %}
{% include recline-deps.html %}
{% endif %}
<!-- link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap-responsive.css" -->
<!-- link rel="stylesheet" href="vendor/bootstrap/2.3.2/css/bootstrap-responsive.css" -->
<link href="{{ page.root }}css-site/pygments.css" rel="stylesheet" type="text/css" />
<link href="{{ page.root }}css-site/style.css" rel="stylesheet" type="text/css" />

View File

@ -19,8 +19,8 @@
#recline-flot-tooltip {
position: absolute;
background-color: #FEE !important;
color: #000000 !important;
opacity: 0.8 !important;
border: 1px solid #fdd !important;
background-color: #FEE;
color: #000000;
opacity: 0.8;
border: 1px solid #fdd;
}

View File

@ -67,6 +67,10 @@
vertical-align: top;
}
.header .recline-query-editor label {
display:none;
}
/**********************************************************
* Pager
*********************************************************/
@ -78,6 +82,10 @@
margin-left: 20px;
}
.header .recline-pager .pagination label {
display:none;
}
.header .recline-pager .pagination input {
width: 30px;
height: 18px;

View File

@ -173,3 +173,12 @@ classes should alter those!
line-height: 13px;
}
.recline-cell-reorder {
font-size: 12px;
padding: 2px;
width: 22px;
height: 14px;
line-height: 13px;
cursor: move;
background: url("../images/drag-handle.png") no-repeat center center;
}

View File

@ -1,73 +1,69 @@
jQuery(function($) {
window.dataExplorer = null;
window.multiView = null;
window.explorerDiv = $('.data-explorer-here');
// This is some fancy stuff to allow configuring the multiview from
// parameters in the query string
//
// For more on state see the view documentation.
var state = recline.View.parseQueryString(decodeURIComponent(window.location.search));
if (state) {
_.each(state, function(value, key) {
try {
value = JSON.parse(value);
} catch(e) {}
state[key] = value;
});
} else {
state.url = 'demo';
}
var dataset = null;
if (state.dataset || state.url) {
var datasetInfo = _.extend({
url: state.url,
backend: state.backend
},
state.dataset
);
dataset = new recline.Model.Dataset(datasetInfo);
} else {
dataset = new recline.Model.Dataset({
records: [
{id: 0, date: '2011-01-01', x: 1, y: 2, z: 3, country: 'DE', title: 'first', lat:52.56, lon:13.40},
{id: 1, date: '2011-02-02', x: 2, y: 4, z: 24, country: 'UK', title: 'second', lat:54.97, lon:-1.60},
{id: 2, date: '2011-03-03', x: 3, y: 6, z: 9, country: 'US', title: 'third', lat:40.00, lon:-75.5},
{id: 3, date: '2011-04-04', x: 4, y: 8, z: 6, country: 'UK', title: 'fourth', lat:57.27, lon:-6.20},
{id: 4, date: '2011-05-04', x: 5, y: 10, z: 15, country: 'UK', title: 'fifth', lat:51.58, lon:0},
{id: 5, date: '2011-06-02', x: 6, y: 12, z: 18, country: 'DE', title: 'sixth', lat:51.04, lon:7.9}
],
// let's be really explicit about fields
// Plus take opportunity to set date to be a date field and set some labels
fields: [
{id: 'id'},
{id: 'date', type: 'date'},
{id: 'x', type: 'number'},
{id: 'y', type: 'number'},
{id: 'z', type: 'number'},
{id: 'country', 'label': 'Country'},
{id: 'title', 'label': 'Title'},
{id: 'lat'},
{id: 'lon'}
]
});
}
createExplorer(dataset, state);
// create the demo dataset
var dataset = createDemoDataset();
// now create the multiview
// this is rather more elaborate than the minimum as we configure the
// MultiView in various ways (see function below)
window.multiview = createMultiView(dataset);
// last, we'll demonstrate binding to changes in the dataset
// this will print out a summary of each change onto the page in the
// changelog section
dataset.records.bind('all', function(name, obj) {
var $info = $('<div />');
$info.html(name + ': ' + JSON.stringify(obj.toJSON()));
$('.changelog').append($info);
$('.changelog').show();
});
});
// create standard demo dataset
function createDemoDataset() {
var dataset = new recline.Model.Dataset({
records: [
{id: 0, date: '2011-01-01', x: 1, y: 2, z: 3, country: 'DE', title: 'first', lat:52.56, lon:13.40},
{id: 1, date: '2011-02-02', x: 2, y: 4, z: 24, country: 'UK', title: 'second', lat:54.97, lon:-1.60},
{id: 2, date: '2011-03-03', x: 3, y: 6, z: 9, country: 'US', title: 'third', lat:40.00, lon:-75.5},
{id: 3, date: '2011-04-04', x: 4, y: 8, z: 6, country: 'UK', title: 'fourth', lat:57.27, lon:-6.20},
{id: 4, date: '2011-05-04', x: 5, y: 10, z: 15, country: 'UK', title: 'fifth', lat:51.58, lon:0},
{id: 5, date: '2011-06-02', x: 6, y: 12, z: 18, country: 'DE', title: 'sixth', lat:51.04, lon:7.9}
],
// let's be really explicit about fields
// Plus take opportunity to set date to be a date field and set some labels
fields: [
{id: 'id'},
{id: 'date', type: 'date'},
{id: 'x', type: 'number'},
{id: 'y', type: 'number'},
{id: 'z', type: 'number'},
{id: 'country', 'label': 'Country'},
{id: 'title', 'label': 'Title'},
{id: 'lat'},
{id: 'lon'}
]
});
return dataset;
}
// make Explorer creation / initialization in a function so we can call it
// again and again
var createExplorer = function(dataset, state) {
// remove existing data explorer view
// make MultivView
//
// creation / initialization in a function so we can call it again and again
var createMultiView = function(dataset, state) {
// remove existing multiview if present
var reload = false;
if (window.dataExplorer) {
window.dataExplorer.remove();
if (window.multiView) {
window.multiView.remove();
window.multiView = null;
reload = true;
}
window.dataExplorer = null;
var $el = $('<div />');
$el.appendTo(window.explorerDiv);
// customize the subviews for the MultiView
var views = [
{
id: 'grid',
@ -77,8 +73,12 @@ var createExplorer = function(dataset, state) {
state: {
gridOptions: {
editable: true,
// Enable support for row add
enabledAddRow: true,
enabledDelRow: true,
// Enable support for row delete
enabledDelRow: true,
// Enable support for row Reoder
enableReOrderRow:true,
autoEdit: false,
enableCellNavigation: true
},
@ -106,11 +106,12 @@ var createExplorer = function(dataset, state) {
}
];
window.dataExplorer = new recline.View.MultiView({
var multiView = new recline.View.MultiView({
model: dataset,
el: $el,
state: state,
views: views
});
return multiView;
}

View File

@ -7,14 +7,24 @@ root: ../../
<style type="text/css">
.recline-slickgrid {
height: 550px;
height: 300px;
}
.recline-timeline .vmm-timeline {
height: 550px;
}
.changelog {
display: none;
border-bottom: 1px solid #ccc;
margin-bottom: 10px;
}
</style>
<div class="changelog">
<h3>Changes</h3>
</div>
<div class="data-explorer-here"></div>
<div style="clear: both;"></div>

25
dist/recline.css vendored
View File

@ -19,10 +19,10 @@
#recline-flot-tooltip {
position: absolute;
background-color: #FEE !important;
color: #000000 !important;
opacity: 0.8 !important;
border: 1px solid #fdd !important;
background-color: #FEE;
color: #000000;
opacity: 0.8;
border: 1px solid #fdd;
}
/**********************************************************
* (Data) Grid
@ -316,6 +316,10 @@ div.data-table-cell-content-numeric > a.data-table-cell-edit {
vertical-align: top;
}
.header .recline-query-editor label {
display:none;
}
/**********************************************************
* Pager
*********************************************************/
@ -327,6 +331,10 @@ div.data-table-cell-content-numeric > a.data-table-cell-edit {
margin-left: 20px;
}
.header .recline-pager .pagination label {
display:none;
}
.header .recline-pager .pagination input {
width: 30px;
height: 18px;
@ -622,6 +630,15 @@ classes should alter those!
line-height: 13px;
}
.recline-cell-reorder {
font-size: 12px;
padding: 2px;
width: 22px;
height: 14px;
line-height: 13px;
cursor: move;
background: url("../images/drag-handle.png") no-repeat center center;
}
.recline-timeline {
position: relative;
}

View File

@ -191,7 +191,11 @@ my.Dataset = Backbone.Model.extend({
this.trigger('query:start');
if (queryObj) {
this.queryState.set(queryObj, {silent: true});
var attributes = queryObj;
if (queryObj instanceof my.Query) {
attributes = queryObj.toJSON();
}
this.queryState.set(attributes, {silent: true});
}
var actualQuery = this.queryState.toJSON();

141
dist/recline.js vendored
View File

@ -885,7 +885,11 @@ my.Dataset = Backbone.Model.extend({
this.trigger('query:start');
if (queryObj) {
this.queryState.set(queryObj, {silent: true});
var attributes = queryObj;
if (queryObj instanceof my.Query) {
attributes = queryObj.toJSON();
}
this.queryState.set(attributes, {silent: true});
}
var actualQuery = this.queryState.toJSON();
@ -2459,7 +2463,7 @@ my.Map = Backbone.View.extend({
if (typeof(coord) != 'string') {
return(parseFloat(coord));
}
var dms = coord.split(/[^\.\d\w]+/);
var dms = coord.split(/[^-?\.\d\w]+/);
var deg = 0; var m = 0;
var toDeg = [1, 60, 3600]; // conversion factors for Deg, min, sec
var i;
@ -3046,6 +3050,9 @@ my.MultiView = Backbone.View.extend({
// the main views
_.each(this.pageViews, function(view, pageName) {
view.view.render();
if (view.view.redraw) {
view.view.redraw();
}
$dataViewContainer.append(view.view.el);
if (view.view.elSidebar) {
$dataSidebar.append(view.view.elSidebar);
@ -3516,11 +3523,35 @@ my.SlickGrid = Backbone.View.extend({
}
}
};
//Add row delete support , check if enableDelRow is set to true or not set
//Add row delete support, check if enableReOrderRow is set to true , by
//default it is set to false
// state: {
// gridOptions: {
// enableReOrderRow: true,
// },
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enableReOrderRow != undefined
&& this.state.get("gridOptions").enableReOrderRow == true ){
columns.push({
id: "#",
name: "",
width: 22,
behavior: "selectAndMove",
selectable: false,
resizable: false,
cssClass: "recline-cell-reorder"
})
}
//Add row delete support, check if enabledDelRow is set to true , by
//default it is set to false
// state: {
// gridOptions: {
// enabledDelRow: true,
// },
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enabledDelRow != undefined
&& this.state.get("gridOptions").enabledDelRow == true ){
columns.push({
columns.push({
id: 'del',
name: '',
field: 'del',
@ -3528,7 +3559,8 @@ my.SlickGrid = Backbone.View.extend({
width: 38,
formatter: formatter,
validator:validator
})}
})
}
_.each(this.model.fields.toJSON(),function(field){
var column = {
id: field.id,
@ -3640,6 +3672,71 @@ my.SlickGrid = Backbone.View.extend({
this.grid.setSortColumn(column, sortAsc);
}
/* Row reordering support based on
https://github.com/mleibman/SlickGrid/blob/gh-pages/examples/example9-row-reordering.html
*/
self.grid.setSelectionModel(new Slick.RowSelectionModel());
var moveRowsPlugin = new Slick.RowMoveManager({
cancelEditOnDrag: true
});
moveRowsPlugin.onBeforeMoveRows.subscribe(function (e, data) {
for (var i = 0; i < data.rows.length; i++) {
// no point in moving before or after itself
if (data.rows[i] == data.insertBefore || data.rows[i] == data.insertBefore - 1) {
e.stopPropagation();
return false;
}
}
return true;
});
moveRowsPlugin.onMoveRows.subscribe(function (e, args) {
var extractedRows = [], left, right;
var rows = args.rows;
var insertBefore = args.insertBefore;
var data = self.model.records.toJSON()
left = data.slice(0, insertBefore);
right= data.slice(insertBefore, data.length);
rows.sort(function(a,b) { return a-b; });
for (var i = 0; i < rows.length; i++) {
extractedRows.push(data[rows[i]]);
}
rows.reverse();
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row < insertBefore) {
left.splice(row, 1);
} else {
right.splice(row - insertBefore, 1);
}
}
data = left.concat(extractedRows.concat(right));
var selectedRows = [];
for (var i = 0; i < rows.length; i++)
selectedRows.push(left.length + i);
self.model.records.reset(data)
});
//register The plugin to handle row Reorder
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enableReOrderRow != undefined
&& this.state.get("gridOptions").enableReOrderRow == true ){
self.grid.registerPlugin(moveRowsPlugin);
}
/* end row reordering support*/
this._slickHandler.subscribe(this.grid.onSort, function(e, args){
var order = (args.sortAsc) ? 'asc':'desc';
var sort = [{
@ -3648,11 +3745,11 @@ my.SlickGrid = Backbone.View.extend({
}];
self.model.query({sort: sort});
});
this._slickHandler.subscribe(this.grid.onColumnsReordered, 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;
@ -3677,16 +3774,26 @@ my.SlickGrid = Backbone.View.extend({
this._slickHandler.subscribe(this.grid.onClick,function(e, args){
//try catch , because this fail in qunit , but no
//error on browser.
try{e.preventDefault()}catch(e){}
if (args.cell == 0 && self.state.get("gridOptions").enabledDelRow == true){
// We need to delete the associated model
var model = data.getModel(args.row);
model.destroy()
}
try{e.preventDefault()}catch(e){}
// The cell of grid that handle row delete is The first cell (0) if
// The grid ReOrder is not present ie enableReOrderRow == false
// else it is The the second cell (1) , because The 0 is now cell
// that handle row Reoder.
var cell =0
if(self.state.get("gridOptions")
&& self.state.get("gridOptions").enableReOrderRow != undefined
&& self.state.get("gridOptions").enableReOrderRow == true ){
cell =1
}
if (args.cell == cell && self.state.get("gridOptions").enabledDelRow == true){
// We need to delete the associated model
var model = data.getModel(args.row);
model.destroy()
}
}) ;
var columnpicker = new Slick.Controls.ColumnPicker(columns, this.grid,
_.extend(options,{state:this.state}));
if (self.visible){
self.grid.init();
self.rendered = true;
@ -3694,8 +3801,8 @@ my.SlickGrid = Backbone.View.extend({
// Defer rendering until the view is visible
self.rendered = false;
}
return this;
},
remove: function () {
@ -4393,7 +4500,7 @@ my.Pager = Backbone.View.extend({
<div class="pagination"> \
<ul> \
<li class="prev action-pagination-update"><a href="">&laquo;</a></li> \
<li class="active"><a><input name="from" type="text" value="{{from}}" /> &ndash; <input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="active"><label for="from">From</label><a><input name="from" type="text" value="{{from}}" /> &ndash; <label for="to">To</label><input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="next action-pagination-update"><a href="">&raquo;</a></li> \
</ul> \
</div> \
@ -4467,7 +4574,7 @@ my.QueryEditor = Backbone.View.extend({
<form action="" method="GET" class="form-inline"> \
<div class="input-prepend text-query"> \
<span class="add-on"><i class="icon-search"></i></span> \
<input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
<label>Search</label><input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
</div> \
<button type="submit" class="btn">Go &raquo;</button> \
</form> \

View File

@ -21,17 +21,20 @@ Before writing any code with Recline, you need to do the following preparation s
2. Include the relevant CSS in the head section of your document:
{% highlight html %}
<!-- you do not have to use bootstrap but we use it by default -->
<link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap.css" />
<link rel="stylesheet" href="vendor/bootstrap/2.3.2/css/bootstrap.css" />
<!-- CSS for relevant view components - here we just have grid -->
<link rel="stylesheet" href="css/grid.css" />{% endhighlight %}
3. Include the relevant Javascript files somewhere on the page (preferably before body close tag):
{% highlight html %}<!-- 3rd party dependencies -->
<script type="text/javascript" src="vendor/jquery/1.7.1/jquery.js"></script>
<script type="text/javascript" src="vendor/underscore/1.1.6/underscore.js"></script>
<script type="text/javascript" src="vendor/backbone/0.5.1/backbone.js"></script>
<script type="text/javascript" src="vendor/jquery.mustache.js"></script>
<script type="text/javascript" src="vendor/bootstrap/2.0.2/bootstrap.js"></script>
<script type="text/javascript" src="vendor/underscore/1.4.4/underscore.js"></script>
<script type="text/javascript" src="vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="vendor/mustache/0.5.0-dev/mustache.js"></script>
<script type="text/javascript" src="vendor/bootstrap/2.3.2/bootstrap.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/jquery.event.drag-2.2.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/slick.core.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/slick.grid.js"></script>
<!-- note that we could include individual components rather than whole of recline e.g.
<script type="text/javascript" src="src/model.js"></script>
<script type="text/javascript" src="src/backend/memory.js"></script>
@ -65,15 +68,18 @@ var dataset = new recline.Model.Dataset({
### Setting up the Grid
Let's create a data grid view to display the dataset we have just created. We're going to use the SlickGrid-based grid so we need the following:
Let's create a data grid view to display the dataset we have just created. We're going to use the SlickGrid-based grid so we need the following CSS and JS dependencies in addition to those above:
{% highlight html %}
<link rel="stylesheet" href="css/slickgrid.css">
<link rel="stylesheet" href="vendor/slickgrid/2.0.1/slick.grid.css">
<!-- vendor -->
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/plugins/slick.rowselectionmodel.js"></script>
<script type="text/javascript" src="vendor/slickgrid/2.0.1/plugins/slick.rowmovemanager.js"></script>
<!-- Recline -->
<script type="text/javascript" src="src/view.slickgrid.js"></script>
@ -129,6 +135,9 @@ library and the Recline Flot Graph view:
<!--[if lte IE 8]>
<script language="javascript" type="text/javascript" src="vendor/flot/excanvas.min.js"></script>
<![endif]-->
<!-- you only need moment when you have datetime data -->
<script type="text/javascript" src="vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="vendor/flot/jquery.flot.js"></script>
<script type="text/javascript" src="vendor/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="src/view.graph.js"></script>

View File

@ -105,7 +105,7 @@ Here is an example of the page setup for an app using every Recline component:
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="vendor/bootstrap/2.0.2/css/bootstrap.css" />
<link rel="stylesheet" href="vendor/bootstrap/2.3.2/css/bootstrap.css" />
{% include recline-deps.html %}
{% endhighlight %}

BIN
images/drag-handle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -191,7 +191,11 @@ my.Dataset = Backbone.Model.extend({
this.trigger('query:start');
if (queryObj) {
this.queryState.set(queryObj, {silent: true});
var attributes = queryObj;
if (queryObj instanceof my.Query) {
attributes = queryObj.toJSON();
}
this.queryState.set(attributes, {silent: true});
}
var actualQuery = this.queryState.toJSON();

View File

@ -334,7 +334,7 @@ my.Map = Backbone.View.extend({
if (typeof(coord) != 'string') {
return(parseFloat(coord));
}
var dms = coord.split(/[^\.\d\w]+/);
var dms = coord.split(/[^-?\.\d\w]+/);
var deg = 0; var m = 0;
var toDeg = [1, 60, 3600]; // conversion factors for Deg, min, sec
var i;

View File

@ -11,7 +11,7 @@ this.recline.View = this.recline.View || {};
// Manage multiple views together along with query editor etc. Usage:
//
// <pre>
// var myExplorer = new model.recline.MultiView({
// var myExplorer = new recline.View.MultiView({
// model: {{recline.Model.Dataset instance}}
// el: {{an existing dom element}}
// views: {{dataset views}}
@ -79,10 +79,10 @@ this.recline.View = this.recline.View || {};
// special as it includes config of many of the subviews.
//
// <pre>
// state = {
// var state = {
// query: {dataset query state - see dataset.queryState object}
// view-{id1}: {view-state for this view}
// view-{id2}: {view-state for }
// 'view-{id1}': {view-state for this view}
// 'view-{id2}': {view-state for }
// ...
// // Explorer
// currentView: id of current view (defaults to first view if not specified)
@ -248,6 +248,9 @@ my.MultiView = Backbone.View.extend({
// the main views
_.each(this.pageViews, function(view, pageName) {
view.view.render();
if (view.view.redraw) {
view.view.redraw();
}
$dataViewContainer.append(view.view.el);
if (view.view.elSidebar) {
$dataSidebar.append(view.view.elSidebar);

View File

@ -155,11 +155,35 @@ my.SlickGrid = Backbone.View.extend({
}
}
};
//Add row delete support , check if enableDelRow is set to true or not set
//Add row delete support, check if enableReOrderRow is set to true , by
//default it is set to false
// state: {
// gridOptions: {
// enableReOrderRow: true,
// },
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enableReOrderRow != undefined
&& this.state.get("gridOptions").enableReOrderRow == true ){
columns.push({
id: "#",
name: "",
width: 22,
behavior: "selectAndMove",
selectable: false,
resizable: false,
cssClass: "recline-cell-reorder"
})
}
//Add row delete support, check if enabledDelRow is set to true , by
//default it is set to false
// state: {
// gridOptions: {
// enabledDelRow: true,
// },
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enabledDelRow != undefined
&& this.state.get("gridOptions").enabledDelRow == true ){
columns.push({
columns.push({
id: 'del',
name: '',
field: 'del',
@ -167,7 +191,8 @@ my.SlickGrid = Backbone.View.extend({
width: 38,
formatter: formatter,
validator:validator
})}
})
}
_.each(this.model.fields.toJSON(),function(field){
var column = {
id: field.id,
@ -279,6 +304,71 @@ my.SlickGrid = Backbone.View.extend({
this.grid.setSortColumn(column, sortAsc);
}
/* Row reordering support based on
https://github.com/mleibman/SlickGrid/blob/gh-pages/examples/example9-row-reordering.html
*/
self.grid.setSelectionModel(new Slick.RowSelectionModel());
var moveRowsPlugin = new Slick.RowMoveManager({
cancelEditOnDrag: true
});
moveRowsPlugin.onBeforeMoveRows.subscribe(function (e, data) {
for (var i = 0; i < data.rows.length; i++) {
// no point in moving before or after itself
if (data.rows[i] == data.insertBefore || data.rows[i] == data.insertBefore - 1) {
e.stopPropagation();
return false;
}
}
return true;
});
moveRowsPlugin.onMoveRows.subscribe(function (e, args) {
var extractedRows = [], left, right;
var rows = args.rows;
var insertBefore = args.insertBefore;
var data = self.model.records.toJSON()
left = data.slice(0, insertBefore);
right= data.slice(insertBefore, data.length);
rows.sort(function(a,b) { return a-b; });
for (var i = 0; i < rows.length; i++) {
extractedRows.push(data[rows[i]]);
}
rows.reverse();
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
if (row < insertBefore) {
left.splice(row, 1);
} else {
right.splice(row - insertBefore, 1);
}
}
data = left.concat(extractedRows.concat(right));
var selectedRows = [];
for (var i = 0; i < rows.length; i++)
selectedRows.push(left.length + i);
self.model.records.reset(data)
});
//register The plugin to handle row Reorder
if(this.state.get("gridOptions")
&& this.state.get("gridOptions").enableReOrderRow != undefined
&& this.state.get("gridOptions").enableReOrderRow == true ){
self.grid.registerPlugin(moveRowsPlugin);
}
/* end row reordering support*/
this._slickHandler.subscribe(this.grid.onSort, function(e, args){
var order = (args.sortAsc) ? 'asc':'desc';
var sort = [{
@ -287,11 +377,11 @@ my.SlickGrid = Backbone.View.extend({
}];
self.model.query({sort: sort});
});
this._slickHandler.subscribe(this.grid.onColumnsReordered, 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;
@ -316,16 +406,26 @@ my.SlickGrid = Backbone.View.extend({
this._slickHandler.subscribe(this.grid.onClick,function(e, args){
//try catch , because this fail in qunit , but no
//error on browser.
try{e.preventDefault()}catch(e){}
if (args.cell == 0 && self.state.get("gridOptions").enabledDelRow == true){
// We need to delete the associated model
var model = data.getModel(args.row);
model.destroy()
}
try{e.preventDefault()}catch(e){}
// The cell of grid that handle row delete is The first cell (0) if
// The grid ReOrder is not present ie enableReOrderRow == false
// else it is The the second cell (1) , because The 0 is now cell
// that handle row Reoder.
var cell =0
if(self.state.get("gridOptions")
&& self.state.get("gridOptions").enableReOrderRow != undefined
&& self.state.get("gridOptions").enableReOrderRow == true ){
cell =1
}
if (args.cell == cell && self.state.get("gridOptions").enabledDelRow == true){
// We need to delete the associated model
var model = data.getModel(args.row);
model.destroy()
}
}) ;
var columnpicker = new Slick.Controls.ColumnPicker(columns, this.grid,
_.extend(options,{state:this.state}));
if (self.visible){
self.grid.init();
self.rendered = true;
@ -333,8 +433,8 @@ my.SlickGrid = Backbone.View.extend({
// Defer rendering until the view is visible
self.rendered = false;
}
return this;
},
remove: function () {

View File

@ -12,7 +12,7 @@ my.Pager = Backbone.View.extend({
<div class="pagination"> \
<ul> \
<li class="prev action-pagination-update"><a href="">&laquo;</a></li> \
<li class="active"><a><input name="from" type="text" value="{{from}}" /> &ndash; <input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="active"><label for="from">From</label><a><input name="from" type="text" value="{{from}}" /> &ndash; <label for="to">To</label><input name="to" type="text" value="{{to}}" /> </a></li> \
<li class="next action-pagination-update"><a href="">&raquo;</a></li> \
</ul> \
</div> \

View File

@ -12,7 +12,7 @@ my.QueryEditor = Backbone.View.extend({
<form action="" method="GET" class="form-inline"> \
<div class="input-prepend text-query"> \
<span class="add-on"><i class="icon-search"></i></span> \
<input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
<label>Search</label><input type="text" name="q" value="{{q}}" class="span2" placeholder="Search data ..." class="search-query" /> \
</div> \
<button type="submit" class="btn">Go &raquo;</button> \
</form> \

View File

@ -14,7 +14,7 @@
<script type="text/javascript" src="../vendor/jquery-ui-1.8.14.custom.min.js"></script>
<script type="text/javascript" src="../vendor/jquery.flot/0.7/jquery.flot.js"></script>
<script type="text/javascript" src="../vendor/jquery.mustache.js"></script>
<script type="text/javascript" src="../vendor/bootstrap/2.0.2/bootstrap.js"></script>
<script type="text/javascript" src="../vendor/bootstrap/2.3.2/bootstrap.js"></script>
<script type="text/javascript" src="../vendor/leaflet/0.3.1/leaflet.js"></script>
<script type="text/javascript" src="qunit/qunit.js"></script>

View File

@ -18,14 +18,24 @@
<script type="text/javascript" src="../vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="../vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="../vendor/mustache/0.5.0-dev/mustache.js"></script>
<script type="text/javascript" src="../vendor/bootstrap/2.0.2/bootstrap.js"></script>
<script type="text/javascript" src="../vendor/bootstrap/2.3.2/bootstrap.js"></script>
<script type="text/javascript" src="../vendor/flot/jquery.flot.js"></script>
<script type="text/javascript" src="../vendor/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="../vendor/leaflet/0.4.4/leaflet-src.js"></script>
<script type="text/javascript" src="../vendor/leaflet.markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/slick.core.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/slick.formatters.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/slick.editors.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/plugins/slick.rowselectionmodel.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/plugins/slick.rowmovemanager.js"></script>
<script type="text/javascript" src="../vendor/timeline/js/timeline.js"></script>
<!--[if lte IE 7]>
<script language="javascript" type="text/javascript" src="../vendor/json/json2.js"></script>

View File

@ -177,6 +177,31 @@ test('Dataset getFieldsSummary', function () {
});
});
test('query with Query model', function () {
var dataset = new recline.Model.Dataset({
records: [{country: 'UK'}, {country: 'DE'}]
});
var query = new recline.Model.Query();
query.addFilter({type: 'term', field: 'country', term: 'DE'});
dataset.query(query).done(function (results) {
deepEqual(results.length, 1);
deepEqual(results.models[0].toJSON(), {country: 'DE'});
});
});
test('query with plain object', function () {
var dataset = new recline.Model.Dataset({
records: [{country: 'UK'}, {country: 'DE'}]
});
var query = {q: 'DE'};
dataset.query(query).done(function (results) {
deepEqual(results.length, 1);
deepEqual(results.models[0].toJSON(), {country: 'DE'});
});
});
test('fetch without and with explicit fields', function () {
var dataset = new recline.Model.Dataset({
backend: 'csv',

View File

@ -112,7 +112,10 @@ test('_getGeometryFromRecord non-GeoJSON', function () {
[{ lon: 47, lat: 53}, [47,53]],
[{ lon: -47, lat: 53}, [-47,53]],
["53.3,47.32", [47.32, 53.3]],
["53.3,-47.32", [-47.32, 53.3]],
["53.3, 47.32", [47.32, 53.3]],
["-53.3, 47.32", [47.32, -53.3]],
["53.3, -47.32", [-47.32, 53.3]],
["(53.3,47.32)", [47.32, 53.3]],
[[53.3,47.32], [53.3, 47.32]],
["53.3 N, 113.5 W", [-113.5, 53.3]],

View File

@ -113,7 +113,7 @@ test('delete-row' , function(){
columnsWidth:[
{column:'id',width: 250}
],
gridOptions: {editable: true , "enabledDelRow":true},
gridOptions: {editable: true , "enabledDelRow":true },
columnsEditor: [{column: 'country', editor: Slick.Editors.Text}]
}
});
@ -125,7 +125,6 @@ test('delete-row' , function(){
dataset.records.on('remove', function(record){
equal(dataset.records.length, old_length -1 );
});
// Be sure a cell change triggers a change of the model
e = new Slick.EventData();
view.grid.onClick.notify({
@ -139,6 +138,54 @@ test('delete-row' , function(){
});
//Test delete row with RowReorder set to True , This is The same
// test as above (delete-row), the only diference is that here we Enable
// row ReOrder to true, so The cell That handle delete row of grid is
// 1 instead of 0.
// The cell of grid that handle row delete is The first cell (0) if
// The grid ReOrder is not present ie enableReOrderRow == false
// else it is The the second cell (1) , because The 0 is now cell
// that handle row Reoder.
test('delete-row-with-row-reorder-activated' , function(){
var dataset = Fixture.getDataset();
var view = new recline.View.SlickGrid({
model: dataset,
state: {
hiddenColumns:['x','lat','title'],
columnsOrder:['lon','id','z','date', 'y', 'country'],
columnsWidth:[
{column:'id',width: 250}
],
gridOptions: {editable: true , "enabledDelRow":true , "enableReOrderRow":true},
columnsEditor: [{column: 'country', editor: Slick.Editors.Text}]
}
});
$('.fixtures .test-datatable').append(view.el);
view.render();
view.show();
old_length = dataset.records.length
dataset.records.on('remove', function(record){
equal(dataset.records.length, old_length -1 );
});
// Be sure a cell change triggers a change of the model
e = new Slick.EventData();
view.grid.onClick.notify({
row: 1,
// cell is 1 instead of 0
cell: 1,
grid: view.grid
}, e, view.grid);
view.remove();
});
test('add-row' , function(){
//To test adding row on slickgrid , we add some menu GridControl
//I am based on the FlotControl in flot wiewer , to add a similary

View File

@ -1,686 +0,0 @@
/*!
* Bootstrap Responsive v2.0.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.hide-text {
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
}
.input-block-level {
display: block;
width: 100%;
min-height: 28px;
/* Make inputs at least the height of their button counterpart */
/* Makes inputs behave like true block-level elements */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.hidden {
display: none;
visibility: hidden;
}
.visible-phone {
display: none;
}
.visible-tablet {
display: none;
}
.visible-desktop {
display: block;
}
.hidden-phone {
display: block;
}
.hidden-tablet {
display: block;
}
.hidden-desktop {
display: none;
}
@media (max-width: 767px) {
.visible-phone {
display: block;
}
.hidden-phone {
display: none;
}
.hidden-desktop {
display: block;
}
.visible-desktop {
display: none;
}
}
@media (min-width: 768px) and (max-width: 979px) {
.visible-tablet {
display: block;
}
.hidden-tablet {
display: none;
}
.hidden-desktop {
display: block;
}
.visible-desktop {
display: none;
}
}
@media (max-width: 480px) {
.nav-collapse {
-webkit-transform: translate3d(0, 0, 0);
}
.page-header h1 small {
display: block;
line-height: 18px;
}
input[type="checkbox"],
input[type="radio"] {
border: 1px solid #ccc;
}
.form-horizontal .control-group > label {
float: none;
width: auto;
padding-top: 0;
text-align: left;
}
.form-horizontal .controls {
margin-left: 0;
}
.form-horizontal .control-list {
padding-top: 0;
}
.form-horizontal .form-actions {
padding-left: 10px;
padding-right: 10px;
}
.modal {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
width: auto;
margin: 0;
}
.modal.fade.in {
top: auto;
}
.modal-header .close {
padding: 10px;
margin: -10px;
}
.carousel-caption {
position: static;
}
}
@media (max-width: 767px) {
body {
padding-left: 20px;
padding-right: 20px;
}
.navbar-fixed-top {
margin-left: -20px;
margin-right: -20px;
}
.container {
width: auto;
}
.row-fluid {
width: 100%;
}
.row {
margin-left: 0;
}
.row > [class*="span"],
.row-fluid > [class*="span"] {
float: none;
display: block;
width: auto;
margin: 0;
}
.thumbnails [class*="span"] {
width: auto;
}
input[class*="span"],
select[class*="span"],
textarea[class*="span"],
.uneditable-input {
display: block;
width: 100%;
min-height: 28px;
/* Make inputs at least the height of their button counterpart */
/* Makes inputs behave like true block-level elements */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.input-prepend input[class*="span"],
.input-append input[class*="span"] {
width: auto;
}
}
@media (min-width: 768px) and (max-width: 979px) {
.row {
margin-left: -20px;
*zoom: 1;
}
.row:before,
.row:after {
display: table;
content: "";
}
.row:after {
clear: both;
}
[class*="span"] {
float: left;
margin-left: 20px;
}
.container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
width: 724px;
}
.span12 {
width: 724px;
}
.span11 {
width: 662px;
}
.span10 {
width: 600px;
}
.span9 {
width: 538px;
}
.span8 {
width: 476px;
}
.span7 {
width: 414px;
}
.span6 {
width: 352px;
}
.span5 {
width: 290px;
}
.span4 {
width: 228px;
}
.span3 {
width: 166px;
}
.span2 {
width: 104px;
}
.span1 {
width: 42px;
}
.offset12 {
margin-left: 764px;
}
.offset11 {
margin-left: 702px;
}
.offset10 {
margin-left: 640px;
}
.offset9 {
margin-left: 578px;
}
.offset8 {
margin-left: 516px;
}
.offset7 {
margin-left: 454px;
}
.offset6 {
margin-left: 392px;
}
.offset5 {
margin-left: 330px;
}
.offset4 {
margin-left: 268px;
}
.offset3 {
margin-left: 206px;
}
.offset2 {
margin-left: 144px;
}
.offset1 {
margin-left: 82px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before,
.row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.762430939%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span12 {
width: 99.999999993%;
}
.row-fluid > .span11 {
width: 91.436464082%;
}
.row-fluid > .span10 {
width: 82.87292817100001%;
}
.row-fluid > .span9 {
width: 74.30939226%;
}
.row-fluid > .span8 {
width: 65.74585634900001%;
}
.row-fluid > .span7 {
width: 57.182320438000005%;
}
.row-fluid > .span6 {
width: 48.618784527%;
}
.row-fluid > .span5 {
width: 40.055248616%;
}
.row-fluid > .span4 {
width: 31.491712705%;
}
.row-fluid > .span3 {
width: 22.928176794%;
}
.row-fluid > .span2 {
width: 14.364640883%;
}
.row-fluid > .span1 {
width: 5.801104972%;
}
input,
textarea,
.uneditable-input {
margin-left: 0;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 714px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 652px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 590px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 528px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 466px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 404px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 342px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 280px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 218px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 156px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 94px;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 32px;
}
}
@media (max-width: 979px) {
body {
padding-top: 0;
}
.navbar-fixed-top {
position: static;
margin-bottom: 18px;
}
.navbar-fixed-top .navbar-inner {
padding: 5px;
}
.navbar .container {
width: auto;
padding: 0;
}
.navbar .brand {
padding-left: 10px;
padding-right: 10px;
margin: 0 0 0 -5px;
}
.navbar .nav-collapse {
clear: left;
}
.navbar .nav {
float: none;
margin: 0 0 9px;
}
.navbar .nav > li {
float: none;
}
.navbar .nav > li > a {
margin-bottom: 2px;
}
.navbar .nav > .divider-vertical {
display: none;
}
.navbar .nav .nav-header {
color: #999999;
text-shadow: none;
}
.navbar .nav > li > a,
.navbar .dropdown-menu a {
padding: 6px 15px;
font-weight: bold;
color: #999999;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.navbar .dropdown-menu li + li a {
margin-bottom: 2px;
}
.navbar .nav > li > a:hover,
.navbar .dropdown-menu a:hover {
background-color: #222222;
}
.navbar .dropdown-menu {
position: static;
top: auto;
left: auto;
float: none;
display: block;
max-width: none;
margin: 0 15px;
padding: 0;
background-color: transparent;
border: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.navbar .dropdown-menu:before,
.navbar .dropdown-menu:after {
display: none;
}
.navbar .dropdown-menu .divider {
display: none;
}
.navbar-form,
.navbar-search {
float: none;
padding: 9px 15px;
margin: 9px 0;
border-top: 1px solid #222222;
border-bottom: 1px solid #222222;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
}
.navbar .nav.pull-right {
float: none;
margin-left: 0;
}
.navbar-static .navbar-inner {
padding-left: 10px;
padding-right: 10px;
}
.btn-navbar {
display: block;
}
.nav-collapse {
overflow: hidden;
height: 0;
}
}
@media (min-width: 980px) {
.nav-collapse.collapse {
height: auto !important;
overflow: visible !important;
}
}
@media (min-width: 1200px) {
.row {
margin-left: -30px;
*zoom: 1;
}
.row:before,
.row:after {
display: table;
content: "";
}
.row:after {
clear: both;
}
[class*="span"] {
float: left;
margin-left: 30px;
}
.container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
width: 1170px;
}
.span12 {
width: 1170px;
}
.span11 {
width: 1070px;
}
.span10 {
width: 970px;
}
.span9 {
width: 870px;
}
.span8 {
width: 770px;
}
.span7 {
width: 670px;
}
.span6 {
width: 570px;
}
.span5 {
width: 470px;
}
.span4 {
width: 370px;
}
.span3 {
width: 270px;
}
.span2 {
width: 170px;
}
.span1 {
width: 70px;
}
.offset12 {
margin-left: 1230px;
}
.offset11 {
margin-left: 1130px;
}
.offset10 {
margin-left: 1030px;
}
.offset9 {
margin-left: 930px;
}
.offset8 {
margin-left: 830px;
}
.offset7 {
margin-left: 730px;
}
.offset6 {
margin-left: 630px;
}
.offset5 {
margin-left: 530px;
}
.offset4 {
margin-left: 430px;
}
.offset3 {
margin-left: 330px;
}
.offset2 {
margin-left: 230px;
}
.offset1 {
margin-left: 130px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before,
.row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.564102564%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span12 {
width: 100%;
}
.row-fluid > .span11 {
width: 91.45299145300001%;
}
.row-fluid > .span10 {
width: 82.905982906%;
}
.row-fluid > .span9 {
width: 74.358974359%;
}
.row-fluid > .span8 {
width: 65.81196581200001%;
}
.row-fluid > .span7 {
width: 57.264957265%;
}
.row-fluid > .span6 {
width: 48.717948718%;
}
.row-fluid > .span5 {
width: 40.170940171000005%;
}
.row-fluid > .span4 {
width: 31.623931624%;
}
.row-fluid > .span3 {
width: 23.076923077%;
}
.row-fluid > .span2 {
width: 14.529914530000001%;
}
.row-fluid > .span1 {
width: 5.982905983%;
}
input,
textarea,
.uneditable-input {
margin-left: 0;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 1160px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 1060px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 960px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 860px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 760px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 660px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 560px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 460px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 360px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 260px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 160px;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 60px;
}
.thumbnails {
margin-left: -30px;
}
.thumbnails > li {
margin-left: 30px;
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6167
vendor/bootstrap/2.3.2/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,402 @@
/*!
* jquery.event.drag - v 2.2
* Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
* Open Source MIT License - http://threedubmedia.com/code/license
*/
// Created: 2008-06-04
// Updated: 2012-05-21
// REQUIRES: jquery 1.7.x
;(function( $ ){
// add the jquery instance method
$.fn.drag = function( str, arg, opts ){
// figure out the event type
var type = typeof str == "string" ? str : "",
// figure out the event handler...
fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
// fix the event type
if ( type.indexOf("drag") !== 0 )
type = "drag"+ type;
// were options passed
opts = ( str == fn ? arg : opts ) || {};
// trigger or bind event handler
return fn ? this.bind( type, opts, fn ) : this.trigger( type );
};
// local refs (increase compression)
var $event = $.event,
$special = $event.special,
// configure the drag special event
drag = $special.drag = {
// these are the default settings
defaults: {
which: 1, // mouse button pressed to start drag sequence
distance: 0, // distance dragged before dragstart
not: ':input', // selector to suppress dragging on target elements
handle: null, // selector to match handle target elements
relative: false, // true to use "position", false to use "offset"
drop: true, // false to suppress drop events, true or selector to allow
click: false // false to suppress click events after dragend (no proxy)
},
// the key name for stored drag data
datakey: "dragdata",
// prevent bubbling for better performance
noBubble: true,
// count bound related events
add: function( obj ){
// read the interaction data
var data = $.data( this, drag.datakey ),
// read any passed options
opts = obj.data || {};
// count another realted event
data.related += 1;
// extend data options bound with this event
// don't iterate "opts" in case it is a node
$.each( drag.defaults, function( key, def ){
if ( opts[ key ] !== undefined )
data[ key ] = opts[ key ];
});
},
// forget unbound related events
remove: function(){
$.data( this, drag.datakey ).related -= 1;
},
// configure interaction, capture settings
setup: function(){
// check for related events
if ( $.data( this, drag.datakey ) )
return;
// initialize the drag data with copied defaults
var data = $.extend({ related:0 }, drag.defaults );
// store the interaction data
$.data( this, drag.datakey, data );
// bind the mousedown event, which starts drag interactions
$event.add( this, "touchstart mousedown", drag.init, data );
// prevent image dragging in IE...
if ( this.attachEvent )
this.attachEvent("ondragstart", drag.dontstart );
},
// destroy configured interaction
teardown: function(){
var data = $.data( this, drag.datakey ) || {};
// check for related events
if ( data.related )
return;
// remove the stored data
$.removeData( this, drag.datakey );
// remove the mousedown event
$event.remove( this, "touchstart mousedown", drag.init );
// enable text selection
drag.textselect( true );
// un-prevent image dragging in IE...
if ( this.detachEvent )
this.detachEvent("ondragstart", drag.dontstart );
},
// initialize the interaction
init: function( event ){
// sorry, only one touch at a time
if ( drag.touched )
return;
// the drag/drop interaction data
var dd = event.data, results;
// check the which directive
if ( event.which != 0 && dd.which > 0 && event.which != dd.which )
return;
// check for suppressed selector
if ( $( event.target ).is( dd.not ) )
return;
// check for handle selector
if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length )
return;
drag.touched = event.type == 'touchstart' ? this : null;
dd.propagates = 1;
dd.mousedown = this;
dd.interactions = [ drag.interaction( this, dd ) ];
dd.target = event.target;
dd.pageX = event.pageX;
dd.pageY = event.pageY;
dd.dragging = null;
// handle draginit event...
results = drag.hijack( event, "draginit", dd );
// early cancel
if ( !dd.propagates )
return;
// flatten the result set
results = drag.flatten( results );
// insert new interaction elements
if ( results && results.length ){
dd.interactions = [];
$.each( results, function(){
dd.interactions.push( drag.interaction( this, dd ) );
});
}
// remember how many interactions are propagating
dd.propagates = dd.interactions.length;
// locate and init the drop targets
if ( dd.drop !== false && $special.drop )
$special.drop.handler( event, dd );
// disable text selection
drag.textselect( false );
// bind additional events...
if ( drag.touched )
$event.add( drag.touched, "touchmove touchend", drag.handler, dd );
else
$event.add( document, "mousemove mouseup", drag.handler, dd );
// helps prevent text selection or scrolling
if ( !drag.touched || dd.live )
return false;
},
// returns an interaction object
interaction: function( elem, dd ){
var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 };
return {
drag: elem,
callback: new drag.callback(),
droppable: [],
offset: offset
};
},
// handle drag-releatd DOM events
handler: function( event ){
// read the data before hijacking anything
var dd = event.data;
// handle various events
switch ( event.type ){
// mousemove, check distance, start dragging
case !dd.dragging && 'touchmove':
event.preventDefault();
case !dd.dragging && 'mousemove':
// drag tolerance, x≤ + y≤ = distance≤
if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) )
break; // distance tolerance not reached
event.target = dd.target; // force target from "mousedown" event (fix distance issue)
drag.hijack( event, "dragstart", dd ); // trigger "dragstart"
if ( dd.propagates ) // "dragstart" not rejected
dd.dragging = true; // activate interaction
// mousemove, dragging
case 'touchmove':
event.preventDefault();
case 'mousemove':
if ( dd.dragging ){
// trigger "drag"
drag.hijack( event, "drag", dd );
if ( dd.propagates ){
// manage drop events
if ( dd.drop !== false && $special.drop )
$special.drop.handler( event, dd ); // "dropstart", "dropend"
break; // "drag" not rejected, stop
}
event.type = "mouseup"; // helps "drop" handler behave
}
// mouseup, stop dragging
case 'touchend':
case 'mouseup':
default:
if ( drag.touched )
$event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events
else
$event.remove( document, "mousemove mouseup", drag.handler ); // remove page events
if ( dd.dragging ){
if ( dd.drop !== false && $special.drop )
$special.drop.handler( event, dd ); // "drop"
drag.hijack( event, "dragend", dd ); // trigger "dragend"
}
drag.textselect( true ); // enable text selection
// if suppressing click events...
if ( dd.click === false && dd.dragging )
$.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 );
dd.dragging = drag.touched = false; // deactivate element
break;
}
},
// re-use event object for custom events
hijack: function( event, type, dd, x, elem ){
// not configured
if ( !dd )
return;
// remember the original event and type
var orig = { event:event.originalEvent, type:event.type },
// is the event drag related or drog related?
mode = type.indexOf("drop") ? "drag" : "drop",
// iteration vars
result, i = x || 0, ia, $elems, callback,
len = !isNaN( x ) ? x : dd.interactions.length;
// modify the event type
event.type = type;
// remove the original event
event.originalEvent = null;
// initialize the results
dd.results = [];
// handle each interacted element
do if ( ia = dd.interactions[ i ] ){
// validate the interaction
if ( type !== "dragend" && ia.cancelled )
continue;
// set the dragdrop properties on the event object
callback = drag.properties( event, dd, ia );
// prepare for more results
ia.results = [];
// handle each element
$( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){
// identify drag or drop targets individually
callback.target = subject;
// force propagtion of the custom event
event.isPropagationStopped = function(){ return false; };
// handle the event
result = subject ? $event.dispatch.call( subject, event, callback ) : null;
// stop the drag interaction for this element
if ( result === false ){
if ( mode == "drag" ){
ia.cancelled = true;
dd.propagates -= 1;
}
if ( type == "drop" ){
ia[ mode ][p] = null;
}
}
// assign any dropinit elements
else if ( type == "dropinit" )
ia.droppable.push( drag.element( result ) || subject );
// accept a returned proxy element
if ( type == "dragstart" )
ia.proxy = $( drag.element( result ) || ia.drag )[0];
// remember this result
ia.results.push( result );
// forget the event result, for recycling
delete event.result;
// break on cancelled handler
if ( type !== "dropinit" )
return result;
});
// flatten the results
dd.results[ i ] = drag.flatten( ia.results );
// accept a set of valid drop targets
if ( type == "dropinit" )
ia.droppable = drag.flatten( ia.droppable );
// locate drop targets
if ( type == "dragstart" && !ia.cancelled )
callback.update();
}
while ( ++i < len )
// restore the original event & type
event.type = orig.type;
event.originalEvent = orig.event;
// return all handler results
return drag.flatten( dd.results );
},
// extend the callback object with drag/drop properties...
properties: function( event, dd, ia ){
var obj = ia.callback;
// elements
obj.drag = ia.drag;
obj.proxy = ia.proxy || ia.drag;
// starting mouse position
obj.startX = dd.pageX;
obj.startY = dd.pageY;
// current distance dragged
obj.deltaX = event.pageX - dd.pageX;
obj.deltaY = event.pageY - dd.pageY;
// original element position
obj.originalX = ia.offset.left;
obj.originalY = ia.offset.top;
// adjusted element position
obj.offsetX = obj.originalX + obj.deltaX;
obj.offsetY = obj.originalY + obj.deltaY;
// assign the drop targets information
obj.drop = drag.flatten( ( ia.drop || [] ).slice() );
obj.available = drag.flatten( ( ia.droppable || [] ).slice() );
return obj;
},
// determine is the argument is an element or jquery instance
element: function( arg ){
if ( arg && ( arg.jquery || arg.nodeType == 1 ) )
return arg;
},
// flatten nested jquery objects and arrays into a single dimension array
flatten: function( arr ){
return $.map( arr, function( member ){
return member && member.jquery ? $.makeArray( member ) :
member && member.length ? drag.flatten( member ) : member;
});
},
// toggles text selection attributes ON (true) or OFF (false)
textselect: function( bool ){
$( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart )
.css("MozUserSelect", bool ? "" : "none" );
// .attr("unselectable", bool ? "off" : "on" )
document.unselectable = bool ? "off" : "on";
},
// suppress "selectstart" and "ondragstart" events
dontstart: function(){
return false;
},
// a callback instance contructor
callback: function(){}
};
// callback methods
drag.callback.prototype = {
update: function(){
if ( $special.drop && this.available.length )
$.each( this.available, function( i ){
$special.drop.locate( this, i );
});
}
};
// patch $.event.$dispatch to allow suppressing clicks
var $dispatch = $event.dispatch;
$event.dispatch = function( event ){
if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){
$.removeData( this, "suppress."+ event.type );
return;
}
return $dispatch.apply( this, arguments );
};
// event fix hooks for touch events...
var touchHooks =
$event.fixHooks.touchstart =
$event.fixHooks.touchmove =
$event.fixHooks.touchend =
$event.fixHooks.touchcancel = {
props: "clientX clientY pageX pageY screenX screenY".split( " " ),
filter: function( event, orig ) {
if ( orig ){
var touched = ( orig.touches && orig.touches[0] )
|| ( orig.changedTouches && orig.changedTouches[0] )
|| null;
// iOS webkit: touchstart, touchmove, touchend
if ( touched )
$.each( touchHooks.props, function( i, prop ){
event[ prop ] = touched[ prop ];
});
}
return event;
}
};
// share the same special event configuration with related events...
$special.draginit = $special.dragstart = $special.dragend = drag;
})( jQuery );

View File

@ -0,0 +1,302 @@
/*!
* jquery.event.drop - v 2.2
* Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
* Open Source MIT License - http://threedubmedia.com/code/license
*/
// Created: 2008-06-04
// Updated: 2012-05-21
// REQUIRES: jquery 1.7.x, event.drag 2.2
;(function($){ // secure $ jQuery alias
// Events: drop, dropstart, dropend
// add the jquery instance method
$.fn.drop = function( str, arg, opts ){
// figure out the event type
var type = typeof str == "string" ? str : "",
// figure out the event handler...
fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
// fix the event type
if ( type.indexOf("drop") !== 0 )
type = "drop"+ type;
// were options passed
opts = ( str == fn ? arg : opts ) || {};
// trigger or bind event handler
return fn ? this.bind( type, opts, fn ) : this.trigger( type );
};
// DROP MANAGEMENT UTILITY
// returns filtered drop target elements, caches their positions
$.drop = function( opts ){
opts = opts || {};
// safely set new options...
drop.multi = opts.multi === true ? Infinity :
opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi;
drop.delay = opts.delay || drop.delay;
drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance :
opts.tolerance === null ? null : drop.tolerance;
drop.mode = opts.mode || drop.mode || 'intersect';
};
// local refs (increase compression)
var $event = $.event,
$special = $event.special,
// configure the drop special event
drop = $.event.special.drop = {
// these are the default settings
multi: 1, // allow multiple drop winners per dragged element
delay: 20, // async timeout delay
mode: 'overlap', // drop tolerance mode
// internal cache
targets: [],
// the key name for stored drop data
datakey: "dropdata",
// prevent bubbling for better performance
noBubble: true,
// count bound related events
add: function( obj ){
// read the interaction data
var data = $.data( this, drop.datakey );
// count another realted event
data.related += 1;
},
// forget unbound related events
remove: function(){
$.data( this, drop.datakey ).related -= 1;
},
// configure the interactions
setup: function(){
// check for related events
if ( $.data( this, drop.datakey ) )
return;
// initialize the drop element data
var data = {
related: 0,
active: [],
anyactive: 0,
winner: 0,
location: {}
};
// store the drop data on the element
$.data( this, drop.datakey, data );
// store the drop target in internal cache
drop.targets.push( this );
},
// destroy the configure interaction
teardown: function(){
var data = $.data( this, drop.datakey ) || {};
// check for related events
if ( data.related )
return;
// remove the stored data
$.removeData( this, drop.datakey );
// reference the targeted element
var element = this;
// remove from the internal cache
drop.targets = $.grep( drop.targets, function( target ){
return ( target !== element );
});
},
// shared event handler
handler: function( event, dd ){
// local vars
var results, $targets;
// make sure the right data is available
if ( !dd )
return;
// handle various events
switch ( event.type ){
// draginit, from $.event.special.drag
case 'mousedown': // DROPINIT >>
case 'touchstart': // DROPINIT >>
// collect and assign the drop targets
$targets = $( drop.targets );
if ( typeof dd.drop == "string" )
$targets = $targets.filter( dd.drop );
// reset drop data winner properties
$targets.each(function(){
var data = $.data( this, drop.datakey );
data.active = [];
data.anyactive = 0;
data.winner = 0;
});
// set available target elements
dd.droppable = $targets;
// activate drop targets for the initial element being dragged
$special.drag.hijack( event, "dropinit", dd );
break;
// drag, from $.event.special.drag
case 'mousemove': // TOLERATE >>
case 'touchmove': // TOLERATE >>
drop.event = event; // store the mousemove event
if ( !drop.timer )
// monitor drop targets
drop.tolerate( dd );
break;
// dragend, from $.event.special.drag
case 'mouseup': // DROP >> DROPEND >>
case 'touchend': // DROP >> DROPEND >>
drop.timer = clearTimeout( drop.timer ); // delete timer
if ( dd.propagates ){
$special.drag.hijack( event, "drop", dd );
$special.drag.hijack( event, "dropend", dd );
}
break;
}
},
// returns the location positions of an element
locate: function( elem, index ){
var data = $.data( elem, drop.datakey ),
$elem = $( elem ),
posi = $elem.offset() || {},
height = $elem.outerHeight(),
width = $elem.outerWidth(),
location = {
elem: elem,
width: width,
height: height,
top: posi.top,
left: posi.left,
right: posi.left + width,
bottom: posi.top + height
};
// drag elements might not have dropdata
if ( data ){
data.location = location;
data.index = index;
data.elem = elem;
}
return location;
},
// test the location positions of an element against another OR an X,Y coord
contains: function( target, test ){ // target { location } contains test [x,y] or { location }
return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right
&& ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom );
},
// stored tolerance modes
modes: { // fn scope: "$.event.special.drop" object
// target with mouse wins, else target with most overlap wins
'intersect': function( event, proxy, target ){
return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor
1e9 : this.modes.overlap.apply( this, arguments ); // check overlap
},
// target with most overlap wins
'overlap': function( event, proxy, target ){
// calculate the area of overlap...
return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) )
* Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) );
},
// proxy is completely contained within target bounds
'fit': function( event, proxy, target ){
return this.contains( target, proxy ) ? 1 : 0;
},
// center of the proxy is contained within target bounds
'middle': function( event, proxy, target ){
return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0;
}
},
// sort drop target cache by by winner (dsc), then index (asc)
sort: function( a, b ){
return ( b.winner - a.winner ) || ( a.index - b.index );
},
// async, recursive tolerance execution
tolerate: function( dd ){
// declare local refs
var i, drp, drg, data, arr, len, elem,
// interaction iteration variables
x = 0, ia, end = dd.interactions.length,
// determine the mouse coords
xy = [ drop.event.pageX, drop.event.pageY ],
// custom or stored tolerance fn
tolerance = drop.tolerance || drop.modes[ drop.mode ];
// go through each passed interaction...
do if ( ia = dd.interactions[x] ){
// check valid interaction
if ( !ia )
return;
// initialize or clear the drop data
ia.drop = [];
// holds the drop elements
arr = [];
len = ia.droppable.length;
// determine the proxy location, if needed
if ( tolerance )
drg = drop.locate( ia.proxy );
// reset the loop
i = 0;
// loop each stored drop target
do if ( elem = ia.droppable[i] ){
data = $.data( elem, drop.datakey );
drp = data.location;
if ( !drp ) continue;
// find a winner: tolerance function is defined, call it
data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp )
// mouse position is always the fallback
: drop.contains( drp, xy ) ? 1 : 0;
arr.push( data );
} while ( ++i < len ); // loop
// sort the drop targets
arr.sort( drop.sort );
// reset the loop
i = 0;
// loop through all of the targets again
do if ( data = arr[ i ] ){
// winners...
if ( data.winner && ia.drop.length < drop.multi ){
// new winner... dropstart
if ( !data.active[x] && !data.anyactive ){
// check to make sure that this is not prevented
if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){
data.active[x] = 1;
data.anyactive += 1;
}
// if false, it is not a winner
else
data.winner = 0;
}
// if it is still a winner
if ( data.winner )
ia.drop.push( data.elem );
}
// losers...
else if ( data.active[x] && data.anyactive == 1 ){
// former winner... dropend
$special.drag.hijack( drop.event, "dropend", dd, x, data.elem );
data.active[x] = 0;
data.anyactive -= 1;
}
} while ( ++i < len ); // loop
} while ( ++x < end ) // loop
// check if the mouse is still moving or is idle
if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY )
delete drop.timer; // idle, don't recurse
else // recurse
drop.timer = setTimeout(function(){
drop.tolerate( dd );
}, drop.delay );
// remember event, to compare idleness
drop.last = drop.event;
}
};
// share the same special event configuration with related events...
$special.dropinit = $special.dropstart = $special.dropend = drop;
})(jQuery); // confine scope

View File

@ -0,0 +1,83 @@
(function ($) {
// Register namespace
$.extend(true, window, {
"Slick": {
"AutoTooltips": AutoTooltips
}
});
/**
* AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content.
* @constructor
* @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells
* @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells
* @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip
*/
function AutoTooltips(options) {
var _grid;
var _self = this;
var _defaults = {
enableForCells: true,
enableForHeaderCells: false,
maxToolTipLength: null
};
/**
* Initialize plugin.
*/
function init(grid) {
options = $.extend(true, {}, _defaults, options);
_grid = grid;
if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter);
if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter);
}
/**
* Destroy plugin.
*/
function destroy() {
if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter);
if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter);
}
/**
* Handle mouse entering grid cell to add/remove tooltip.
* @param {jQuery.Event} e - The event
*/
function handleMouseEnter(e) {
var cell = _grid.getCellFromEvent(e);
if (cell) {
var $node = $(_grid.getCellNode(cell.row, cell.cell));
var text;
if ($node.innerWidth() < $node[0].scrollWidth) {
text = $.trim($node.text());
if (options.maxToolTipLength && text.length > options.maxToolTipLength) {
text = text.substr(0, options.maxToolTipLength - 3) + "...";
}
} else {
text = "";
}
$node.attr("title", text);
}
}
/**
* Handle mouse entering header cell to add/remove tooltip.
* @param {jQuery.Event} e - The event
* @param {object} args.column - The column definition
*/
function handleHeaderMouseEnter(e, args) {
var column = args.column,
$node = $(e.target).closest(".slick-header-column");
if (!column.toolTip) {
$node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : "");
}
}
// Public API
$.extend(this, {
"init": init,
"destroy": destroy
});
}
})(jQuery);

View File

@ -0,0 +1,86 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"CellCopyManager": CellCopyManager
}
});
function CellCopyManager() {
var _grid;
var _self = this;
var _copiedRanges;
function init(grid) {
_grid = grid;
_grid.onKeyDown.subscribe(handleKeyDown);
}
function destroy() {
_grid.onKeyDown.unsubscribe(handleKeyDown);
}
function handleKeyDown(e, args) {
var ranges;
if (!_grid.getEditorLock().isActive()) {
if (e.which == $.ui.keyCode.ESCAPE) {
if (_copiedRanges) {
e.preventDefault();
clearCopySelection();
_self.onCopyCancelled.notify({ranges: _copiedRanges});
_copiedRanges = null;
}
}
if (e.which == 67 && (e.ctrlKey || e.metaKey)) {
ranges = _grid.getSelectionModel().getSelectedRanges();
if (ranges.length != 0) {
e.preventDefault();
_copiedRanges = ranges;
markCopySelection(ranges);
_self.onCopyCells.notify({ranges: ranges});
}
}
if (e.which == 86 && (e.ctrlKey || e.metaKey)) {
if (_copiedRanges) {
e.preventDefault();
clearCopySelection();
ranges = _grid.getSelectionModel().getSelectedRanges();
_self.onPasteCells.notify({from: _copiedRanges, to: ranges});
_copiedRanges = null;
}
}
}
}
function markCopySelection(ranges) {
var columns = _grid.getColumns();
var hash = {};
for (var i = 0; i < ranges.length; i++) {
for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
hash[j] = {};
for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {
hash[j][columns[k].id] = "copied";
}
}
}
_grid.setCellCssStyles("copy-manager", hash);
}
function clearCopySelection() {
_grid.removeCellCssStyles("copy-manager");
}
$.extend(this, {
"init": init,
"destroy": destroy,
"clearCopySelection": clearCopySelection,
"onCopyCells": new Slick.Event(),
"onCopyCancelled": new Slick.Event(),
"onPasteCells": new Slick.Event()
});
}
})(jQuery);

View File

@ -0,0 +1,66 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"CellRangeDecorator": CellRangeDecorator
}
});
/***
* Displays an overlay on top of a given cell range.
*
* TODO:
* Currently, it blocks mouse events to DOM nodes behind it.
* Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding.
* Could also construct the borders separately using 4 individual DIVs.
*
* @param {Grid} grid
* @param {Object} options
*/
function CellRangeDecorator(grid, options) {
var _elem;
var _defaults = {
selectionCssClass: 'slick-range-decorator',
selectionCss: {
"zIndex": "9999",
"border": "2px dashed red"
}
};
options = $.extend(true, {}, _defaults, options);
function show(range) {
if (!_elem) {
_elem = $("<div></div>", {css: options.selectionCss})
.addClass(options.selectionCssClass)
.css("position", "absolute")
.appendTo(grid.getCanvasNode());
}
var from = grid.getCellNodeBox(range.fromRow, range.fromCell);
var to = grid.getCellNodeBox(range.toRow, range.toCell);
_elem.css({
top: from.top - 1,
left: from.left - 1,
height: to.bottom - from.top - 2,
width: to.right - from.left - 2
});
return _elem;
}
function hide() {
if (_elem) {
_elem.remove();
_elem = null;
}
}
$.extend(this, {
"show": show,
"hide": hide
});
}
})(jQuery);

View File

@ -0,0 +1,113 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"CellRangeSelector": CellRangeSelector
}
});
function CellRangeSelector(options) {
var _grid;
var _canvas;
var _dragging;
var _decorator;
var _self = this;
var _handler = new Slick.EventHandler();
var _defaults = {
selectionCss: {
"border": "2px dashed blue"
}
};
function init(grid) {
options = $.extend(true, {}, _defaults, options);
_decorator = new Slick.CellRangeDecorator(grid, options);
_grid = grid;
_canvas = _grid.getCanvasNode();
_handler
.subscribe(_grid.onDragInit, handleDragInit)
.subscribe(_grid.onDragStart, handleDragStart)
.subscribe(_grid.onDrag, handleDrag)
.subscribe(_grid.onDragEnd, handleDragEnd);
}
function destroy() {
_handler.unsubscribeAll();
}
function handleDragInit(e, dd) {
// prevent the grid from cancelling drag'n'drop by default
e.stopImmediatePropagation();
}
function handleDragStart(e, dd) {
var cell = _grid.getCellFromEvent(e);
if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {
if (_grid.canCellBeSelected(cell.row, cell.cell)) {
_dragging = true;
e.stopImmediatePropagation();
}
}
if (!_dragging) {
return;
}
_grid.focus();
var start = _grid.getCellFromPoint(
dd.startX - $(_canvas).offset().left,
dd.startY - $(_canvas).offset().top);
dd.range = {start: start, end: {}};
return _decorator.show(new Slick.Range(start.row, start.cell));
}
function handleDrag(e, dd) {
if (!_dragging) {
return;
}
e.stopImmediatePropagation();
var end = _grid.getCellFromPoint(
e.pageX - $(_canvas).offset().left,
e.pageY - $(_canvas).offset().top);
if (!_grid.canCellBeSelected(end.row, end.cell)) {
return;
}
dd.range.end = end;
_decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
}
function handleDragEnd(e, dd) {
if (!_dragging) {
return;
}
_dragging = false;
e.stopImmediatePropagation();
_decorator.hide();
_self.onCellRangeSelected.notify({
range: new Slick.Range(
dd.range.start.row,
dd.range.start.cell,
dd.range.end.row,
dd.range.end.cell
)
});
}
$.extend(this, {
"init": init,
"destroy": destroy,
"onBeforeCellRangeSelected": new Slick.Event(),
"onCellRangeSelected": new Slick.Event()
});
}
})(jQuery);

View File

@ -0,0 +1,154 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"CellSelectionModel": CellSelectionModel
}
});
function CellSelectionModel(options) {
var _grid;
var _canvas;
var _ranges = [];
var _self = this;
var _selector = new Slick.CellRangeSelector({
"selectionCss": {
"border": "2px solid black"
}
});
var _options;
var _defaults = {
selectActiveCell: true
};
function init(grid) {
_options = $.extend(true, {}, _defaults, options);
_grid = grid;
_canvas = _grid.getCanvasNode();
_grid.onActiveCellChanged.subscribe(handleActiveCellChange);
_grid.onKeyDown.subscribe(handleKeyDown);
grid.registerPlugin(_selector);
_selector.onCellRangeSelected.subscribe(handleCellRangeSelected);
_selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);
}
function destroy() {
_grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);
_grid.onKeyDown.unsubscribe(handleKeyDown);
_selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);
_selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);
_grid.unregisterPlugin(_selector);
}
function removeInvalidRanges(ranges) {
var result = [];
for (var i = 0; i < ranges.length; i++) {
var r = ranges[i];
if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) {
result.push(r);
}
}
return result;
}
function setSelectedRanges(ranges) {
_ranges = removeInvalidRanges(ranges);
_self.onSelectedRangesChanged.notify(_ranges);
}
function getSelectedRanges() {
return _ranges;
}
function handleBeforeCellRangeSelected(e, args) {
if (_grid.getEditorLock().isActive()) {
e.stopPropagation();
return false;
}
}
function handleCellRangeSelected(e, args) {
setSelectedRanges([args.range]);
}
function handleActiveCellChange(e, args) {
if (_options.selectActiveCell && args.row != null && args.cell != null) {
setSelectedRanges([new Slick.Range(args.row, args.cell)]);
}
}
function handleKeyDown(e) {
/***
* Кey codes
* 37 left
* 38 up
* 39 right
* 40 down
*/
var ranges, last;
var active = _grid.getActiveCell();
if ( active && e.shiftKey && !e.ctrlKey && !e.altKey &&
(e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) {
ranges = getSelectedRanges();
if (!ranges.length)
ranges.push(new Slick.Range(active.row, active.cell));
// keyboard can work with last range only
last = ranges.pop();
// can't handle selection out of active cell
if (!last.contains(active.row, active.cell))
last = new Slick.Range(active.row, active.cell);
var dRow = last.toRow - last.fromRow,
dCell = last.toCell - last.fromCell,
// walking direction
dirRow = active.row == last.fromRow ? 1 : -1,
dirCell = active.cell == last.fromCell ? 1 : -1;
if (e.which == 37) {
dCell -= dirCell;
} else if (e.which == 39) {
dCell += dirCell ;
} else if (e.which == 38) {
dRow -= dirRow;
} else if (e.which == 40) {
dRow += dirRow;
}
// define new selection range
var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell);
if (removeInvalidRanges([new_last]).length) {
ranges.push(new_last);
var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;
var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;
_grid.scrollRowIntoView(viewRow);
_grid.scrollCellIntoView(viewRow, viewCell);
}
else
ranges.push(last);
setSelectedRanges(ranges);
e.preventDefault();
e.stopPropagation();
}
}
$.extend(this, {
"getSelectedRanges": getSelectedRanges,
"setSelectedRanges": setSelectedRanges,
"init": init,
"destroy": destroy,
"onSelectedRangesChanged": new Slick.Event()
});
}
})(jQuery);

View File

@ -0,0 +1,153 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"CheckboxSelectColumn": CheckboxSelectColumn
}
});
function CheckboxSelectColumn(options) {
var _grid;
var _self = this;
var _handler = new Slick.EventHandler();
var _selectedRowsLookup = {};
var _defaults = {
columnId: "_checkbox_selector",
cssClass: null,
toolTip: "Select/Deselect All",
width: 30
};
var _options = $.extend(true, {}, _defaults, options);
function init(grid) {
_grid = grid;
_handler
.subscribe(_grid.onSelectedRowsChanged, handleSelectedRowsChanged)
.subscribe(_grid.onClick, handleClick)
.subscribe(_grid.onHeaderClick, handleHeaderClick)
.subscribe(_grid.onKeyDown, handleKeyDown);
}
function destroy() {
_handler.unsubscribeAll();
}
function handleSelectedRowsChanged(e, args) {
var selectedRows = _grid.getSelectedRows();
var lookup = {}, row, i;
for (i = 0; i < selectedRows.length; i++) {
row = selectedRows[i];
lookup[row] = true;
if (lookup[row] !== _selectedRowsLookup[row]) {
_grid.invalidateRow(row);
delete _selectedRowsLookup[row];
}
}
for (i in _selectedRowsLookup) {
_grid.invalidateRow(i);
}
_selectedRowsLookup = lookup;
_grid.render();
if (selectedRows.length && selectedRows.length == _grid.getDataLength()) {
_grid.updateColumnHeader(_options.columnId, "<input type='checkbox' checked='checked'>", _options.toolTip);
} else {
_grid.updateColumnHeader(_options.columnId, "<input type='checkbox'>", _options.toolTip);
}
}
function handleKeyDown(e, args) {
if (e.which == 32) {
if (_grid.getColumns()[args.cell].id === _options.columnId) {
// if editing, try to commit
if (!_grid.getEditorLock().isActive() || _grid.getEditorLock().commitCurrentEdit()) {
toggleRowSelection(args.row);
}
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
function handleClick(e, args) {
// clicking on a row select checkbox
if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) {
// if editing, try to commit
if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
toggleRowSelection(args.row);
e.stopPropagation();
e.stopImmediatePropagation();
}
}
function toggleRowSelection(row) {
if (_selectedRowsLookup[row]) {
_grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) {
return n != row
}));
} else {
_grid.setSelectedRows(_grid.getSelectedRows().concat(row));
}
}
function handleHeaderClick(e, args) {
if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
// if editing, try to commit
if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
if ($(e.target).is(":checked")) {
var rows = [];
for (var i = 0; i < _grid.getDataLength(); i++) {
rows.push(i);
}
_grid.setSelectedRows(rows);
} else {
_grid.setSelectedRows([]);
}
e.stopPropagation();
e.stopImmediatePropagation();
}
}
function getColumnDefinition() {
return {
id: _options.columnId,
name: "<input type='checkbox'>",
toolTip: _options.toolTip,
field: "sel",
width: _options.width,
resizable: false,
sortable: false,
cssClass: _options.cssClass,
formatter: checkboxSelectionFormatter
};
}
function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) {
if (dataContext) {
return _selectedRowsLookup[row]
? "<input type='checkbox' checked='checked'>"
: "<input type='checkbox'>";
}
return null;
}
$.extend(this, {
"init": init,
"destroy": destroy,
"getColumnDefinition": getColumnDefinition
});
}
})(jQuery);

View File

@ -0,0 +1,39 @@
.slick-column-name,
.slick-sort-indicator {
/**
* This makes all "float:right" elements after it that spill over to the next line
* display way below the lower boundary of the column thus hiding them.
*/
display: inline-block;
float: left;
margin-bottom: 100px;
}
.slick-header-button {
display: inline-block;
float: right;
vertical-align: top;
margin: 1px;
/**
* This makes all "float:right" elements after it that spill over to the next line
* display way below the lower boundary of the column thus hiding them.
*/
margin-bottom: 100px;
height: 15px;
width: 15px;
background-repeat: no-repeat;
background-position: center center;
cursor: pointer;
}
.slick-header-button-hidden {
width: 0;
-webkit-transition: 0.2s width;
-ms-transition: 0.2s width;
transition: 0.2s width;
}
.slick-header-column:hover > .slick-header-button {
width: 15px;
}

View File

@ -0,0 +1,177 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"Plugins": {
"HeaderButtons": HeaderButtons
}
}
});
/***
* A plugin to add custom buttons to column headers.
*
* USAGE:
*
* Add the plugin .js & .css files and register it with the grid.
*
* To specify a custom button in a column header, extend the column definition like so:
*
* var columns = [
* {
* id: 'myColumn',
* name: 'My column',
*
* // This is the relevant part
* header: {
* buttons: [
* {
* // button options
* },
* {
* // button options
* }
* ]
* }
* }
* ];
*
* Available button options:
* cssClass: CSS class to add to the button.
* image: Relative button image path.
* tooltip: Button tooltip.
* showOnHover: Only show the button on hover.
* handler: Button click handler.
* command: A command identifier to be passed to the onCommand event handlers.
*
* The plugin exposes the following events:
* onCommand: Fired on button click for buttons with 'command' specified.
* Event args:
* grid: Reference to the grid.
* column: Column definition.
* command: Button command identified.
* button: Button options. Note that you can change the button options in your
* event handler, and the column header will be automatically updated to
* reflect them. This is useful if you want to implement something like a
* toggle button.
*
*
* @param options {Object} Options:
* buttonCssClass: a CSS class to use for buttons (default 'slick-header-button')
* @class Slick.Plugins.HeaderButtons
* @constructor
*/
function HeaderButtons(options) {
var _grid;
var _self = this;
var _handler = new Slick.EventHandler();
var _defaults = {
buttonCssClass: "slick-header-button"
};
function init(grid) {
options = $.extend(true, {}, _defaults, options);
_grid = grid;
_handler
.subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
.subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
// Force the grid to re-render the header now that the events are hooked up.
_grid.setColumns(_grid.getColumns());
}
function destroy() {
_handler.unsubscribeAll();
}
function handleHeaderCellRendered(e, args) {
var column = args.column;
if (column.header && column.header.buttons) {
// Append buttons in reverse order since they are floated to the right.
var i = column.header.buttons.length;
while (i--) {
var button = column.header.buttons[i];
var btn = $("<div></div>")
.addClass(options.buttonCssClass)
.data("column", column)
.data("button", button);
if (button.showOnHover) {
btn.addClass("slick-header-button-hidden");
}
if (button.image) {
btn.css("backgroundImage", "url(" + button.image + ")");
}
if (button.cssClass) {
btn.addClass(button.cssClass);
}
if (button.tooltip) {
btn.attr("title", button.tooltip);
}
if (button.command) {
btn.data("command", button.command);
}
if (button.handler) {
btn.bind("click", button.handler);
}
btn
.bind("click", handleButtonClick)
.appendTo(args.node);
}
}
}
function handleBeforeHeaderCellDestroy(e, args) {
var column = args.column;
if (column.header && column.header.buttons) {
// Removing buttons via jQuery will also clean up any event handlers and data.
// NOTE: If you attach event handlers directly or using a different framework,
// you must also clean them up here to avoid memory leaks.
$(args.node).find("." + options.buttonCssClass).remove();
}
}
function handleButtonClick(e) {
var command = $(this).data("command");
var columnDef = $(this).data("column");
var button = $(this).data("button");
if (command != null) {
_self.onCommand.notify({
"grid": _grid,
"column": columnDef,
"command": command,
"button": button
}, e, _self);
// Update the header in case the user updated the button definition in the handler.
_grid.updateColumnHeader(columnDef.id);
}
// Stop propagation so that it doesn't register as a header click event.
e.preventDefault();
e.stopPropagation();
}
$.extend(this, {
"init": init,
"destroy": destroy,
"onCommand": new Slick.Event()
});
}
})(jQuery);

View File

@ -0,0 +1,59 @@
/* Menu button */
.slick-header-menubutton {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 14px;
background-repeat: no-repeat;
background-position: left center;
background-image: url(../images/down.gif);
cursor: pointer;
display: none;
border-left: thin ridge silver;
}
.slick-header-column:hover > .slick-header-menubutton,
.slick-header-column-active .slick-header-menubutton {
display: inline-block;
}
/* Menu */
.slick-header-menu {
position: absolute;
display: inline-block;
margin: 0;
padding: 2px;
cursor: default;
}
/* Menu items */
.slick-header-menuitem {
list-style: none;
margin: 0;
padding: 0;
cursor: pointer;
}
.slick-header-menuicon {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
margin-right: 4px;
background-repeat: no-repeat;
background-position: center center;
}
.slick-header-menucontent {
display: inline-block;
vertical-align: middle;
}
/* Disabled */
.slick-header-menuitem-disabled {
color: silver;
}

View File

@ -0,0 +1,275 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"Plugins": {
"HeaderMenu": HeaderMenu
}
}
});
/***
* A plugin to add drop-down menus to column headers.
*
* USAGE:
*
* Add the plugin .js & .css files and register it with the grid.
*
* To specify a menu in a column header, extend the column definition like so:
*
* var columns = [
* {
* id: 'myColumn',
* name: 'My column',
*
* // This is the relevant part
* header: {
* menu: {
* items: [
* {
* // menu item options
* },
* {
* // menu item options
* }
* ]
* }
* }
* }
* ];
*
*
* Available menu options:
* tooltip: Menu button tooltip.
*
*
* Available menu item options:
* title: Menu item text.
* disabled: Whether the item is disabled.
* tooltip: Item tooltip.
* command: A command identifier to be passed to the onCommand event handlers.
* iconCssClass: A CSS class to be added to the menu item icon.
* iconImage: A url to the icon image.
*
*
* The plugin exposes the following events:
* onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.
* Event args:
* grid: Reference to the grid.
* column: Column definition.
* menu: Menu options. Note that you can change the menu items here.
*
* onCommand: Fired on menu item click for buttons with 'command' specified.
* Event args:
* grid: Reference to the grid.
* column: Column definition.
* command: Button command identified.
* button: Button options. Note that you can change the button options in your
* event handler, and the column header will be automatically updated to
* reflect them. This is useful if you want to implement something like a
* toggle button.
*
*
* @param options {Object} Options:
* buttonCssClass: an extra CSS class to add to the menu button
* buttonImage: a url to the menu button image (default '../images/down.gif')
* @class Slick.Plugins.HeaderButtons
* @constructor
*/
function HeaderMenu(options) {
var _grid;
var _self = this;
var _handler = new Slick.EventHandler();
var _defaults = {
buttonCssClass: null,
buttonImage: null
};
var $menu;
var $activeHeaderColumn;
function init(grid) {
options = $.extend(true, {}, _defaults, options);
_grid = grid;
_handler
.subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
.subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
// Force the grid to re-render the header now that the events are hooked up.
_grid.setColumns(_grid.getColumns());
// Hide the menu on outside click.
$(document.body).bind("mousedown", handleBodyMouseDown);
}
function destroy() {
_handler.unsubscribeAll();
$(document.body).unbind("mousedown", handleBodyMouseDown);
}
function handleBodyMouseDown(e) {
if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
hideMenu();
}
}
function hideMenu() {
if ($menu) {
$menu.remove();
$menu = null;
$activeHeaderColumn
.removeClass("slick-header-column-active");
}
}
function handleHeaderCellRendered(e, args) {
var column = args.column;
var menu = column.header && column.header.menu;
if (menu) {
var $el = $("<div></div>")
.addClass("slick-header-menubutton")
.data("column", column)
.data("menu", menu);
if (options.buttonCssClass) {
$el.addClass(options.buttonCssClass);
}
if (options.buttonImage) {
$el.css("background-image", "url(" + options.buttonImage + ")");
}
if (menu.tooltip) {
$el.attr("title", menu.tooltip);
}
$el
.bind("click", showMenu)
.appendTo(args.node);
}
}
function handleBeforeHeaderCellDestroy(e, args) {
var column = args.column;
if (column.header && column.header.menu) {
$(args.node).find(".slick-header-menubutton").remove();
}
}
function showMenu(e) {
var $menuButton = $(this);
var menu = $menuButton.data("menu");
var columnDef = $menuButton.data("column");
// Let the user modify the menu or cancel altogether,
// or provide alternative menu implementation.
if (_self.onBeforeMenuShow.notify({
"grid": _grid,
"column": columnDef,
"menu": menu
}, e, _self) == false) {
return;
}
if (!$menu) {
$menu = $("<div class='slick-header-menu'></div>")
.appendTo(_grid.getContainerNode());
}
$menu.empty();
// Construct the menu items.
for (var i = 0; i < menu.items.length; i++) {
var item = menu.items[i];
var $li = $("<div class='slick-header-menuitem'></div>")
.data("command", item.command || '')
.data("column", columnDef)
.data("item", item)
.bind("click", handleMenuItemClick)
.appendTo($menu);
if (item.disabled) {
$li.addClass("slick-header-menuitem-disabled");
}
if (item.tooltip) {
$li.attr("title", item.tooltip);
}
var $icon = $("<div class='slick-header-menuicon'></div>")
.appendTo($li);
if (item.iconCssClass) {
$icon.addClass(item.iconCssClass);
}
if (item.iconImage) {
$icon.css("background-image", "url(" + item.iconImage + ")");
}
$("<span class='slick-header-menucontent'></span>")
.text(item.title)
.appendTo($li);
}
// Position the menu.
$menu
.offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left });
// Mark the header as active to keep the highlighting.
$activeHeaderColumn = $menuButton.closest(".slick-header-column");
$activeHeaderColumn
.addClass("slick-header-column-active");
// Stop propagation so that it doesn't register as a header click event.
e.preventDefault();
e.stopPropagation();
}
function handleMenuItemClick(e) {
var command = $(this).data("command");
var columnDef = $(this).data("column");
var item = $(this).data("item");
if (item.disabled) {
return;
}
hideMenu();
if (command != null && command != '') {
_self.onCommand.notify({
"grid": _grid,
"column": columnDef,
"command": command,
"item": item
}, e, _self);
}
// Stop propagation so that it doesn't register as a header click event.
e.preventDefault();
e.stopPropagation();
}
$.extend(this, {
"init": init,
"destroy": destroy,
"onBeforeMenuShow": new Slick.Event(),
"onCommand": new Slick.Event()
});
}
})(jQuery);

View File

@ -0,0 +1,138 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"RowMoveManager": RowMoveManager
}
});
function RowMoveManager(options) {
var _grid;
var _canvas;
var _dragging;
var _self = this;
var _handler = new Slick.EventHandler();
var _defaults = {
cancelEditOnDrag: false
};
function init(grid) {
options = $.extend(true, {}, _defaults, options);
_grid = grid;
_canvas = _grid.getCanvasNode();
_handler
.subscribe(_grid.onDragInit, handleDragInit)
.subscribe(_grid.onDragStart, handleDragStart)
.subscribe(_grid.onDrag, handleDrag)
.subscribe(_grid.onDragEnd, handleDragEnd);
}
function destroy() {
_handler.unsubscribeAll();
}
function handleDragInit(e, dd) {
// prevent the grid from cancelling drag'n'drop by default
e.stopImmediatePropagation();
}
function handleDragStart(e, dd) {
var cell = _grid.getCellFromEvent(e);
if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) {
_grid.getEditorLock().cancelCurrentEdit();
}
if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) {
return false;
}
_dragging = true;
e.stopImmediatePropagation();
var selectedRows = _grid.getSelectedRows();
if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) {
selectedRows = [cell.row];
_grid.setSelectedRows(selectedRows);
}
var rowHeight = _grid.getOptions().rowHeight;
dd.selectedRows = selectedRows;
dd.selectionProxy = $("<div class='slick-reorder-proxy'/>")
.css("position", "absolute")
.css("zIndex", "99999")
.css("width", $(_canvas).innerWidth())
.css("height", rowHeight * selectedRows.length)
.appendTo(_canvas);
dd.guide = $("<div class='slick-reorder-guide'/>")
.css("position", "absolute")
.css("zIndex", "99998")
.css("width", $(_canvas).innerWidth())
.css("top", -1000)
.appendTo(_canvas);
dd.insertBefore = -1;
}
function handleDrag(e, dd) {
if (!_dragging) {
return;
}
e.stopImmediatePropagation();
var top = e.pageY - $(_canvas).offset().top;
dd.selectionProxy.css("top", top - 5);
var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength()));
if (insertBefore !== dd.insertBefore) {
var eventData = {
"rows": dd.selectedRows,
"insertBefore": insertBefore
};
if (_self.onBeforeMoveRows.notify(eventData) === false) {
dd.guide.css("top", -1000);
dd.canMove = false;
} else {
dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight);
dd.canMove = true;
}
dd.insertBefore = insertBefore;
}
}
function handleDragEnd(e, dd) {
if (!_dragging) {
return;
}
_dragging = false;
e.stopImmediatePropagation();
dd.guide.remove();
dd.selectionProxy.remove();
if (dd.canMove) {
var eventData = {
"rows": dd.selectedRows,
"insertBefore": dd.insertBefore
};
// TODO: _grid.remapCellCssClasses ?
_self.onMoveRows.notify(eventData);
}
}
$.extend(this, {
"onBeforeMoveRows": new Slick.Event(),
"onMoveRows": new Slick.Event(),
"init": init,
"destroy": destroy
});
}
})(jQuery);

View File

@ -0,0 +1,187 @@
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"RowSelectionModel": RowSelectionModel
}
});
function RowSelectionModel(options) {
var _grid;
var _ranges = [];
var _self = this;
var _handler = new Slick.EventHandler();
var _inHandler;
var _options;
var _defaults = {
selectActiveRow: true
};
function init(grid) {
_options = $.extend(true, {}, _defaults, options);
_grid = grid;
_handler.subscribe(_grid.onActiveCellChanged,
wrapHandler(handleActiveCellChange));
_handler.subscribe(_grid.onKeyDown,
wrapHandler(handleKeyDown));
_handler.subscribe(_grid.onClick,
wrapHandler(handleClick));
}
function destroy() {
_handler.unsubscribeAll();
}
function wrapHandler(handler) {
return function () {
if (!_inHandler) {
_inHandler = true;
handler.apply(this, arguments);
_inHandler = false;
}
};
}
function rangesToRows(ranges) {
var rows = [];
for (var i = 0; i < ranges.length; i++) {
for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
function rowsToRanges(rows) {
var ranges = [];
var lastCell = _grid.getColumns().length - 1;
for (var i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
function getRowsRange(from, to) {
var i, rows = [];
for (i = from; i <= to; i++) {
rows.push(i);
}
for (i = to; i < from; i++) {
rows.push(i);
}
return rows;
}
function getSelectedRows() {
return rangesToRows(_ranges);
}
function setSelectedRows(rows) {
setSelectedRanges(rowsToRanges(rows));
}
function setSelectedRanges(ranges) {
_ranges = ranges;
_self.onSelectedRangesChanged.notify(_ranges);
}
function getSelectedRanges() {
return _ranges;
}
function handleActiveCellChange(e, data) {
if (_options.selectActiveRow && data.row != null) {
setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]);
}
}
function handleKeyDown(e) {
var activeRow = _grid.getActiveCell();
if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) {
var selectedRows = getSelectedRows();
selectedRows.sort(function (x, y) {
return x - y
});
if (!selectedRows.length) {
selectedRows = [activeRow.row];
}
var top = selectedRows[0];
var bottom = selectedRows[selectedRows.length - 1];
var active;
if (e.which == 40) {
active = activeRow.row < bottom || top == bottom ? ++bottom : ++top;
} else {
active = activeRow.row < bottom ? --bottom : --top;
}
if (active >= 0 && active < _grid.getDataLength()) {
_grid.scrollRowIntoView(active);
_ranges = rowsToRanges(getRowsRange(top, bottom));
setSelectedRanges(_ranges);
}
e.preventDefault();
e.stopPropagation();
}
}
function handleClick(e) {
var cell = _grid.getCellFromEvent(e);
if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!_grid.getOptions().multiSelect || (
!e.ctrlKey && !e.shiftKey && !e.metaKey)) {
return false;
}
var selection = rangesToRows(_ranges);
var idx = $.inArray(cell.row, selection);
if (idx === -1 && (e.ctrlKey || e.metaKey)) {
selection.push(cell.row);
_grid.setActiveCell(cell.row, cell.cell);
} else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {
selection = $.grep(selection, function (o, i) {
return (o !== cell.row);
});
_grid.setActiveCell(cell.row, cell.cell);
} else if (selection.length && e.shiftKey) {
var last = selection.pop();
var from = Math.min(cell.row, last);
var to = Math.max(cell.row, last);
selection = [];
for (var i = from; i <= to; i++) {
if (i !== last) {
selection.push(i);
}
}
selection.push(last);
_grid.setActiveCell(cell.row, cell.cell);
}
_ranges = rowsToRanges(selection);
setSelectedRanges(_ranges);
e.stopImmediatePropagation();
return true;
}
$.extend(this, {
"getSelectedRows": getSelectedRows,
"setSelectedRows": setSelectedRows,
"getSelectedRanges": getSelectedRanges,
"setSelectedRanges": setSelectedRanges,
"init": init,
"destroy": destroy,
"onSelectedRangesChanged": new Slick.Event()
});
}
})(jQuery);

467
vendor/slickgrid/2.0.1/slick.core.js vendored Normal file
View File

@ -0,0 +1,467 @@
/***
* Contains core SlickGrid classes.
* @module Core
* @namespace Slick
*/
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"Event": Event,
"EventData": EventData,
"EventHandler": EventHandler,
"Range": Range,
"NonDataRow": NonDataItem,
"Group": Group,
"GroupTotals": GroupTotals,
"EditorLock": EditorLock,
/***
* A global singleton editor lock.
* @class GlobalEditorLock
* @static
* @constructor
*/
"GlobalEditorLock": new EditorLock()
}
});
/***
* An event object for passing data to event handlers and letting them control propagation.
* <p>This is pretty much identical to how W3C and jQuery implement events.</p>
* @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.
* <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code>
* object the event was fired with.<p>
* @method subscribe
* @param fn {Function} Event handler.
*/
this.subscribe = function (fn) {
handlers.push(fn);
};
/***
* Removes an event handler added with <code>subscribe(fn)</code>.
* @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 <code>EventData</code> 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 <code>Event</code> 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);
return this; // allow chaining
};
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;
}
}
return this; // allow chaining
};
this.unsubscribeAll = function () {
var i = handlers.length;
while (i--) {
handlers[i].event.unsubscribe(handlers[i].handler);
}
handlers = [];
return this; // allow chaining
}
}
/***
* 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 <code>fromRow</code>.
* @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>.
*/
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;
/**
* Grouping level, starting with 0.
* @property level
* @type {Number}
*/
this.level = 0;
/***
* 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;
/**
* Rows that are part of the group.
* @property rows
* @type {Array}
*/
this.rows = [];
/**
* Sub-groups that are part of the group.
* @property groups
* @type {Array}
*/
this.groups = null;
/**
* A unique key used to identify the group. This key can be used in calls to DataView
* collapseGroup() or expandGroup().
* @property groupingKey
* @type {Object}
*/
this.groupingKey = 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 &&
this.title === group.title;
};
/***
* 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;
/***
* Whether the totals have been fully initialized / calculated.
* Will be set to false for lazy-calculated group totals.
* @param initialized
* @type {Boolean}
*/
this.initialized = false;
}
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);

512
vendor/slickgrid/2.0.1/slick.editors.js vendored Normal file
View File

@ -0,0 +1,512 @@
/***
* Contains basic SlickGrid editors.
* @module Editors
* @namespace Slick
*/
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"Editors": {
"Text": TextEditor,
"Integer": IntegerEditor,
"Date": DateEditor,
"YesNoSelect": YesNoSelectEditor,
"Checkbox": CheckboxEditor,
"PercentComplete": PercentCompleteEditor,
"LongText": LongTextEditor
}
}
});
function TextEditor(args) {
var $input;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<INPUT type=text class='editor-text' />")
.appendTo(args.container)
.bind("keydown.nav", function (e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
})
.focus()
.select();
};
this.destroy = function () {
$input.remove();
};
this.focus = function () {
$input.focus();
};
this.getValue = function () {
return $input.val();
};
this.setValue = function (val) {
$input.val(val);
};
this.loadValue = function (item) {
defaultValue = item[args.column.field] || "";
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
$input.select();
};
this.serializeValue = function () {
return $input.val();
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function () {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null
};
};
this.init();
}
function IntegerEditor(args) {
var $input;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<INPUT type=text class='editor-text' />");
$input.bind("keydown.nav", function (e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
});
$input.appendTo(args.container);
$input.focus().select();
};
this.destroy = function () {
$input.remove();
};
this.focus = function () {
$input.focus();
};
this.loadValue = function (item) {
defaultValue = item[args.column.field];
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
$input.select();
};
this.serializeValue = function () {
return parseInt($input.val(), 10) || 0;
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function () {
if (isNaN($input.val())) {
return {
valid: false,
msg: "Please enter a valid integer"
};
}
return {
valid: true,
msg: null
};
};
this.init();
}
function DateEditor(args) {
var $input;
var defaultValue;
var scope = this;
var calendarOpen = false;
this.init = function () {
$input = $("<INPUT type=text class='editor-text' />");
$input.appendTo(args.container);
$input.focus().select();
$input.datepicker({
showOn: "button",
buttonImageOnly: true,
buttonImage: "../images/calendar.gif",
beforeShow: function () {
calendarOpen = true
},
onClose: function () {
calendarOpen = false
}
});
$input.width($input.width() - 18);
};
this.destroy = function () {
$.datepicker.dpDiv.stop(true, true);
$input.datepicker("hide");
$input.datepicker("destroy");
$input.remove();
};
this.show = function () {
if (calendarOpen) {
$.datepicker.dpDiv.stop(true, true).show();
}
};
this.hide = function () {
if (calendarOpen) {
$.datepicker.dpDiv.stop(true, true).hide();
}
};
this.position = function (position) {
if (!calendarOpen) {
return;
}
$.datepicker.dpDiv
.css("top", position.top + 30)
.css("left", position.left);
};
this.focus = function () {
$input.focus();
};
this.loadValue = function (item) {
defaultValue = item[args.column.field];
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
$input.select();
};
this.serializeValue = function () {
return $input.val();
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function () {
return {
valid: true,
msg: null
};
};
this.init();
}
function YesNoSelectEditor(args) {
var $select;
var defaultValue;
var scope = this;
this.init = function () {
$select = $("<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>");
$select.appendTo(args.container);
$select.focus();
};
this.destroy = function () {
$select.remove();
};
this.focus = function () {
$select.focus();
};
this.loadValue = function (item) {
$select.val((defaultValue = item[args.column.field]) ? "yes" : "no");
$select.select();
};
this.serializeValue = function () {
return ($select.val() == "yes");
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return ($select.val() != defaultValue);
};
this.validate = function () {
return {
valid: true,
msg: null
};
};
this.init();
}
function CheckboxEditor(args) {
var $select;
var defaultValue;
var scope = this;
this.init = function () {
$select = $("<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>");
$select.appendTo(args.container);
$select.focus();
};
this.destroy = function () {
$select.remove();
};
this.focus = function () {
$select.focus();
};
this.loadValue = function (item) {
defaultValue = !!item[args.column.field];
if (defaultValue) {
$select.prop('checked', true);
} else {
$select.prop('checked', false);
}
};
this.serializeValue = function () {
return $select.prop('checked');
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (this.serializeValue() !== defaultValue);
};
this.validate = function () {
return {
valid: true,
msg: null
};
};
this.init();
}
function PercentCompleteEditor(args) {
var $input, $picker;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<INPUT type=text class='editor-percentcomplete' />");
$input.width($(args.container).innerWidth() - 25);
$input.appendTo(args.container);
$picker = $("<div class='editor-percentcomplete-picker' />").appendTo(args.container);
$picker.append("<div class='editor-percentcomplete-helper'><div class='editor-percentcomplete-wrapper'><div class='editor-percentcomplete-slider' /><div class='editor-percentcomplete-buttons' /></div></div>");
$picker.find(".editor-percentcomplete-buttons").append("<button val=0>Not started</button><br/><button val=50>In Progress</button><br/><button val=100>Complete</button>");
$input.focus().select();
$picker.find(".editor-percentcomplete-slider").slider({
orientation: "vertical",
range: "min",
value: defaultValue,
slide: function (event, ui) {
$input.val(ui.value)
}
});
$picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) {
$input.val($(this).attr("val"));
$picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val"));
})
};
this.destroy = function () {
$input.remove();
$picker.remove();
};
this.focus = function () {
$input.focus();
};
this.loadValue = function (item) {
$input.val(defaultValue = item[args.column.field]);
$input.select();
};
this.serializeValue = function () {
return parseInt($input.val(), 10) || 0;
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue);
};
this.validate = function () {
if (isNaN(parseInt($input.val(), 10))) {
return {
valid: false,
msg: "Please enter a valid positive number"
};
}
return {
valid: true,
msg: null
};
};
this.init();
}
/*
* An example of a "detached" editor.
* The UI is added onto document BODY and .position(), .show() and .hide() are implemented.
* KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
*/
function LongTextEditor(args) {
var $input, $wrapper;
var defaultValue;
var scope = this;
this.init = function () {
var $container = $("body");
$wrapper = $("<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>")
.appendTo($container);
$input = $("<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>")
.appendTo($wrapper);
$("<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>")
.appendTo($wrapper);
$wrapper.find("button:first").bind("click", this.save);
$wrapper.find("button:last").bind("click", this.cancel);
$input.bind("keydown", this.handleKeyDown);
scope.position(args.position);
$input.focus().select();
};
this.handleKeyDown = function (e) {
if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {
scope.save();
} else if (e.which == $.ui.keyCode.ESCAPE) {
e.preventDefault();
scope.cancel();
} else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {
e.preventDefault();
args.grid.navigatePrev();
} else if (e.which == $.ui.keyCode.TAB) {
e.preventDefault();
args.grid.navigateNext();
}
};
this.save = function () {
args.commitChanges();
};
this.cancel = function () {
$input.val(defaultValue);
args.cancelChanges();
};
this.hide = function () {
$wrapper.hide();
};
this.show = function () {
$wrapper.show();
};
this.position = function (position) {
$wrapper
.css("top", position.top - 5)
.css("left", position.left - 5)
};
this.destroy = function () {
$wrapper.remove();
};
this.focus = function () {
$input.focus();
};
this.loadValue = function (item) {
$input.val(defaultValue = item[args.column.field]);
$input.select();
};
this.serializeValue = function () {
return $input.val();
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function () {
return {
valid: true,
msg: null
};
};
this.init();
}
})(jQuery);

View File

@ -0,0 +1,59 @@
/***
* Contains basic SlickGrid formatters.
*
* NOTE: These are merely examples. You will most likely need to implement something more
* robust/extensible/localizable/etc. for your use!
*
* @module Formatters
* @namespace Slick
*/
(function ($) {
// register namespace
$.extend(true, window, {
"Slick": {
"Formatters": {
"PercentComplete": PercentCompleteFormatter,
"PercentCompleteBar": PercentCompleteBarFormatter,
"YesNo": YesNoFormatter,
"Checkmark": CheckmarkFormatter
}
}
});
function PercentCompleteFormatter(row, cell, value, columnDef, dataContext) {
if (value == null || value === "") {
return "-";
} else if (value < 50) {
return "<span style='color:red;font-weight:bold;'>" + value + "%</span>";
} else {
return "<span style='color:green'>" + value + "%</span>";
}
}
function PercentCompleteBarFormatter(row, cell, value, columnDef, dataContext) {
if (value == null || value === "") {
return "";
}
var color;
if (value < 30) {
color = "red";
} else if (value < 70) {
color = "silver";
} else {
color = "green";
}
return "<span class='percent-complete-bar' style='background:" + color + ";width:" + value + "%'></span>";
}
function YesNoFormatter(row, cell, value, columnDef, dataContext) {
return value ? "Yes" : "No";
}
function CheckmarkFormatter(row, cell, value, columnDef, dataContext) {
return value ? "<img src='../images/tick.png'>" : "";
}
})(jQuery);

3422
vendor/slickgrid/2.0.1/slick.grid.js vendored Normal file

File diff suppressed because it is too large Load Diff