smartkiosk-server 0.12.1 → 0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.ruby-version +1 -1
- data/Gemfile +9 -6
- data/Gemfile.lock +36 -9
- data/app/assets/images/sort-asc.png +0 -0
- data/app/assets/images/sort-desc.png +0 -0
- data/app/assets/javascripts/application.js +1 -1
- data/app/assets/javascripts/monitoring.js.coffee +25 -0
- data/app/assets/javascripts/monitoring/helpers/application.js.coffee +12 -0
- data/app/assets/javascripts/monitoring/layouts/application.js.coffee +2 -0
- data/app/assets/javascripts/monitoring/pages/application.js.coffee +1 -0
- data/app/assets/javascripts/monitoring/pages/welcome/index.js.coffee +229 -0
- data/app/assets/javascripts/monitoring/resources/.gitkeep +0 -0
- data/app/assets/javascripts/monitoring/routes.js.coffee +8 -0
- data/app/assets/javascripts/monitoring/templates/layouts/.gitkeep +0 -0
- data/app/assets/javascripts/monitoring/templates/layouts/application.jst.hamlc +1 -0
- data/app/assets/javascripts/monitoring/templates/pages/welcome/_row.jst.hamlc +28 -0
- data/app/assets/javascripts/monitoring/templates/pages/welcome/_tbody.jst.hamlc +3 -0
- data/app/assets/javascripts/monitoring/templates/pages/welcome/index.jst.hamlc +37 -0
- data/app/assets/javascripts/monitoring/templates/widgets/.gitkeep +0 -0
- data/app/assets/javascripts/monitoring/widgets/.gitkeep +0 -0
- data/app/assets/javascripts/monitoring_preloader.js.coffee.erb +17 -0
- data/app/assets/stylesheets/active_admin.css.scss +4 -4
- data/app/assets/stylesheets/{fonts.css.scss → active_admin/fonts.css.scss} +0 -0
- data/app/assets/stylesheets/active_admin/provider_groups.css.scss +12 -0
- data/app/assets/stylesheets/{provider_receipt_templates.css.scss → active_admin/provider_receipt_templates.css.scss} +0 -0
- data/app/assets/stylesheets/{terminals.css.scss → active_admin/terminals.css.scss} +0 -0
- data/app/assets/stylesheets/monitoring.css.scss +4 -0
- data/app/assets/stylesheets/monitoring/bootstrap.css +9 -0
- data/app/assets/stylesheets/monitoring/theme.css.scss +35 -0
- data/app/controllers/monitoring_controller.rb +31 -0
- data/app/controllers/payments_controller.rb +1 -1
- data/app/controllers/terminal_builds_controller.rb +2 -0
- data/app/controllers/welcome_controller.rb +5 -2
- data/app/models/ability.rb +6 -4
- data/app/models/agent.rb +6 -0
- data/app/models/collection.rb +2 -2
- data/app/models/payment.rb +3 -3
- data/app/models/provider_gateway.rb +1 -1
- data/app/models/report_result.rb +1 -1
- data/app/models/report_template.rb +3 -3
- data/app/models/role.rb +51 -10
- data/app/models/terminal.rb +25 -1
- data/app/models/terminal_build.rb +1 -1
- data/app/models/terminal_order.rb +1 -1
- data/app/models/terminal_ping.rb +3 -0
- data/app/models/terminal_profile.rb +6 -0
- data/app/models/user_role.rb +1 -1
- data/app/views/monitoring/index.html.erb +28 -0
- data/config.ru +7 -0
- data/config/deploy.rb +1 -1
- data/config/environments/development.rb +2 -0
- data/config/environments/production.rb +1 -1
- data/config/initializers/monitoring.rb +3 -0
- data/config/initializers/redis.rb +1 -1
- data/config/locales/activerecord.ru.yml +14 -0
- data/config/locales/smartkiosk.ru.yml +38 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20130419125334_add_ping_to_terminals.rb +22 -0
- data/db/schema.rb +25 -7
- data/lib/blueprints.rb +2 -0
- data/lib/monitorer.rb +41 -0
- data/lib/seeder.rb +1 -1
- data/lib/smartkiosk/server/version.rb +1 -1
- data/vendor/assets/javascripts/chosen.jquery.js +1 -1
- data/vendor/assets/javascripts/cookie.jquery.js +95 -0
- data/vendor/assets/javascripts/copypaste.js +148 -0
- data/vendor/assets/javascripts/event-drag.jquery.js +402 -0
- data/vendor/assets/javascripts/sheetclip.js +87 -0
- data/vendor/assets/javascripts/slick.cellrangedecorator.js +64 -0
- data/vendor/assets/javascripts/slick.cellrangeselector.js +111 -0
- data/vendor/assets/javascripts/slick.cellselectionmodel.js +152 -0
- data/vendor/assets/javascripts/slick.core.js +458 -0
- data/vendor/assets/javascripts/slick.grid.js +3287 -0
- data/vendor/assets/stylesheets/slick.grid.css +167 -0
- metadata +42 -9
- data/app/assets/stylesheets/provider_groups.css.scss +0 -12
- data/app/views/layouts/application.html.erb +0 -1
- data/app/views/welcome/index.html.erb +0 -25
@@ -0,0 +1,87 @@
|
|
1
|
+
/**
|
2
|
+
* SheetClip - Spreadsheet Clipboard Parser
|
3
|
+
* version 0.2
|
4
|
+
*
|
5
|
+
* This tiny library transforms JavaScript arrays to strings that are pasteable by LibreOffice, OpenOffice,
|
6
|
+
* Google Docs and Microsoft Excel.
|
7
|
+
*
|
8
|
+
* Copyright 2012, Marcin Warpechowski
|
9
|
+
* Licensed under the MIT license.
|
10
|
+
* http://github.com/warpech/sheetclip/
|
11
|
+
*/
|
12
|
+
/*jslint white: true*/
|
13
|
+
(function (global) {
|
14
|
+
"use strict";
|
15
|
+
|
16
|
+
function countQuotes(str) {
|
17
|
+
return str.split('"').length - 1;
|
18
|
+
}
|
19
|
+
|
20
|
+
global.SheetClip = {
|
21
|
+
parse: function (str) {
|
22
|
+
var r, rlen, rows, arr = [], a = 0, c, clen, multiline, last;
|
23
|
+
rows = str.split('\n');
|
24
|
+
if (rows.length > 1 && rows[rows.length - 1] === '') {
|
25
|
+
rows.pop();
|
26
|
+
}
|
27
|
+
for (r = 0, rlen = rows.length; r < rlen; r += 1) {
|
28
|
+
rows[r] = rows[r].split('\t');
|
29
|
+
for (c = 0, clen = rows[r].length; c < clen; c += 1) {
|
30
|
+
if (!arr[a]) {
|
31
|
+
arr[a] = [];
|
32
|
+
}
|
33
|
+
if (multiline && c === 0) {
|
34
|
+
last = arr[a].length - 1;
|
35
|
+
arr[a][last] = arr[a][last] + '\n' + rows[r][0];
|
36
|
+
if (multiline && countQuotes(rows[r][0]) % 2 === 1) {
|
37
|
+
multiline = false;
|
38
|
+
arr[a][last] = arr[a][last].substring(0, arr[a][last].length - 1).replace(/""/g, '"');
|
39
|
+
}
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
if (c === clen - 1 && rows[r][c].indexOf('"') === 0) {
|
43
|
+
arr[a].push(rows[r][c].substring(1).replace(/""/g, '"'));
|
44
|
+
multiline = true;
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
arr[a].push(rows[r][c].replace(/""/g, '"'));
|
48
|
+
multiline = false;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
if (!multiline) {
|
53
|
+
a += 1;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return arr;
|
57
|
+
},
|
58
|
+
|
59
|
+
stringify: function (arr) {
|
60
|
+
var r, rlen, c, clen, str = '', val;
|
61
|
+
for (r = 0, rlen = arr.length; r < rlen; r += 1) {
|
62
|
+
for (c = 0, clen = arr[r].length; c < clen; c += 1) {
|
63
|
+
if (c > 0) {
|
64
|
+
str += '\t';
|
65
|
+
}
|
66
|
+
val = arr[r][c];
|
67
|
+
if (typeof val === 'string') {
|
68
|
+
if (val.indexOf('\n') > -1) {
|
69
|
+
str += '"' + val.replace(/"/g, '""') + '"';
|
70
|
+
}
|
71
|
+
else {
|
72
|
+
str += val;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
else if (val === null || val === void 0) { //void 0 resolves to undefined
|
76
|
+
str += '';
|
77
|
+
}
|
78
|
+
else {
|
79
|
+
str += val;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
str += '\n';
|
83
|
+
}
|
84
|
+
return str;
|
85
|
+
}
|
86
|
+
};
|
87
|
+
}(window));
|
@@ -0,0 +1,64 @@
|
|
1
|
+
(function ($) {
|
2
|
+
// register namespace
|
3
|
+
$.extend(true, window, {
|
4
|
+
"Slick": {
|
5
|
+
"CellRangeDecorator": CellRangeDecorator
|
6
|
+
}
|
7
|
+
});
|
8
|
+
|
9
|
+
/***
|
10
|
+
* Displays an overlay on top of a given cell range.
|
11
|
+
*
|
12
|
+
* TODO:
|
13
|
+
* Currently, it blocks mouse events to DOM nodes behind it.
|
14
|
+
* Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding.
|
15
|
+
* Could also construct the borders separately using 4 individual DIVs.
|
16
|
+
*
|
17
|
+
* @param {Grid} grid
|
18
|
+
* @param {Object} options
|
19
|
+
*/
|
20
|
+
function CellRangeDecorator(grid, options) {
|
21
|
+
var _elem;
|
22
|
+
var _defaults = {
|
23
|
+
selectionCss: {
|
24
|
+
"zIndex": "9999",
|
25
|
+
"border": "2px dashed red"
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
options = $.extend(true, {}, _defaults, options);
|
30
|
+
|
31
|
+
|
32
|
+
function show(range) {
|
33
|
+
if (!_elem) {
|
34
|
+
_elem = $("<div></div>", {css: options.selectionCss})
|
35
|
+
.css("position", "absolute")
|
36
|
+
.appendTo(grid.getCanvasNode());
|
37
|
+
}
|
38
|
+
|
39
|
+
var from = grid.getCellNodeBox(range.fromRow, range.fromCell);
|
40
|
+
var to = grid.getCellNodeBox(range.toRow, range.toCell);
|
41
|
+
|
42
|
+
_elem.css({
|
43
|
+
top: from.top - 1,
|
44
|
+
left: from.left - 1,
|
45
|
+
height: to.bottom - from.top - 2,
|
46
|
+
width: to.right - from.left - 2
|
47
|
+
});
|
48
|
+
|
49
|
+
return _elem;
|
50
|
+
}
|
51
|
+
|
52
|
+
function hide() {
|
53
|
+
if (_elem) {
|
54
|
+
_elem.remove();
|
55
|
+
_elem = null;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
$.extend(this, {
|
60
|
+
"show": show,
|
61
|
+
"hide": hide
|
62
|
+
});
|
63
|
+
}
|
64
|
+
})(jQuery);
|
@@ -0,0 +1,111 @@
|
|
1
|
+
(function ($) {
|
2
|
+
// register namespace
|
3
|
+
$.extend(true, window, {
|
4
|
+
"Slick": {
|
5
|
+
"CellRangeSelector": CellRangeSelector
|
6
|
+
}
|
7
|
+
});
|
8
|
+
|
9
|
+
|
10
|
+
function CellRangeSelector(options) {
|
11
|
+
var _grid;
|
12
|
+
var _canvas;
|
13
|
+
var _dragging;
|
14
|
+
var _decorator;
|
15
|
+
var _self = this;
|
16
|
+
var _handler = new Slick.EventHandler();
|
17
|
+
var _defaults = {
|
18
|
+
selectionCss: {
|
19
|
+
"border": "2px dashed blue"
|
20
|
+
}
|
21
|
+
};
|
22
|
+
|
23
|
+
|
24
|
+
function init(grid) {
|
25
|
+
options = $.extend(true, {}, _defaults, options);
|
26
|
+
_decorator = new Slick.CellRangeDecorator(grid, options);
|
27
|
+
_grid = grid;
|
28
|
+
_canvas = _grid.getCanvasNode();
|
29
|
+
_handler
|
30
|
+
.subscribe(_grid.onDragInit, handleDragInit)
|
31
|
+
.subscribe(_grid.onDragStart, handleDragStart)
|
32
|
+
.subscribe(_grid.onDrag, handleDrag)
|
33
|
+
.subscribe(_grid.onDragEnd, handleDragEnd);
|
34
|
+
}
|
35
|
+
|
36
|
+
function destroy() {
|
37
|
+
_handler.unsubscribeAll();
|
38
|
+
}
|
39
|
+
|
40
|
+
function handleDragInit(e, dd) {
|
41
|
+
// prevent the grid from cancelling drag'n'drop by default
|
42
|
+
e.stopImmediatePropagation();
|
43
|
+
}
|
44
|
+
|
45
|
+
function handleDragStart(e, dd) {
|
46
|
+
var cell = _grid.getCellFromEvent(e);
|
47
|
+
if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {
|
48
|
+
if (_grid.canCellBeSelected(cell.row, cell.cell)) {
|
49
|
+
_dragging = true;
|
50
|
+
e.stopImmediatePropagation();
|
51
|
+
}
|
52
|
+
}
|
53
|
+
if (!_dragging) {
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
|
57
|
+
var start = _grid.getCellFromPoint(
|
58
|
+
dd.startX - $(_canvas).offset().left,
|
59
|
+
dd.startY - $(_canvas).offset().top);
|
60
|
+
|
61
|
+
dd.range = {start: start, end: {}};
|
62
|
+
|
63
|
+
return _decorator.show(new Slick.Range(start.row, start.cell));
|
64
|
+
}
|
65
|
+
|
66
|
+
function handleDrag(e, dd) {
|
67
|
+
if (!_dragging) {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
e.stopImmediatePropagation();
|
71
|
+
|
72
|
+
var end = _grid.getCellFromPoint(
|
73
|
+
e.pageX - $(_canvas).offset().left,
|
74
|
+
e.pageY - $(_canvas).offset().top);
|
75
|
+
|
76
|
+
if (!_grid.canCellBeSelected(end.row, end.cell)) {
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
|
80
|
+
dd.range.end = end;
|
81
|
+
_decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
|
82
|
+
}
|
83
|
+
|
84
|
+
function handleDragEnd(e, dd) {
|
85
|
+
if (!_dragging) {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
89
|
+
_dragging = false;
|
90
|
+
e.stopImmediatePropagation();
|
91
|
+
|
92
|
+
_decorator.hide();
|
93
|
+
_self.onCellRangeSelected.notify({
|
94
|
+
range: new Slick.Range(
|
95
|
+
dd.range.start.row,
|
96
|
+
dd.range.start.cell,
|
97
|
+
dd.range.end.row,
|
98
|
+
dd.range.end.cell
|
99
|
+
)
|
100
|
+
});
|
101
|
+
}
|
102
|
+
|
103
|
+
$.extend(this, {
|
104
|
+
"init": init,
|
105
|
+
"destroy": destroy,
|
106
|
+
|
107
|
+
"onBeforeCellRangeSelected": new Slick.Event(),
|
108
|
+
"onCellRangeSelected": new Slick.Event()
|
109
|
+
});
|
110
|
+
}
|
111
|
+
})(jQuery);
|
@@ -0,0 +1,152 @@
|
|
1
|
+
(function ($) {
|
2
|
+
// register namespace
|
3
|
+
$.extend(true, window, {
|
4
|
+
"Slick": {
|
5
|
+
"CellSelectionModel": CellSelectionModel
|
6
|
+
}
|
7
|
+
});
|
8
|
+
|
9
|
+
|
10
|
+
function CellSelectionModel(options) {
|
11
|
+
var _grid;
|
12
|
+
var _canvas;
|
13
|
+
var _ranges = [];
|
14
|
+
var _self = this;
|
15
|
+
var _selector = new Slick.CellRangeSelector({
|
16
|
+
"selectionCss": {
|
17
|
+
"border": "2px solid black"
|
18
|
+
}
|
19
|
+
});
|
20
|
+
var _options;
|
21
|
+
var _defaults = {
|
22
|
+
selectActiveCell: true
|
23
|
+
};
|
24
|
+
|
25
|
+
|
26
|
+
function init(grid) {
|
27
|
+
_options = $.extend(true, {}, _defaults, options);
|
28
|
+
_grid = grid;
|
29
|
+
_canvas = _grid.getCanvasNode();
|
30
|
+
_grid.onActiveCellChanged.subscribe(handleActiveCellChange);
|
31
|
+
_grid.onKeyDown.subscribe(handleKeyDown);
|
32
|
+
grid.registerPlugin(_selector);
|
33
|
+
_selector.onCellRangeSelected.subscribe(handleCellRangeSelected);
|
34
|
+
_selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);
|
35
|
+
}
|
36
|
+
|
37
|
+
function destroy() {
|
38
|
+
_grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);
|
39
|
+
_grid.onKeyDown.unsubscribe(handleKeyDown);
|
40
|
+
_selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);
|
41
|
+
_selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);
|
42
|
+
_grid.unregisterPlugin(_selector);
|
43
|
+
}
|
44
|
+
|
45
|
+
function removeInvalidRanges(ranges) {
|
46
|
+
var result = [];
|
47
|
+
|
48
|
+
for (var i = 0; i < ranges.length; i++) {
|
49
|
+
var r = ranges[i];
|
50
|
+
if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) {
|
51
|
+
result.push(r);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
return result;
|
56
|
+
}
|
57
|
+
|
58
|
+
function setSelectedRanges(ranges) {
|
59
|
+
_ranges = removeInvalidRanges(ranges);
|
60
|
+
_self.onSelectedRangesChanged.notify(_ranges);
|
61
|
+
}
|
62
|
+
|
63
|
+
function getSelectedRanges() {
|
64
|
+
return _ranges;
|
65
|
+
}
|
66
|
+
|
67
|
+
function handleBeforeCellRangeSelected(e, args) {
|
68
|
+
if (_grid.getEditorLock().isActive()) {
|
69
|
+
e.stopPropagation();
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
function handleCellRangeSelected(e, args) {
|
75
|
+
setSelectedRanges([args.range]);
|
76
|
+
}
|
77
|
+
|
78
|
+
function handleActiveCellChange(e, args) {
|
79
|
+
if (_options.selectActiveCell && args.row != null && args.cell != null) {
|
80
|
+
setSelectedRanges([new Slick.Range(args.row, args.cell)]);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
function handleKeyDown(e) {
|
85
|
+
/***
|
86
|
+
* Кey codes
|
87
|
+
* 37 left
|
88
|
+
* 38 up
|
89
|
+
* 39 right
|
90
|
+
* 40 down
|
91
|
+
*/
|
92
|
+
var ranges, last;
|
93
|
+
var active = _grid.getActiveCell();
|
94
|
+
|
95
|
+
if ( active && e.shiftKey && !e.ctrlKey && !e.altKey &&
|
96
|
+
(e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) {
|
97
|
+
|
98
|
+
ranges = getSelectedRanges();
|
99
|
+
if (!ranges.length)
|
100
|
+
ranges.push(new Slick.Range(active.row, active.cell));
|
101
|
+
|
102
|
+
// keyboard can work with last range only
|
103
|
+
last = ranges.pop();
|
104
|
+
|
105
|
+
// can't handle selection out of active cell
|
106
|
+
if (!last.contains(active.row, active.cell))
|
107
|
+
last = new Slick.Range(active.row, active.cell);
|
108
|
+
|
109
|
+
var dRow = last.toRow - last.fromRow,
|
110
|
+
dCell = last.toCell - last.fromCell,
|
111
|
+
// walking direction
|
112
|
+
dirRow = active.row == last.fromRow ? 1 : -1,
|
113
|
+
dirCell = active.cell == last.fromCell ? 1 : -1;
|
114
|
+
|
115
|
+
if (e.which == 37) {
|
116
|
+
dCell -= dirCell;
|
117
|
+
} else if (e.which == 39) {
|
118
|
+
dCell += dirCell ;
|
119
|
+
} else if (e.which == 38) {
|
120
|
+
dRow -= dirRow;
|
121
|
+
} else if (e.which == 40) {
|
122
|
+
dRow += dirRow;
|
123
|
+
}
|
124
|
+
|
125
|
+
// define new selection range
|
126
|
+
var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell);
|
127
|
+
if (removeInvalidRanges([new_last]).length) {
|
128
|
+
ranges.push(new_last);
|
129
|
+
_grid.scrollRowIntoView(dirRow > 0 ? new_last.toRow : new_last.fromRow);
|
130
|
+
_grid.scrollCellIntoView(new_last.fromRow, dirCell > 0 ? new_last.toCell : new_last.fromCell);
|
131
|
+
}
|
132
|
+
else
|
133
|
+
ranges.push(last);
|
134
|
+
|
135
|
+
setSelectedRanges(ranges);
|
136
|
+
|
137
|
+
e.preventDefault();
|
138
|
+
e.stopPropagation();
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
$.extend(this, {
|
143
|
+
"getSelectedRanges": getSelectedRanges,
|
144
|
+
"setSelectedRanges": setSelectedRanges,
|
145
|
+
|
146
|
+
"init": init,
|
147
|
+
"destroy": destroy,
|
148
|
+
|
149
|
+
"onSelectedRangesChanged": new Slick.Event()
|
150
|
+
});
|
151
|
+
}
|
152
|
+
})(jQuery);
|
@@ -0,0 +1,458 @@
|
|
1
|
+
/***
|
2
|
+
* Contains core SlickGrid classes.
|
3
|
+
* @module Core
|
4
|
+
* @namespace Slick
|
5
|
+
*/
|
6
|
+
|
7
|
+
(function ($) {
|
8
|
+
// register namespace
|
9
|
+
$.extend(true, window, {
|
10
|
+
"Slick": {
|
11
|
+
"Event": Event,
|
12
|
+
"EventData": EventData,
|
13
|
+
"EventHandler": EventHandler,
|
14
|
+
"Range": Range,
|
15
|
+
"NonDataRow": NonDataItem,
|
16
|
+
"Group": Group,
|
17
|
+
"GroupTotals": GroupTotals,
|
18
|
+
"EditorLock": EditorLock,
|
19
|
+
|
20
|
+
/***
|
21
|
+
* A global singleton editor lock.
|
22
|
+
* @class GlobalEditorLock
|
23
|
+
* @static
|
24
|
+
* @constructor
|
25
|
+
*/
|
26
|
+
"GlobalEditorLock": new EditorLock()
|
27
|
+
}
|
28
|
+
});
|
29
|
+
|
30
|
+
/***
|
31
|
+
* An event object for passing data to event handlers and letting them control propagation.
|
32
|
+
* <p>This is pretty much identical to how W3C and jQuery implement events.</p>
|
33
|
+
* @class EventData
|
34
|
+
* @constructor
|
35
|
+
*/
|
36
|
+
function EventData() {
|
37
|
+
var isPropagationStopped = false;
|
38
|
+
var isImmediatePropagationStopped = false;
|
39
|
+
|
40
|
+
/***
|
41
|
+
* Stops event from propagating up the DOM tree.
|
42
|
+
* @method stopPropagation
|
43
|
+
*/
|
44
|
+
this.stopPropagation = function () {
|
45
|
+
isPropagationStopped = true;
|
46
|
+
};
|
47
|
+
|
48
|
+
/***
|
49
|
+
* Returns whether stopPropagation was called on this event object.
|
50
|
+
* @method isPropagationStopped
|
51
|
+
* @return {Boolean}
|
52
|
+
*/
|
53
|
+
this.isPropagationStopped = function () {
|
54
|
+
return isPropagationStopped;
|
55
|
+
};
|
56
|
+
|
57
|
+
/***
|
58
|
+
* Prevents the rest of the handlers from being executed.
|
59
|
+
* @method stopImmediatePropagation
|
60
|
+
*/
|
61
|
+
this.stopImmediatePropagation = function () {
|
62
|
+
isImmediatePropagationStopped = true;
|
63
|
+
};
|
64
|
+
|
65
|
+
/***
|
66
|
+
* Returns whether stopImmediatePropagation was called on this event object.\
|
67
|
+
* @method isImmediatePropagationStopped
|
68
|
+
* @return {Boolean}
|
69
|
+
*/
|
70
|
+
this.isImmediatePropagationStopped = function () {
|
71
|
+
return isImmediatePropagationStopped;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
/***
|
76
|
+
* A simple publisher-subscriber implementation.
|
77
|
+
* @class Event
|
78
|
+
* @constructor
|
79
|
+
*/
|
80
|
+
function Event() {
|
81
|
+
var handlers = [];
|
82
|
+
|
83
|
+
/***
|
84
|
+
* Adds an event handler to be called when the event is fired.
|
85
|
+
* <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code>
|
86
|
+
* object the event was fired with.<p>
|
87
|
+
* @method subscribe
|
88
|
+
* @param fn {Function} Event handler.
|
89
|
+
*/
|
90
|
+
this.subscribe = function (fn) {
|
91
|
+
handlers.push(fn);
|
92
|
+
};
|
93
|
+
|
94
|
+
/***
|
95
|
+
* Removes an event handler added with <code>subscribe(fn)</code>.
|
96
|
+
* @method unsubscribe
|
97
|
+
* @param fn {Function} Event handler to be removed.
|
98
|
+
*/
|
99
|
+
this.unsubscribe = function (fn) {
|
100
|
+
for (var i = handlers.length - 1; i >= 0; i--) {
|
101
|
+
if (handlers[i] === fn) {
|
102
|
+
handlers.splice(i, 1);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
};
|
106
|
+
|
107
|
+
/***
|
108
|
+
* Fires an event notifying all subscribers.
|
109
|
+
* @method notify
|
110
|
+
* @param args {Object} Additional data object to be passed to all handlers.
|
111
|
+
* @param e {EventData}
|
112
|
+
* Optional.
|
113
|
+
* An <code>EventData</code> object to be passed to all handlers.
|
114
|
+
* For DOM events, an existing W3C/jQuery event object can be passed in.
|
115
|
+
* @param scope {Object}
|
116
|
+
* Optional.
|
117
|
+
* The scope ("this") within which the handler will be executed.
|
118
|
+
* If not specified, the scope will be set to the <code>Event</code> instance.
|
119
|
+
*/
|
120
|
+
this.notify = function (args, e, scope) {
|
121
|
+
e = e || new EventData();
|
122
|
+
scope = scope || this;
|
123
|
+
|
124
|
+
var returnValue;
|
125
|
+
for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) {
|
126
|
+
returnValue = handlers[i].call(scope, e, args);
|
127
|
+
}
|
128
|
+
|
129
|
+
return returnValue;
|
130
|
+
};
|
131
|
+
}
|
132
|
+
|
133
|
+
function EventHandler() {
|
134
|
+
var handlers = [];
|
135
|
+
|
136
|
+
this.subscribe = function (event, handler) {
|
137
|
+
handlers.push({
|
138
|
+
event: event,
|
139
|
+
handler: handler
|
140
|
+
});
|
141
|
+
event.subscribe(handler);
|
142
|
+
|
143
|
+
return this; // allow chaining
|
144
|
+
};
|
145
|
+
|
146
|
+
this.unsubscribe = function (event, handler) {
|
147
|
+
var i = handlers.length;
|
148
|
+
while (i--) {
|
149
|
+
if (handlers[i].event === event &&
|
150
|
+
handlers[i].handler === handler) {
|
151
|
+
handlers.splice(i, 1);
|
152
|
+
event.unsubscribe(handler);
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return this; // allow chaining
|
158
|
+
};
|
159
|
+
|
160
|
+
this.unsubscribeAll = function () {
|
161
|
+
var i = handlers.length;
|
162
|
+
while (i--) {
|
163
|
+
handlers[i].event.unsubscribe(handlers[i].handler);
|
164
|
+
}
|
165
|
+
handlers = [];
|
166
|
+
|
167
|
+
return this; // allow chaining
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
/***
|
172
|
+
* A structure containing a range of cells.
|
173
|
+
* @class Range
|
174
|
+
* @constructor
|
175
|
+
* @param fromRow {Integer} Starting row.
|
176
|
+
* @param fromCell {Integer} Starting cell.
|
177
|
+
* @param toRow {Integer} Optional. Ending row. Defaults to <code>fromRow</code>.
|
178
|
+
* @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>.
|
179
|
+
*/
|
180
|
+
function Range(fromRow, fromCell, toRow, toCell) {
|
181
|
+
if (toRow === undefined && toCell === undefined) {
|
182
|
+
toRow = fromRow;
|
183
|
+
toCell = fromCell;
|
184
|
+
}
|
185
|
+
|
186
|
+
/***
|
187
|
+
* @property fromRow
|
188
|
+
* @type {Integer}
|
189
|
+
*/
|
190
|
+
this.fromRow = Math.min(fromRow, toRow);
|
191
|
+
|
192
|
+
/***
|
193
|
+
* @property fromCell
|
194
|
+
* @type {Integer}
|
195
|
+
*/
|
196
|
+
this.fromCell = Math.min(fromCell, toCell);
|
197
|
+
|
198
|
+
/***
|
199
|
+
* @property toRow
|
200
|
+
* @type {Integer}
|
201
|
+
*/
|
202
|
+
this.toRow = Math.max(fromRow, toRow);
|
203
|
+
|
204
|
+
/***
|
205
|
+
* @property toCell
|
206
|
+
* @type {Integer}
|
207
|
+
*/
|
208
|
+
this.toCell = Math.max(fromCell, toCell);
|
209
|
+
|
210
|
+
/***
|
211
|
+
* Returns whether a range represents a single row.
|
212
|
+
* @method isSingleRow
|
213
|
+
* @return {Boolean}
|
214
|
+
*/
|
215
|
+
this.isSingleRow = function () {
|
216
|
+
return this.fromRow == this.toRow;
|
217
|
+
};
|
218
|
+
|
219
|
+
/***
|
220
|
+
* Returns whether a range represents a single cell.
|
221
|
+
* @method isSingleCell
|
222
|
+
* @return {Boolean}
|
223
|
+
*/
|
224
|
+
this.isSingleCell = function () {
|
225
|
+
return this.fromRow == this.toRow && this.fromCell == this.toCell;
|
226
|
+
};
|
227
|
+
|
228
|
+
/***
|
229
|
+
* Returns whether a range contains a given cell.
|
230
|
+
* @method contains
|
231
|
+
* @param row {Integer}
|
232
|
+
* @param cell {Integer}
|
233
|
+
* @return {Boolean}
|
234
|
+
*/
|
235
|
+
this.contains = function (row, cell) {
|
236
|
+
return row >= this.fromRow && row <= this.toRow &&
|
237
|
+
cell >= this.fromCell && cell <= this.toCell;
|
238
|
+
};
|
239
|
+
|
240
|
+
/***
|
241
|
+
* Returns a readable representation of a range.
|
242
|
+
* @method toString
|
243
|
+
* @return {String}
|
244
|
+
*/
|
245
|
+
this.toString = function () {
|
246
|
+
if (this.isSingleCell()) {
|
247
|
+
return "(" + this.fromRow + ":" + this.fromCell + ")";
|
248
|
+
}
|
249
|
+
else {
|
250
|
+
return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")";
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
/***
|
257
|
+
* A base class that all special / non-data rows (like Group and GroupTotals) derive from.
|
258
|
+
* @class NonDataItem
|
259
|
+
* @constructor
|
260
|
+
*/
|
261
|
+
function NonDataItem() {
|
262
|
+
this.__nonDataRow = true;
|
263
|
+
}
|
264
|
+
|
265
|
+
|
266
|
+
/***
|
267
|
+
* Information about a group of rows.
|
268
|
+
* @class Group
|
269
|
+
* @extends Slick.NonDataItem
|
270
|
+
* @constructor
|
271
|
+
*/
|
272
|
+
function Group() {
|
273
|
+
this.__group = true;
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Grouping level, starting with 0.
|
277
|
+
* @property level
|
278
|
+
* @type {Number}
|
279
|
+
*/
|
280
|
+
this.level = 0;
|
281
|
+
|
282
|
+
/***
|
283
|
+
* Number of rows in the group.
|
284
|
+
* @property count
|
285
|
+
* @type {Integer}
|
286
|
+
*/
|
287
|
+
this.count = 0;
|
288
|
+
|
289
|
+
/***
|
290
|
+
* Grouping value.
|
291
|
+
* @property value
|
292
|
+
* @type {Object}
|
293
|
+
*/
|
294
|
+
this.value = null;
|
295
|
+
|
296
|
+
/***
|
297
|
+
* Formatted display value of the group.
|
298
|
+
* @property title
|
299
|
+
* @type {String}
|
300
|
+
*/
|
301
|
+
this.title = null;
|
302
|
+
|
303
|
+
/***
|
304
|
+
* Whether a group is collapsed.
|
305
|
+
* @property collapsed
|
306
|
+
* @type {Boolean}
|
307
|
+
*/
|
308
|
+
this.collapsed = false;
|
309
|
+
|
310
|
+
/***
|
311
|
+
* GroupTotals, if any.
|
312
|
+
* @property totals
|
313
|
+
* @type {GroupTotals}
|
314
|
+
*/
|
315
|
+
this.totals = null;
|
316
|
+
|
317
|
+
/**
|
318
|
+
* Rows that are part of the group.
|
319
|
+
* @property rows
|
320
|
+
* @type {Array}
|
321
|
+
*/
|
322
|
+
this.rows = [];
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Sub-groups that are part of the group.
|
326
|
+
* @property groups
|
327
|
+
* @type {Array}
|
328
|
+
*/
|
329
|
+
this.groups = null;
|
330
|
+
|
331
|
+
/**
|
332
|
+
* A unique key used to identify the group. This key can be used in calls to DataView
|
333
|
+
* collapseGroup() or expandGroup().
|
334
|
+
* @property groupingKey
|
335
|
+
* @type {Object}
|
336
|
+
*/
|
337
|
+
this.groupingKey = null;
|
338
|
+
}
|
339
|
+
|
340
|
+
Group.prototype = new NonDataItem();
|
341
|
+
|
342
|
+
/***
|
343
|
+
* Compares two Group instances.
|
344
|
+
* @method equals
|
345
|
+
* @return {Boolean}
|
346
|
+
* @param group {Group} Group instance to compare to.
|
347
|
+
*/
|
348
|
+
Group.prototype.equals = function (group) {
|
349
|
+
return this.value === group.value &&
|
350
|
+
this.count === group.count &&
|
351
|
+
this.collapsed === group.collapsed;
|
352
|
+
};
|
353
|
+
|
354
|
+
/***
|
355
|
+
* Information about group totals.
|
356
|
+
* An instance of GroupTotals will be created for each totals row and passed to the aggregators
|
357
|
+
* so that they can store arbitrary data in it. That data can later be accessed by group totals
|
358
|
+
* formatters during the display.
|
359
|
+
* @class GroupTotals
|
360
|
+
* @extends Slick.NonDataItem
|
361
|
+
* @constructor
|
362
|
+
*/
|
363
|
+
function GroupTotals() {
|
364
|
+
this.__groupTotals = true;
|
365
|
+
|
366
|
+
/***
|
367
|
+
* Parent Group.
|
368
|
+
* @param group
|
369
|
+
* @type {Group}
|
370
|
+
*/
|
371
|
+
this.group = null;
|
372
|
+
}
|
373
|
+
|
374
|
+
GroupTotals.prototype = new NonDataItem();
|
375
|
+
|
376
|
+
/***
|
377
|
+
* A locking helper to track the active edit controller and ensure that only a single controller
|
378
|
+
* can be active at a time. This prevents a whole class of state and validation synchronization
|
379
|
+
* issues. An edit controller (such as SlickGrid) can query if an active edit is in progress
|
380
|
+
* and attempt a commit or cancel before proceeding.
|
381
|
+
* @class EditorLock
|
382
|
+
* @constructor
|
383
|
+
*/
|
384
|
+
function EditorLock() {
|
385
|
+
var activeEditController = null;
|
386
|
+
|
387
|
+
/***
|
388
|
+
* Returns true if a specified edit controller is active (has the edit lock).
|
389
|
+
* If the parameter is not specified, returns true if any edit controller is active.
|
390
|
+
* @method isActive
|
391
|
+
* @param editController {EditController}
|
392
|
+
* @return {Boolean}
|
393
|
+
*/
|
394
|
+
this.isActive = function (editController) {
|
395
|
+
return (editController ? activeEditController === editController : activeEditController !== null);
|
396
|
+
};
|
397
|
+
|
398
|
+
/***
|
399
|
+
* Sets the specified edit controller as the active edit controller (acquire edit lock).
|
400
|
+
* If another edit controller is already active, and exception will be thrown.
|
401
|
+
* @method activate
|
402
|
+
* @param editController {EditController} edit controller acquiring the lock
|
403
|
+
*/
|
404
|
+
this.activate = function (editController) {
|
405
|
+
if (editController === activeEditController) { // already activated?
|
406
|
+
return;
|
407
|
+
}
|
408
|
+
if (activeEditController !== null) {
|
409
|
+
throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController";
|
410
|
+
}
|
411
|
+
if (!editController.commitCurrentEdit) {
|
412
|
+
throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";
|
413
|
+
}
|
414
|
+
if (!editController.cancelCurrentEdit) {
|
415
|
+
throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";
|
416
|
+
}
|
417
|
+
activeEditController = editController;
|
418
|
+
};
|
419
|
+
|
420
|
+
/***
|
421
|
+
* Unsets the specified edit controller as the active edit controller (release edit lock).
|
422
|
+
* If the specified edit controller is not the active one, an exception will be thrown.
|
423
|
+
* @method deactivate
|
424
|
+
* @param editController {EditController} edit controller releasing the lock
|
425
|
+
*/
|
426
|
+
this.deactivate = function (editController) {
|
427
|
+
if (activeEditController !== editController) {
|
428
|
+
throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one";
|
429
|
+
}
|
430
|
+
activeEditController = null;
|
431
|
+
};
|
432
|
+
|
433
|
+
/***
|
434
|
+
* Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit
|
435
|
+
* controller and returns whether the commit attempt was successful (commit may fail due to validation
|
436
|
+
* errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded
|
437
|
+
* and false otherwise. If no edit controller is active, returns true.
|
438
|
+
* @method commitCurrentEdit
|
439
|
+
* @return {Boolean}
|
440
|
+
*/
|
441
|
+
this.commitCurrentEdit = function () {
|
442
|
+
return (activeEditController ? activeEditController.commitCurrentEdit() : true);
|
443
|
+
};
|
444
|
+
|
445
|
+
/***
|
446
|
+
* Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit
|
447
|
+
* controller and returns whether the edit was successfully cancelled. If no edit controller is
|
448
|
+
* active, returns true.
|
449
|
+
* @method cancelCurrentEdit
|
450
|
+
* @return {Boolean}
|
451
|
+
*/
|
452
|
+
this.cancelCurrentEdit = function cancelCurrentEdit() {
|
453
|
+
return (activeEditController ? activeEditController.cancelCurrentEdit() : true);
|
454
|
+
};
|
455
|
+
}
|
456
|
+
})(jQuery);
|
457
|
+
|
458
|
+
|