iugu-ux 0.9.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/iugu-ux/version.rb +1 -1
- data/sandbox/log/development.log +432 -0
- data/sandbox/tmp/cache/assets/BD0/110/sprockets%2F52568313d04166c1237865292979c86d +0 -0
- data/sandbox/tmp/cache/assets/C48/D00/sprockets%2F4a51b39094a347e3e325005c7847551c +0 -0
- data/sandbox/tmp/cache/assets/C49/3B0/sprockets%2F405c7112191eed23e8b860254281e763 +0 -0
- data/sandbox/tmp/cache/assets/C4A/8F0/sprockets%2F736644cea24629d653454c90503a0c45 +0 -0
- data/sandbox/tmp/cache/assets/C4F/3A0/sprockets%2F41918e8d7b402652a5649e4b7134d620 +0 -0
- data/sandbox/tmp/cache/assets/C5A/0F0/sprockets%2F66317254e81572f5a89184618e3c3b5b +0 -0
- data/sandbox/tmp/cache/assets/C7A/F30/sprockets%2F70249181577b261a3ea8851b530f1e7c +0 -0
- data/sandbox/tmp/cache/assets/C86/500/sprockets%2F6990a45a256263b7579492ac7a6a626a +0 -0
- data/sandbox/tmp/cache/assets/C87/F60/sprockets%2F83b4a89e682705a1d0c1e1327608f968 +0 -0
- data/sandbox/tmp/cache/assets/C88/820/sprockets%2Fb50c6382fa02d221d1b29a40402d1641 +0 -0
- data/sandbox/tmp/cache/assets/C97/790/sprockets%2Ffa5602401f5500215f5c4555190dcbd2 +0 -0
- data/sandbox/tmp/cache/assets/CC3/930/sprockets%2F7e2c9302e5050f66e888725be83f862d +0 -0
- data/sandbox/tmp/cache/assets/CC7/EB0/sprockets%2F079d3b368c9e843b78c90079f26d628b +0 -0
- data/sandbox/tmp/cache/assets/CC8/510/sprockets%2F868a1670f8822e6b7847dd22e747a66f +0 -0
- data/sandbox/tmp/cache/assets/CCA/850/sprockets%2F1d05d32f046ec7313b0d3619a232d73f +0 -0
- data/sandbox/tmp/cache/assets/CDB/610/sprockets%2Fb5b20836971a5786ca125712b8e0bf5e +0 -0
- data/sandbox/tmp/cache/assets/CDB/E30/sprockets%2F918458a88fec2cd01991c003b2318ab7 +0 -0
- data/sandbox/tmp/cache/assets/CEC/760/sprockets%2F2e588fd16989acc60b813afe18471562 +0 -0
- data/sandbox/tmp/cache/assets/D08/ED0/sprockets%2Fcb1aee736d3b287a872298b7741b012b +0 -0
- data/sandbox/tmp/cache/assets/D1A/B70/sprockets%2F47ae92dd9d034b32098b19bf8f2475a9 +0 -0
- data/sandbox/tmp/cache/assets/D34/3E0/sprockets%2F46a73c17c9a561115944df004dbbdd7b +0 -0
- data/sandbox/tmp/cache/assets/D3B/BC0/sprockets%2F19510a763bd1521ff5fc70ffe0d837d3 +0 -0
- data/sandbox/tmp/cache/assets/D4A/1A0/sprockets%2Fb746360a8b3ce79c57e74a9c15309fdd +0 -0
- data/sandbox/tmp/cache/assets/D4A/EB0/sprockets%2F601dc7267b0c9e83e7c9bd2bf94e3564 +0 -0
- data/sandbox/tmp/cache/assets/D4F/810/sprockets%2Fa10070d6497ba2b4ea552d4a1bb444cb +0 -0
- data/sandbox/tmp/cache/assets/D53/E90/sprockets%2Fb333afe57100a0a8c1bc6c37405e56bb +0 -0
- data/sandbox/tmp/cache/assets/D55/310/sprockets%2F2b9be79ca64ec7d294d4825e21696ce9 +0 -0
- data/sandbox/tmp/cache/assets/D5C/200/sprockets%2Fcd323e6f7545cee0b3ba1e84d7520b00 +0 -0
- data/sandbox/tmp/cache/assets/D66/7C0/sprockets%2F5afbe1fb48c4015a6ffc9085c0519b05 +0 -0
- data/sandbox/tmp/cache/assets/D6D/AA0/sprockets%2F1cb4a75912d38d0bdde6ee6770644d0f +0 -0
- data/sandbox/tmp/cache/assets/D6E/AE0/sprockets%2Fe193ea7b03f186ad2a9515fc78fa8c21 +0 -0
- data/sandbox/tmp/cache/assets/D74/0A0/sprockets%2Fc1392b79af6b88250b634479cbf2bcfc +0 -0
- data/sandbox/tmp/cache/assets/D79/5A0/sprockets%2Feb399a6f39a8dcd0bf011768e7153d8d +0 -0
- data/sandbox/tmp/cache/assets/D7A/D40/sprockets%2F4d2bc08f2787a3870ff93e0e0f7bc67e +0 -0
- data/sandbox/tmp/cache/assets/D7D/A60/sprockets%2Fad3ac10002e5ebc21d911fe229b6ec40 +0 -0
- data/sandbox/tmp/cache/assets/D82/480/sprockets%2F8945f20bf64a2469e9edbfb085e4b8c8 +0 -0
- data/sandbox/tmp/cache/assets/D92/120/sprockets%2Fbbadd54a208919f24fb50bc9e3d3d107 +0 -0
- data/sandbox/tmp/cache/assets/D96/940/sprockets%2F6a2c3af22b173eb975f382b4cdcd8c41 +0 -0
- data/sandbox/tmp/cache/assets/D96/E90/sprockets%2F0d5f451deca7e6811663f30afc3a4fd4 +0 -0
- data/sandbox/tmp/cache/assets/D97/180/sprockets%2F933323d1c368cfeda7a0cacdbd295933 +0 -0
- data/sandbox/tmp/cache/assets/DAD/190/sprockets%2Fbee62a5d7a8cd35a33763f1cd879de59 +0 -0
- data/sandbox/tmp/cache/assets/DB8/5B0/sprockets%2F35ed47d3f028fe65e7d4f5f4f8f95e1b +0 -0
- data/sandbox/tmp/cache/assets/DC5/340/sprockets%2Fd0dab495f0bbced215dd46e8ca316575 +0 -0
- data/sandbox/tmp/cache/assets/DCA/FD0/sprockets%2F0b59a77adfc61bb863e708ba262ce6bd +0 -0
- data/sandbox/tmp/cache/assets/DCE/E70/sprockets%2Facff0eeec48ccf1c25a1b968242b7827 +0 -0
- data/sandbox/tmp/cache/assets/DD4/040/sprockets%2Fe4c0c08358fdcc373c5f2da767cd08ff +0 -0
- data/sandbox/tmp/cache/assets/DD7/9B0/sprockets%2F9f2c0826bd7ae7b745ae8da6db48c26e +0 -0
- data/sandbox/tmp/cache/assets/DE0/650/sprockets%2F0f87cd6ed404cdfc99c86bb974d0ca59 +0 -0
- data/sandbox/tmp/cache/assets/DE5/390/sprockets%2F24a1cda6893cebd53997dc859def67cd +0 -0
- data/sandbox/tmp/cache/assets/DFC/5B0/sprockets%2Fcece55fa8f17c917fcc78cf6113a01bb +0 -0
- data/sandbox/tmp/cache/assets/E00/1D0/sprockets%2F571f97ca7b6dfdfefafd03ae735127a1 +0 -0
- data/sandbox/tmp/cache/assets/E05/2E0/sprockets%2F92ecbfa92baf6e394ce6c39d731b7cd2 +0 -0
- data/sandbox/tmp/cache/assets/E36/F20/sprockets%2Fd3bd4977eec9eca9c55b9a20b81b9cdb +0 -0
- data/sandbox/tmp/cache/assets/E37/A20/sprockets%2Fec3ccca6dfe2fc4e8793b0d83398e5ba +0 -0
- data/sandbox/tmp/cache/assets/F1C/0B0/sprockets%2Ffb1cb6e9be4bf2cd5aaecfbb1de7d77f +0 -0
- data/sandbox/tmp/cache/sass/8e0849d701bca2f5a178ed1215813b1316068a4d/components.sassc +0 -0
- data/sandbox/tmp/cache/sass/8e0849d701bca2f5a178ed1215813b1316068a4d/typography.sassc +0 -0
- data/sandbox/tmp/cache/sass/8e0849d701bca2f5a178ed1215813b1316068a4d/variables.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/checkbox.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/combobox.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/container.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/group.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/input.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/radio.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/responsive_box.sassc +0 -0
- data/sandbox/tmp/cache/sass/b49d75942b505963eb360d4c3b43ec26f9ded67e/view.sassc +0 -0
- data/vendor/assets/javascripts/iugu-ux/components/presenters/iugu-ui-table.jst.eco +1 -1
- data/vendor/assets/javascripts/iugu-ux/components/usecode/iugu-ui-button.js.coffee +15 -3
- data/vendor/assets/javascripts/iugu-ux/components/usecode/iugu-ui-combobox.js.coffee +1 -0
- data/vendor/assets/javascripts/iugu-ux/components/usecode/iugu-ui-table.js.coffee +1 -0
- data/vendor/assets/javascripts/iugu-ux/components/usecode/iugu-ui-view.js.coffee +1 -1
- data/vendor/assets/javascripts/vendor.js +2 -3
- data/vendor/assets/javascripts/vendor/backbone.validation.js +1 -1
- data/vendor/assets/javascripts/vendor/numeral.js +2104 -0
- data/vendor/assets/javascripts/vendor/numeral.languages.js +96 -0
- data/vendor/assets/javascripts/web-app/helpers.coffee +1 -1
- data/vendor/assets/stylesheets/iugu-ux/components/combobox.sass +7 -0
- data/vendor/assets/stylesheets/iugu-ux/components/input.sass +11 -0
- data/vendor/assets/stylesheets/iugu-ux/components/notice.sass +6 -0
- metadata +72 -33
- data/vendor/assets/javascripts/vendor/mobiscroll.core-2.3.1.js +0 -966
- data/vendor/assets/javascripts/vendor/mobiscroll.datetime-2.3.js +0 -656
- data/vendor/assets/javascripts/vendor/mobiscroll.select-2.3.1.js +0 -257
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -46,17 +46,19 @@ class IuguUI.Button
|
|
46
46
|
else if input_selector
|
47
47
|
@input_element = $(input_selector)
|
48
48
|
|
49
|
-
if @getInput() and @el.data("value") == @getInput().val()
|
49
|
+
if @getInput() and @el.data("value") and @el.data("value").toString() == @getInput().val().toString()
|
50
50
|
@el.addClass("selected") unless @el.hasClass("selected")
|
51
51
|
|
52
|
-
debug @getInput()
|
53
|
-
|
54
52
|
if @getInput() and @getInput().is(":checkbox") and @getInput().is(":checked")
|
55
53
|
@el.addClass("selected")
|
56
54
|
|
55
|
+
|
57
56
|
if @getInput() and @getInput().is(":radio") and @getInput().is(":checked")
|
58
57
|
@el.addClass("selected")
|
59
58
|
|
59
|
+
if @getInput() and @getInput().is(":radio") or @getInput().is(":checkbox")
|
60
|
+
@getInput().bind "change", @inputChangedCallback
|
61
|
+
|
60
62
|
@el.bind "click", @click
|
61
63
|
|
62
64
|
if @options.hideInput and @input_element
|
@@ -74,6 +76,16 @@ class IuguUI.Button
|
|
74
76
|
return false unless elms and elms.size() > 1
|
75
77
|
true
|
76
78
|
|
79
|
+
inputChangedCallback: ->
|
80
|
+
if @hasLinkedElements()
|
81
|
+
@linkedElements().removeClass("selected")
|
82
|
+
|
83
|
+
if @getInput() and @getInput().is(":checkbox") and @getInput().is(":checked")
|
84
|
+
@el.addClass("selected")
|
85
|
+
|
86
|
+
if @getInput() and @getInput().is(":radio") and @getInput().is(":checked")
|
87
|
+
@el.addClass("selected")
|
88
|
+
|
77
89
|
configureInputElementValue: ( value ) ->
|
78
90
|
return unless @getInput()
|
79
91
|
|
@@ -87,7 +87,7 @@ class IuguUI.View extends IuguUI.Base
|
|
87
87
|
control.parents('.iugu-ui-form-wrapper').addClass "input-with-errors"
|
88
88
|
|
89
89
|
if list.length == 0
|
90
|
-
group.prepend
|
90
|
+
group.prepend "<div class='notice notice-red'><h4 class='notice-heading'>#{_t 'phrases.error_title'}</h4><ul class='error-list'></ul></div>"
|
91
91
|
list = group.find ".error-list"
|
92
92
|
|
93
93
|
new_attr = attr.replace '.', '-'
|
@@ -13,9 +13,6 @@
|
|
13
13
|
//= require ./vendor/jquery.iframe-transport.js
|
14
14
|
//= require ./vendor/jquery.base64.js
|
15
15
|
//= require ./vendor/jquery.fileupload.js
|
16
|
-
//= require ./vendor/mobiscroll.core-2.3.1.js
|
17
|
-
//= require ./vendor/mobiscroll.datetime-2.3.js
|
18
|
-
//= require ./vendor/mobiscroll.select-2.3.1.js
|
19
16
|
//= require ./vendor/underscore.js
|
20
17
|
//= require ./vendor/backbone.js
|
21
18
|
//= require ./vendor/backbone.associations-min.js
|
@@ -26,6 +23,8 @@
|
|
26
23
|
//= require ./vendor/backbone.forms.js
|
27
24
|
//= require ./vendor/backbone.forms.list.js
|
28
25
|
//= require ./vendor/backbone.forms.default.js
|
26
|
+
//= require ./vendor/numeral.js
|
27
|
+
//= require ./vendor/numeral.languages.js
|
29
28
|
//= require ./vendor/rivets.js
|
30
29
|
//= require ./vendor/async.js
|
31
30
|
//= require ./vendor/tasks.js
|
@@ -502,7 +502,7 @@ Backbone.Validation = (function(_){
|
|
502
502
|
internationalized: function(attrName, model) {
|
503
503
|
var right_attr = attrName.split('.');
|
504
504
|
right_attr = right_attr[right_attr.length-1];
|
505
|
-
return _t(model.
|
505
|
+
return _t(model.identifier + '_fields.' + right_attr);
|
506
506
|
}
|
507
507
|
};
|
508
508
|
|
@@ -0,0 +1,2104 @@
|
|
1
|
+
// numeral.js
|
2
|
+
// version : 1.4.7
|
3
|
+
// author : Adam Draper
|
4
|
+
// license : MIT
|
5
|
+
// http://adamwdraper.github.com/Numeral-js/
|
6
|
+
|
7
|
+
(function () {
|
8
|
+
|
9
|
+
/************************************
|
10
|
+
Constants
|
11
|
+
************************************/
|
12
|
+
|
13
|
+
var numeral,
|
14
|
+
VERSION = '1.4.7',
|
15
|
+
// internal storage for language config files
|
16
|
+
languages = {},
|
17
|
+
currentLanguage = 'en',
|
18
|
+
zeroFormat = null,
|
19
|
+
// check for nodeJS
|
20
|
+
hasModule = (typeof module !== 'undefined' && module.exports);
|
21
|
+
|
22
|
+
|
23
|
+
/************************************
|
24
|
+
Constructors
|
25
|
+
************************************/
|
26
|
+
|
27
|
+
|
28
|
+
// Numeral prototype object
|
29
|
+
function Numeral (number) {
|
30
|
+
this._n = number;
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Implementation of toFixed() that treats floats more like decimals
|
35
|
+
*
|
36
|
+
* Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
|
37
|
+
* problems for accounting- and finance-related software.
|
38
|
+
*/
|
39
|
+
function toFixed (value, precision, optionals) {
|
40
|
+
var power = Math.pow(10, precision),
|
41
|
+
output;
|
42
|
+
|
43
|
+
// Multiply up by precision, round accurately, then divide and use native toFixed():
|
44
|
+
output = (Math.round(value * power) / power).toFixed(precision);
|
45
|
+
|
46
|
+
if (optionals) {
|
47
|
+
var optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
|
48
|
+
output = output.replace(optionalsRegExp, '');
|
49
|
+
}
|
50
|
+
|
51
|
+
return output;
|
52
|
+
}
|
53
|
+
|
54
|
+
/************************************
|
55
|
+
Formatting
|
56
|
+
************************************/
|
57
|
+
|
58
|
+
// determine what type of formatting we need to do
|
59
|
+
function formatNumeral (n, format) {
|
60
|
+
var output;
|
61
|
+
|
62
|
+
// figure out what kind of format we are dealing with
|
63
|
+
if (format.indexOf('$') > -1) { // currency!!!!!
|
64
|
+
output = formatCurrency(n, format);
|
65
|
+
} else if (format.indexOf('%') > -1) { // percentage
|
66
|
+
output = formatPercentage(n, format);
|
67
|
+
} else if (format.indexOf(':') > -1) { // time
|
68
|
+
output = formatTime(n, format);
|
69
|
+
} else { // plain ol' numbers or bytes
|
70
|
+
output = formatNumber(n, format);
|
71
|
+
}
|
72
|
+
|
73
|
+
// return string
|
74
|
+
return output;
|
75
|
+
}
|
76
|
+
|
77
|
+
// revert to number
|
78
|
+
function unformatNumeral (n, string) {
|
79
|
+
if (string.indexOf(':') > -1) {
|
80
|
+
n._n = unformatTime(string);
|
81
|
+
} else {
|
82
|
+
if (string === zeroFormat) {
|
83
|
+
n._n = 0;
|
84
|
+
} else {
|
85
|
+
var stringOriginal = string;
|
86
|
+
if (languages[currentLanguage].delimiters.decimal !== '.') {
|
87
|
+
string = string.replace(/\./g,'').replace(languages[currentLanguage].delimiters.decimal, '.');
|
88
|
+
}
|
89
|
+
|
90
|
+
// see if abbreviations are there so that we can multiply to the correct number
|
91
|
+
var thousandRegExp = new RegExp(languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
|
92
|
+
millionRegExp = new RegExp(languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
|
93
|
+
billionRegExp = new RegExp(languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
|
94
|
+
trillionRegExp = new RegExp(languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
|
95
|
+
|
96
|
+
// see if bytes are there so that we can multiply to the correct number
|
97
|
+
var prefixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
98
|
+
bytesMultiplier = false;
|
99
|
+
|
100
|
+
for (var power = 0; power <= prefixes.length; power++) {
|
101
|
+
bytesMultiplier = (string.indexOf(prefixes[power]) > -1) ? Math.pow(1024, power + 1) : false;
|
102
|
+
|
103
|
+
if (bytesMultiplier) {
|
104
|
+
break;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
// do some math to create our number
|
109
|
+
n._n = ((bytesMultiplier) ? bytesMultiplier : 1) * ((stringOriginal.match(thousandRegExp)) ? Math.pow(10, 3) : 1) * ((stringOriginal.match(millionRegExp)) ? Math.pow(10, 6) : 1) * ((stringOriginal.match(billionRegExp)) ? Math.pow(10, 9) : 1) * ((stringOriginal.match(trillionRegExp)) ? Math.pow(10, 12) : 1) * ((string.indexOf('%') > -1) ? 0.01 : 1) * Number(((string.indexOf('(') > -1) ? '-' : '') + string.replace(/[^0-9\.-]+/g, ''));
|
110
|
+
|
111
|
+
// round if we are talking about bytes
|
112
|
+
n._n = (bytesMultiplier) ? Math.ceil(n._n) : n._n;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
return n._n;
|
116
|
+
}
|
117
|
+
|
118
|
+
function formatCurrency (n, format) {
|
119
|
+
var prependSymbol = (format.indexOf('$') <= 1) ? true : false;
|
120
|
+
|
121
|
+
// remove $ for the moment
|
122
|
+
var space = '';
|
123
|
+
|
124
|
+
// check for space before or after currency
|
125
|
+
if (format.indexOf(' $') > -1) {
|
126
|
+
space = ' ';
|
127
|
+
format = format.replace(' $', '');
|
128
|
+
} else if (format.indexOf('$ ') > -1) {
|
129
|
+
space = ' ';
|
130
|
+
format = format.replace('$ ', '');
|
131
|
+
} else {
|
132
|
+
format = format.replace('$', '');
|
133
|
+
}
|
134
|
+
|
135
|
+
// format the number
|
136
|
+
var output = formatNumeral(n, format);
|
137
|
+
|
138
|
+
// position the symbol
|
139
|
+
if (prependSymbol) {
|
140
|
+
if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
|
141
|
+
output = output.split('');
|
142
|
+
output.splice(1, 0, languages[currentLanguage].currency.symbol + space);
|
143
|
+
output = output.join('');
|
144
|
+
} else {
|
145
|
+
output = languages[currentLanguage].currency.symbol + space + output;
|
146
|
+
}
|
147
|
+
} else {
|
148
|
+
if (output.indexOf(')') > -1) {
|
149
|
+
output = output.split('');
|
150
|
+
output.splice(-1, 0, space + languages[currentLanguage].currency.symbol);
|
151
|
+
output = output.join('');
|
152
|
+
} else {
|
153
|
+
output = output + space + languages[currentLanguage].currency.symbol;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return output;
|
158
|
+
}
|
159
|
+
|
160
|
+
function formatPercentage (n, format) {
|
161
|
+
var space = '';
|
162
|
+
// check for space before %
|
163
|
+
if (format.indexOf(' %') > -1) {
|
164
|
+
space = ' ';
|
165
|
+
format = format.replace(' %', '');
|
166
|
+
} else {
|
167
|
+
format = format.replace('%', '');
|
168
|
+
}
|
169
|
+
|
170
|
+
n._n = n._n * 100;
|
171
|
+
var output = formatNumeral(n, format);
|
172
|
+
if (output.indexOf(')') > -1 ) {
|
173
|
+
output = output.split('');
|
174
|
+
output.splice(-1, 0, space + '%');
|
175
|
+
output = output.join('');
|
176
|
+
} else {
|
177
|
+
output = output + space + '%';
|
178
|
+
}
|
179
|
+
return output;
|
180
|
+
}
|
181
|
+
|
182
|
+
function formatTime (n, format) {
|
183
|
+
var hours = Math.floor(n._n/60/60),
|
184
|
+
minutes = Math.floor((n._n - (hours * 60 * 60))/60),
|
185
|
+
seconds = Math.round(n._n - (hours * 60 * 60) - (minutes * 60));
|
186
|
+
return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
|
187
|
+
}
|
188
|
+
|
189
|
+
function unformatTime (string) {
|
190
|
+
var timeArray = string.split(':'),
|
191
|
+
seconds = 0;
|
192
|
+
// turn hours and minutes into seconds and add them all up
|
193
|
+
if (timeArray.length === 3) {
|
194
|
+
// hours
|
195
|
+
seconds = seconds + (Number(timeArray[0]) * 60 * 60);
|
196
|
+
// minutes
|
197
|
+
seconds = seconds + (Number(timeArray[1]) * 60);
|
198
|
+
// seconds
|
199
|
+
seconds = seconds + Number(timeArray[2]);
|
200
|
+
} else if (timeArray.lenght === 2) {
|
201
|
+
// minutes
|
202
|
+
seconds = seconds + (Number(timeArray[0]) * 60);
|
203
|
+
// seconds
|
204
|
+
seconds = seconds + Number(timeArray[1]);
|
205
|
+
}
|
206
|
+
return Number(seconds);
|
207
|
+
}
|
208
|
+
|
209
|
+
function formatNumber (n, format) {
|
210
|
+
var negP = false,
|
211
|
+
optDec = false,
|
212
|
+
abbr = '',
|
213
|
+
bytes = '',
|
214
|
+
ord = '',
|
215
|
+
abs = Math.abs(n._n);
|
216
|
+
|
217
|
+
// check if number is zero and a custom zero format has been set
|
218
|
+
if (n._n === 0 && zeroFormat !== null) {
|
219
|
+
return zeroFormat;
|
220
|
+
} else {
|
221
|
+
// see if we should use parentheses for negative number
|
222
|
+
if (format.indexOf('(') > -1) {
|
223
|
+
negP = true;
|
224
|
+
format = format.slice(1, -1);
|
225
|
+
}
|
226
|
+
|
227
|
+
// see if abbreviation is wanted
|
228
|
+
if (format.indexOf('a') > -1) {
|
229
|
+
// check for space before abbreviation
|
230
|
+
if (format.indexOf(' a') > -1) {
|
231
|
+
abbr = ' ';
|
232
|
+
format = format.replace(' a', '');
|
233
|
+
} else {
|
234
|
+
format = format.replace('a', '');
|
235
|
+
}
|
236
|
+
|
237
|
+
if (abs >= Math.pow(10, 12)) {
|
238
|
+
// trillion
|
239
|
+
abbr = abbr + languages[currentLanguage].abbreviations.trillion;
|
240
|
+
n._n = n._n / Math.pow(10, 12);
|
241
|
+
} else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9)) {
|
242
|
+
// billion
|
243
|
+
abbr = abbr + languages[currentLanguage].abbreviations.billion;
|
244
|
+
n._n = n._n / Math.pow(10, 9);
|
245
|
+
} else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6)) {
|
246
|
+
// million
|
247
|
+
abbr = abbr + languages[currentLanguage].abbreviations.million;
|
248
|
+
n._n = n._n / Math.pow(10, 6);
|
249
|
+
} else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3)) {
|
250
|
+
// thousand
|
251
|
+
abbr = abbr + languages[currentLanguage].abbreviations.thousand;
|
252
|
+
n._n = n._n / Math.pow(10, 3);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
// see if we are formatting bytes
|
257
|
+
if (format.indexOf('b') > -1) {
|
258
|
+
// check for space before
|
259
|
+
if (format.indexOf(' b') > -1) {
|
260
|
+
bytes = ' ';
|
261
|
+
format = format.replace(' b', '');
|
262
|
+
} else {
|
263
|
+
format = format.replace('b', '');
|
264
|
+
}
|
265
|
+
|
266
|
+
var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
267
|
+
min,
|
268
|
+
max;
|
269
|
+
|
270
|
+
for (var power = 0; power <= prefixes.length; power++) {
|
271
|
+
min = Math.pow(1024, power);
|
272
|
+
max = Math.pow(1024, power+1);
|
273
|
+
|
274
|
+
if (n._n >= min && n._n < max) {
|
275
|
+
bytes = bytes + prefixes[power];
|
276
|
+
if (min > 0) {
|
277
|
+
n._n = n._n / min;
|
278
|
+
}
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
// see if ordinal is wanted
|
285
|
+
if (format.indexOf('o') > -1) {
|
286
|
+
// check for space before
|
287
|
+
if (format.indexOf(' o') > -1) {
|
288
|
+
ord = ' ';
|
289
|
+
format = format.replace(' o', '');
|
290
|
+
} else {
|
291
|
+
format = format.replace('o', '');
|
292
|
+
}
|
293
|
+
|
294
|
+
ord = ord + languages[currentLanguage].ordinal(n._n);
|
295
|
+
}
|
296
|
+
|
297
|
+
if (format.indexOf('[.]') > -1) {
|
298
|
+
optDec = true;
|
299
|
+
format = format.replace('[.]', '.');
|
300
|
+
}
|
301
|
+
|
302
|
+
var w = n._n.toString().split('.')[0],
|
303
|
+
precision = format.split('.')[1],
|
304
|
+
thousands = format.indexOf(','),
|
305
|
+
d = '',
|
306
|
+
neg = false;
|
307
|
+
|
308
|
+
if (precision) {
|
309
|
+
if (precision.indexOf('[') > -1) {
|
310
|
+
precision = precision.replace(']', '');
|
311
|
+
precision = precision.split('[');
|
312
|
+
d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length);
|
313
|
+
} else {
|
314
|
+
d = toFixed(n._n, precision.length);
|
315
|
+
}
|
316
|
+
|
317
|
+
w = d.split('.')[0];
|
318
|
+
|
319
|
+
if (d.split('.')[1].length) {
|
320
|
+
d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
|
321
|
+
} else {
|
322
|
+
d = '';
|
323
|
+
}
|
324
|
+
|
325
|
+
if (optDec && Number(d) === 0) {
|
326
|
+
d = '';
|
327
|
+
}
|
328
|
+
} else {
|
329
|
+
w = toFixed(n._n, null);
|
330
|
+
}
|
331
|
+
|
332
|
+
// format number
|
333
|
+
if (w.indexOf('-') > -1) {
|
334
|
+
w = w.slice(1);
|
335
|
+
neg = true;
|
336
|
+
}
|
337
|
+
|
338
|
+
if (thousands > -1) {
|
339
|
+
w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
|
340
|
+
}
|
341
|
+
|
342
|
+
if (format.indexOf('.') === 0) {
|
343
|
+
w = '';
|
344
|
+
}
|
345
|
+
|
346
|
+
return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
/************************************
|
351
|
+
Top Level Functions
|
352
|
+
************************************/
|
353
|
+
|
354
|
+
numeral = function (input) {
|
355
|
+
if (numeral.isNumeral(input)) {
|
356
|
+
input = input.value();
|
357
|
+
} else if (!Number(input)) {
|
358
|
+
input = 0;
|
359
|
+
}
|
360
|
+
|
361
|
+
return new Numeral(Number(input));
|
362
|
+
};
|
363
|
+
|
364
|
+
// version number
|
365
|
+
numeral.version = VERSION;
|
366
|
+
|
367
|
+
// compare numeral object
|
368
|
+
numeral.isNumeral = function (obj) {
|
369
|
+
return obj instanceof Numeral;
|
370
|
+
};
|
371
|
+
|
372
|
+
// This function will load languages and then set the global language. If
|
373
|
+
// no arguments are passed in, it will simply return the current global
|
374
|
+
// language key.
|
375
|
+
numeral.language = function (key, values) {
|
376
|
+
if (!key) {
|
377
|
+
return currentLanguage;
|
378
|
+
}
|
379
|
+
|
380
|
+
if (key && !values) {
|
381
|
+
currentLanguage = key;
|
382
|
+
}
|
383
|
+
|
384
|
+
if (values || !languages[key]) {
|
385
|
+
loadLanguage(key, values);
|
386
|
+
}
|
387
|
+
|
388
|
+
return numeral;
|
389
|
+
};
|
390
|
+
|
391
|
+
numeral.language('en', {
|
392
|
+
delimiters: {
|
393
|
+
thousands: ',',
|
394
|
+
decimal: '.'
|
395
|
+
},
|
396
|
+
abbreviations: {
|
397
|
+
thousand: 'k',
|
398
|
+
million: 'm',
|
399
|
+
billion: 'b',
|
400
|
+
trillion: 't'
|
401
|
+
},
|
402
|
+
ordinal: function (number) {
|
403
|
+
var b = number % 10;
|
404
|
+
return (~~ (number % 100 / 10) === 1) ? 'th' :
|
405
|
+
(b === 1) ? 'st' :
|
406
|
+
(b === 2) ? 'nd' :
|
407
|
+
(b === 3) ? 'rd' : 'th';
|
408
|
+
},
|
409
|
+
currency: {
|
410
|
+
symbol: '$'
|
411
|
+
}
|
412
|
+
});
|
413
|
+
|
414
|
+
numeral.zeroFormat = function (format) {
|
415
|
+
if (typeof(format) === 'string') {
|
416
|
+
zeroFormat = format;
|
417
|
+
} else {
|
418
|
+
zeroFormat = null;
|
419
|
+
}
|
420
|
+
};
|
421
|
+
|
422
|
+
/************************************
|
423
|
+
Helpers
|
424
|
+
************************************/
|
425
|
+
|
426
|
+
function loadLanguage(key, values) {
|
427
|
+
languages[key] = values;
|
428
|
+
}
|
429
|
+
|
430
|
+
|
431
|
+
/************************************
|
432
|
+
Numeral Prototype
|
433
|
+
************************************/
|
434
|
+
|
435
|
+
|
436
|
+
numeral.fn = Numeral.prototype = {
|
437
|
+
|
438
|
+
clone : function () {
|
439
|
+
return numeral(this);
|
440
|
+
},
|
441
|
+
|
442
|
+
format : function (inputString) {
|
443
|
+
return formatNumeral(this, inputString ? inputString : numeral.defaultFormat);
|
444
|
+
},
|
445
|
+
|
446
|
+
unformat : function (inputString) {
|
447
|
+
return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat);
|
448
|
+
},
|
449
|
+
|
450
|
+
value : function () {
|
451
|
+
return this._n;
|
452
|
+
},
|
453
|
+
|
454
|
+
valueOf : function () {
|
455
|
+
return this._n;
|
456
|
+
},
|
457
|
+
|
458
|
+
set : function (value) {
|
459
|
+
this._n = Number(value);
|
460
|
+
return this;
|
461
|
+
},
|
462
|
+
|
463
|
+
add : function (value) {
|
464
|
+
this._n = this._n + Number(value);
|
465
|
+
return this;
|
466
|
+
},
|
467
|
+
|
468
|
+
subtract : function (value) {
|
469
|
+
this._n = this._n - Number(value);
|
470
|
+
return this;
|
471
|
+
},
|
472
|
+
|
473
|
+
multiply : function (value) {
|
474
|
+
this._n = this._n * Number(value);
|
475
|
+
return this;
|
476
|
+
},
|
477
|
+
|
478
|
+
divide : function (value) {
|
479
|
+
this._n = this._n / Number(value);
|
480
|
+
return this;
|
481
|
+
},
|
482
|
+
|
483
|
+
difference : function (value) {
|
484
|
+
var difference = this._n - Number(value);
|
485
|
+
|
486
|
+
if (difference < 0) {
|
487
|
+
difference = -difference;
|
488
|
+
}
|
489
|
+
|
490
|
+
return difference;
|
491
|
+
}
|
492
|
+
|
493
|
+
};
|
494
|
+
|
495
|
+
/************************************
|
496
|
+
Exposing Numeral
|
497
|
+
************************************/
|
498
|
+
|
499
|
+
// CommonJS module is defined
|
500
|
+
if (hasModule) {
|
501
|
+
module.exports = numeral;
|
502
|
+
}
|
503
|
+
|
504
|
+
/*global ender:false */
|
505
|
+
if (typeof ender === 'undefined') {
|
506
|
+
// here, `this` means `window` in the browser, or `global` on the server
|
507
|
+
// add `numeral` as a global object via a string identifier,
|
508
|
+
// for Closure Compiler 'advanced' mode
|
509
|
+
this['numeral'] = numeral;
|
510
|
+
}
|
511
|
+
|
512
|
+
/*global define:false */
|
513
|
+
if (typeof define === 'function' && define.amd) {
|
514
|
+
define([], function () {
|
515
|
+
return numeral;
|
516
|
+
});
|
517
|
+
}
|
518
|
+
}).call(this);
|
519
|
+
|
520
|
+
/*!
|
521
|
+
* jQuery contextMenu - Plugin for simple contextMenu handling
|
522
|
+
*
|
523
|
+
* Version: 1.5.25
|
524
|
+
*
|
525
|
+
* Authors: Rodney Rehm, Addy Osmani (patches for FF)
|
526
|
+
* Web: http://medialize.github.com/jQuery-contextMenu/
|
527
|
+
*
|
528
|
+
* Licensed under
|
529
|
+
* MIT License http://www.opensource.org/licenses/mit-license
|
530
|
+
* GPL v3 http://opensource.org/licenses/GPL-3.0
|
531
|
+
*
|
532
|
+
*/
|
533
|
+
|
534
|
+
(function($, undefined){
|
535
|
+
|
536
|
+
// TODO: -
|
537
|
+
// ARIA stuff: menuitem, menuitemcheckbox und menuitemradio
|
538
|
+
// create <menu> structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative
|
539
|
+
|
540
|
+
// determine html5 compatibility
|
541
|
+
$.support.htmlMenuitem = ('HTMLMenuItemElement' in window);
|
542
|
+
$.support.htmlCommand = ('HTMLCommandElement' in window);
|
543
|
+
$.support.eventSelectstart = ("onselectstart" in document.documentElement);
|
544
|
+
/* // should the need arise, test for css user-select
|
545
|
+
$.support.cssUserSelect = (function(){
|
546
|
+
var t = false,
|
547
|
+
e = document.createElement('div');
|
548
|
+
|
549
|
+
$.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) {
|
550
|
+
var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect',
|
551
|
+
prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select';
|
552
|
+
|
553
|
+
e.style.cssText = prop + ': text;';
|
554
|
+
if (e.style[propCC] == 'text') {
|
555
|
+
t = true;
|
556
|
+
return false;
|
557
|
+
}
|
558
|
+
|
559
|
+
return true;
|
560
|
+
});
|
561
|
+
|
562
|
+
return t;
|
563
|
+
})();
|
564
|
+
*/
|
565
|
+
|
566
|
+
var // currently active contextMenu trigger
|
567
|
+
$currentTrigger = null,
|
568
|
+
// is contextMenu initialized with at least one menu?
|
569
|
+
initialized = false,
|
570
|
+
// window handle
|
571
|
+
$win = $(window),
|
572
|
+
// number of registered menus
|
573
|
+
counter = 0,
|
574
|
+
// mapping selector to namespace
|
575
|
+
namespaces = {},
|
576
|
+
// mapping namespace to options
|
577
|
+
menus = {},
|
578
|
+
// custom command type handlers
|
579
|
+
types = {},
|
580
|
+
// default values
|
581
|
+
defaults = {
|
582
|
+
// selector of contextMenu trigger
|
583
|
+
selector: null,
|
584
|
+
// where to append the menu to
|
585
|
+
appendTo: null,
|
586
|
+
// method to trigger context menu ["right", "left", "hover"]
|
587
|
+
trigger: "right",
|
588
|
+
// hide menu when mouse leaves trigger / menu elements
|
589
|
+
autoHide: false,
|
590
|
+
// ms to wait before showing a hover-triggered context menu
|
591
|
+
delay: 200,
|
592
|
+
// determine position to show menu at
|
593
|
+
determinePosition: function($menu) {
|
594
|
+
// position to the lower middle of the trigger element
|
595
|
+
if ($.ui && $.ui.position) {
|
596
|
+
// .position() is provided as a jQuery UI utility
|
597
|
+
// (...and it won't work on hidden elements)
|
598
|
+
$menu.css('display', 'block').position({
|
599
|
+
my: "center top",
|
600
|
+
at: "center bottom",
|
601
|
+
of: this,
|
602
|
+
offset: "0 5",
|
603
|
+
collision: "fit"
|
604
|
+
}).css('display', 'none');
|
605
|
+
} else {
|
606
|
+
// determine contextMenu position
|
607
|
+
var offset = this.offset();
|
608
|
+
offset.top += this.outerHeight();
|
609
|
+
offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2;
|
610
|
+
$menu.css(offset);
|
611
|
+
}
|
612
|
+
},
|
613
|
+
// position menu
|
614
|
+
position: function(opt, x, y) {
|
615
|
+
var $this = this,
|
616
|
+
offset;
|
617
|
+
// determine contextMenu position
|
618
|
+
if (!x && !y) {
|
619
|
+
opt.determinePosition.call(this, opt.$menu);
|
620
|
+
return;
|
621
|
+
} else if (x === "maintain" && y === "maintain") {
|
622
|
+
// x and y must not be changed (after re-show on command click)
|
623
|
+
offset = opt.$menu.position();
|
624
|
+
} else {
|
625
|
+
// x and y are given (by mouse event)
|
626
|
+
var triggerIsFixed = opt.$trigger.parents().andSelf()
|
627
|
+
.filter(function() {
|
628
|
+
return $(this).css('position') == "fixed";
|
629
|
+
}).length;
|
630
|
+
|
631
|
+
if (triggerIsFixed) {
|
632
|
+
y -= $win.scrollTop();
|
633
|
+
x -= $win.scrollLeft();
|
634
|
+
}
|
635
|
+
offset = {top: y, left: x};
|
636
|
+
}
|
637
|
+
|
638
|
+
// correct offset if viewport demands it
|
639
|
+
var bottom = $win.scrollTop() + $win.height(),
|
640
|
+
right = $win.scrollLeft() + $win.width(),
|
641
|
+
height = opt.$menu.height(),
|
642
|
+
width = opt.$menu.width();
|
643
|
+
|
644
|
+
if (offset.top + height > bottom) {
|
645
|
+
offset.top -= height;
|
646
|
+
}
|
647
|
+
|
648
|
+
if (offset.left + width > right) {
|
649
|
+
offset.left -= width;
|
650
|
+
}
|
651
|
+
|
652
|
+
opt.$menu.css(offset);
|
653
|
+
},
|
654
|
+
// position the sub-menu
|
655
|
+
positionSubmenu: function($menu) {
|
656
|
+
if ($.ui && $.ui.position) {
|
657
|
+
// .position() is provided as a jQuery UI utility
|
658
|
+
// (...and it won't work on hidden elements)
|
659
|
+
$menu.css('display', 'block').position({
|
660
|
+
my: "left top",
|
661
|
+
at: "right top",
|
662
|
+
of: this,
|
663
|
+
collision: "fit"
|
664
|
+
}).css('display', '');
|
665
|
+
} else {
|
666
|
+
// determine contextMenu position
|
667
|
+
var offset = {
|
668
|
+
top: 0,
|
669
|
+
left: this.outerWidth()
|
670
|
+
};
|
671
|
+
$menu.css(offset);
|
672
|
+
}
|
673
|
+
},
|
674
|
+
// offset to add to zIndex
|
675
|
+
zIndex: 1,
|
676
|
+
// show hide animation settings
|
677
|
+
animation: {
|
678
|
+
duration: 50,
|
679
|
+
show: 'slideDown',
|
680
|
+
hide: 'slideUp'
|
681
|
+
},
|
682
|
+
// events
|
683
|
+
events: {
|
684
|
+
show: $.noop,
|
685
|
+
hide: $.noop
|
686
|
+
},
|
687
|
+
// default callback
|
688
|
+
callback: null,
|
689
|
+
// list of contextMenu items
|
690
|
+
items: {}
|
691
|
+
},
|
692
|
+
// mouse position for hover activation
|
693
|
+
hoveract = {
|
694
|
+
timer: null,
|
695
|
+
pageX: null,
|
696
|
+
pageY: null
|
697
|
+
},
|
698
|
+
// determine zIndex
|
699
|
+
zindex = function($t) {
|
700
|
+
var zin = 0,
|
701
|
+
$tt = $t;
|
702
|
+
|
703
|
+
while (true) {
|
704
|
+
zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0);
|
705
|
+
$tt = $tt.parent();
|
706
|
+
if (!$tt || !$tt.length || "html body".indexOf($tt.prop('nodeName').toLowerCase()) > -1 ) {
|
707
|
+
break;
|
708
|
+
}
|
709
|
+
}
|
710
|
+
|
711
|
+
return zin;
|
712
|
+
},
|
713
|
+
// event handlers
|
714
|
+
handle = {
|
715
|
+
// abort anything
|
716
|
+
abortevent: function(e){
|
717
|
+
e.preventDefault();
|
718
|
+
e.stopImmediatePropagation();
|
719
|
+
},
|
720
|
+
|
721
|
+
// contextmenu show dispatcher
|
722
|
+
contextmenu: function(e) {
|
723
|
+
var $this = $(this);
|
724
|
+
|
725
|
+
// disable actual context-menu
|
726
|
+
e.preventDefault();
|
727
|
+
e.stopImmediatePropagation();
|
728
|
+
|
729
|
+
// abort native-triggered events unless we're triggering on right click
|
730
|
+
if (e.data.trigger != 'right' && e.originalEvent) {
|
731
|
+
return;
|
732
|
+
}
|
733
|
+
|
734
|
+
if (!$this.hasClass('context-menu-disabled')) {
|
735
|
+
// theoretically need to fire a show event at <menu>
|
736
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus
|
737
|
+
// var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this });
|
738
|
+
// e.data.$menu.trigger(evt);
|
739
|
+
|
740
|
+
$currentTrigger = $this;
|
741
|
+
if (e.data.build) {
|
742
|
+
var built = e.data.build($currentTrigger, e);
|
743
|
+
// abort if build() returned false
|
744
|
+
if (built === false) {
|
745
|
+
return;
|
746
|
+
}
|
747
|
+
|
748
|
+
// dynamically build menu on invocation
|
749
|
+
e.data = $.extend(true, {}, defaults, e.data, built || {});
|
750
|
+
|
751
|
+
// abort if there are no items to display
|
752
|
+
if (!e.data.items || $.isEmptyObject(e.data.items)) {
|
753
|
+
// Note: jQuery captures and ignores errors from event handlers
|
754
|
+
if (window.console) {
|
755
|
+
(console.error || console.log)("No items specified to show in contextMenu");
|
756
|
+
}
|
757
|
+
|
758
|
+
throw new Error('No Items sepcified');
|
759
|
+
}
|
760
|
+
|
761
|
+
// backreference for custom command type creation
|
762
|
+
e.data.$trigger = $currentTrigger;
|
763
|
+
|
764
|
+
op.create(e.data);
|
765
|
+
}
|
766
|
+
// show menu
|
767
|
+
op.show.call($this, e.data, e.pageX, e.pageY);
|
768
|
+
}
|
769
|
+
},
|
770
|
+
// contextMenu left-click trigger
|
771
|
+
click: function(e) {
|
772
|
+
e.preventDefault();
|
773
|
+
e.stopImmediatePropagation();
|
774
|
+
$(this).trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));
|
775
|
+
},
|
776
|
+
// contextMenu right-click trigger
|
777
|
+
mousedown: function(e) {
|
778
|
+
// register mouse down
|
779
|
+
var $this = $(this);
|
780
|
+
|
781
|
+
// hide any previous menus
|
782
|
+
if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) {
|
783
|
+
$currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide');
|
784
|
+
}
|
785
|
+
|
786
|
+
// activate on right click
|
787
|
+
if (e.button == 2) {
|
788
|
+
$currentTrigger = $this.data('contextMenuActive', true);
|
789
|
+
}
|
790
|
+
},
|
791
|
+
// contextMenu right-click trigger
|
792
|
+
mouseup: function(e) {
|
793
|
+
// show menu
|
794
|
+
var $this = $(this);
|
795
|
+
if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) {
|
796
|
+
e.preventDefault();
|
797
|
+
e.stopImmediatePropagation();
|
798
|
+
$currentTrigger = $this;
|
799
|
+
$this.trigger($.Event("contextmenu", { data: e.data, pageX: e.pageX, pageY: e.pageY }));
|
800
|
+
}
|
801
|
+
|
802
|
+
$this.removeData('contextMenuActive');
|
803
|
+
},
|
804
|
+
// contextMenu hover trigger
|
805
|
+
mouseenter: function(e) {
|
806
|
+
var $this = $(this),
|
807
|
+
$related = $(e.relatedTarget),
|
808
|
+
$document = $(document);
|
809
|
+
|
810
|
+
// abort if we're coming from a menu
|
811
|
+
if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {
|
812
|
+
return;
|
813
|
+
}
|
814
|
+
|
815
|
+
// abort if a menu is shown
|
816
|
+
if ($currentTrigger && $currentTrigger.length) {
|
817
|
+
return;
|
818
|
+
}
|
819
|
+
|
820
|
+
hoveract.pageX = e.pageX;
|
821
|
+
hoveract.pageY = e.pageY;
|
822
|
+
hoveract.data = e.data;
|
823
|
+
$document.on('mousemove.contextMenuShow', handle.mousemove);
|
824
|
+
hoveract.timer = setTimeout(function() {
|
825
|
+
hoveract.timer = null;
|
826
|
+
$document.off('mousemove.contextMenuShow');
|
827
|
+
$currentTrigger = $this;
|
828
|
+
$this.trigger($.Event("contextmenu", { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY }));
|
829
|
+
}, e.data.delay );
|
830
|
+
},
|
831
|
+
// contextMenu hover trigger
|
832
|
+
mousemove: function(e) {
|
833
|
+
hoveract.pageX = e.pageX;
|
834
|
+
hoveract.pageY = e.pageY;
|
835
|
+
},
|
836
|
+
// contextMenu hover trigger
|
837
|
+
mouseleave: function(e) {
|
838
|
+
// abort if we're leaving for a menu
|
839
|
+
var $related = $(e.relatedTarget);
|
840
|
+
if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) {
|
841
|
+
return;
|
842
|
+
}
|
843
|
+
|
844
|
+
try {
|
845
|
+
clearTimeout(hoveract.timer);
|
846
|
+
} catch(e) {}
|
847
|
+
|
848
|
+
hoveract.timer = null;
|
849
|
+
},
|
850
|
+
|
851
|
+
// click on layer to hide contextMenu
|
852
|
+
layerClick: function(e) {
|
853
|
+
var $this = $(this),
|
854
|
+
root = $this.data('contextMenuRoot'),
|
855
|
+
mouseup = false,
|
856
|
+
button = e.button,
|
857
|
+
x = e.pageX,
|
858
|
+
y = e.pageY,
|
859
|
+
target,
|
860
|
+
offset,
|
861
|
+
selectors;
|
862
|
+
|
863
|
+
e.preventDefault();
|
864
|
+
e.stopImmediatePropagation();
|
865
|
+
|
866
|
+
// This hack looks about as ugly as it is
|
867
|
+
// Firefox 12 (at least) fires the contextmenu event directly "after" mousedown
|
868
|
+
// for some reason `root.$layer.hide(); document.elementFromPoint()` causes this
|
869
|
+
// contextmenu event to be triggered on the uncovered element instead of on the
|
870
|
+
// layer (where every other sane browser, including Firefox nightly at the time)
|
871
|
+
// triggers the event. This workaround might be obsolete by September 2012.
|
872
|
+
$this.on('mouseup', function() {
|
873
|
+
mouseup = true;
|
874
|
+
});
|
875
|
+
setTimeout(function() {
|
876
|
+
var $window, hideshow;
|
877
|
+
// test if we need to reposition the menu
|
878
|
+
if ((root.trigger == 'left' && button == 0) || (root.trigger == 'right' && button == 2)) {
|
879
|
+
if (document.elementFromPoint) {
|
880
|
+
root.$layer.hide();
|
881
|
+
target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop());
|
882
|
+
root.$layer.show();
|
883
|
+
|
884
|
+
selectors = [];
|
885
|
+
for (var s in namespaces) {
|
886
|
+
selectors.push(s);
|
887
|
+
}
|
888
|
+
|
889
|
+
target = $(target).closest(selectors.join(', '));
|
890
|
+
|
891
|
+
if (target.length) {
|
892
|
+
if (target.is(root.$trigger[0])) {
|
893
|
+
root.position.call(root.$trigger, root, x, y);
|
894
|
+
return;
|
895
|
+
}
|
896
|
+
}
|
897
|
+
} else {
|
898
|
+
offset = root.$trigger.offset();
|
899
|
+
$window = $(window);
|
900
|
+
// while this looks kinda awful, it's the best way to avoid
|
901
|
+
// unnecessarily calculating any positions
|
902
|
+
offset.top += $window.scrollTop();
|
903
|
+
if (offset.top <= e.pageY) {
|
904
|
+
offset.left += $window.scrollLeft();
|
905
|
+
if (offset.left <= e.pageX) {
|
906
|
+
offset.bottom = offset.top + root.$trigger.outerHeight();
|
907
|
+
if (offset.bottom >= e.pageY) {
|
908
|
+
offset.right = offset.left + root.$trigger.outerWidth();
|
909
|
+
if (offset.right >= e.pageX) {
|
910
|
+
// reposition
|
911
|
+
root.position.call(root.$trigger, root, x, y);
|
912
|
+
return;
|
913
|
+
}
|
914
|
+
}
|
915
|
+
}
|
916
|
+
}
|
917
|
+
}
|
918
|
+
}
|
919
|
+
|
920
|
+
hideshow = function(e) {
|
921
|
+
if (e) {
|
922
|
+
e.preventDefault();
|
923
|
+
e.stopImmediatePropagation();
|
924
|
+
}
|
925
|
+
|
926
|
+
root.$menu.trigger('contextmenu:hide');
|
927
|
+
if (target && target.length) {
|
928
|
+
setTimeout(function() {
|
929
|
+
target.contextMenu({x: x, y: y});
|
930
|
+
}, 50);
|
931
|
+
}
|
932
|
+
};
|
933
|
+
|
934
|
+
if (mouseup) {
|
935
|
+
// mouseup has already happened
|
936
|
+
hideshow();
|
937
|
+
} else {
|
938
|
+
// remove only after mouseup has completed
|
939
|
+
$this.on('mouseup', hideshow);
|
940
|
+
}
|
941
|
+
}, 50);
|
942
|
+
},
|
943
|
+
// key handled :hover
|
944
|
+
keyStop: function(e, opt) {
|
945
|
+
if (!opt.isInput) {
|
946
|
+
e.preventDefault();
|
947
|
+
}
|
948
|
+
|
949
|
+
e.stopPropagation();
|
950
|
+
},
|
951
|
+
key: function(e) {
|
952
|
+
var opt = $currentTrigger.data('contextMenu') || {},
|
953
|
+
$children = opt.$menu.children(),
|
954
|
+
$round;
|
955
|
+
|
956
|
+
switch (e.keyCode) {
|
957
|
+
case 9:
|
958
|
+
case 38: // up
|
959
|
+
handle.keyStop(e, opt);
|
960
|
+
// if keyCode is [38 (up)] or [9 (tab) with shift]
|
961
|
+
if (opt.isInput) {
|
962
|
+
if (e.keyCode == 9 && e.shiftKey) {
|
963
|
+
e.preventDefault();
|
964
|
+
opt.$selected && opt.$selected.find('input, textarea, select').blur();
|
965
|
+
opt.$menu.trigger('prevcommand');
|
966
|
+
return;
|
967
|
+
} else if (e.keyCode == 38 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {
|
968
|
+
// checkboxes don't capture this key
|
969
|
+
e.preventDefault();
|
970
|
+
return;
|
971
|
+
}
|
972
|
+
} else if (e.keyCode != 9 || e.shiftKey) {
|
973
|
+
opt.$menu.trigger('prevcommand');
|
974
|
+
return;
|
975
|
+
}
|
976
|
+
// omitting break;
|
977
|
+
|
978
|
+
// case 9: // tab - reached through omitted break;
|
979
|
+
case 40: // down
|
980
|
+
handle.keyStop(e, opt);
|
981
|
+
if (opt.isInput) {
|
982
|
+
if (e.keyCode == 9) {
|
983
|
+
e.preventDefault();
|
984
|
+
opt.$selected && opt.$selected.find('input, textarea, select').blur();
|
985
|
+
opt.$menu.trigger('nextcommand');
|
986
|
+
return;
|
987
|
+
} else if (e.keyCode == 40 && opt.$selected.find('input, textarea, select').prop('type') == 'checkbox') {
|
988
|
+
// checkboxes don't capture this key
|
989
|
+
e.preventDefault();
|
990
|
+
return;
|
991
|
+
}
|
992
|
+
} else {
|
993
|
+
opt.$menu.trigger('nextcommand');
|
994
|
+
return;
|
995
|
+
}
|
996
|
+
break;
|
997
|
+
|
998
|
+
case 37: // left
|
999
|
+
handle.keyStop(e, opt);
|
1000
|
+
if (opt.isInput || !opt.$selected || !opt.$selected.length) {
|
1001
|
+
break;
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
if (!opt.$selected.parent().hasClass('context-menu-root')) {
|
1005
|
+
var $parent = opt.$selected.parent().parent();
|
1006
|
+
opt.$selected.trigger('contextmenu:blur');
|
1007
|
+
opt.$selected = $parent;
|
1008
|
+
return;
|
1009
|
+
}
|
1010
|
+
break;
|
1011
|
+
|
1012
|
+
case 39: // right
|
1013
|
+
handle.keyStop(e, opt);
|
1014
|
+
if (opt.isInput || !opt.$selected || !opt.$selected.length) {
|
1015
|
+
break;
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
var itemdata = opt.$selected.data('contextMenu') || {};
|
1019
|
+
if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) {
|
1020
|
+
opt.$selected = null;
|
1021
|
+
itemdata.$selected = null;
|
1022
|
+
itemdata.$menu.trigger('nextcommand');
|
1023
|
+
return;
|
1024
|
+
}
|
1025
|
+
break;
|
1026
|
+
|
1027
|
+
case 35: // end
|
1028
|
+
case 36: // home
|
1029
|
+
if (opt.$selected && opt.$selected.find('input, textarea, select').length) {
|
1030
|
+
return;
|
1031
|
+
} else {
|
1032
|
+
(opt.$selected && opt.$selected.parent() || opt.$menu)
|
1033
|
+
.children(':not(.disabled, .not-selectable)')[e.keyCode == 36 ? 'first' : 'last']()
|
1034
|
+
.trigger('contextmenu:focus');
|
1035
|
+
e.preventDefault();
|
1036
|
+
return;
|
1037
|
+
}
|
1038
|
+
break;
|
1039
|
+
|
1040
|
+
case 13: // enter
|
1041
|
+
handle.keyStop(e, opt);
|
1042
|
+
if (opt.isInput) {
|
1043
|
+
if (opt.$selected && !opt.$selected.is('textarea, select')) {
|
1044
|
+
e.preventDefault();
|
1045
|
+
return;
|
1046
|
+
}
|
1047
|
+
break;
|
1048
|
+
}
|
1049
|
+
opt.$selected && opt.$selected.trigger('mouseup');
|
1050
|
+
return;
|
1051
|
+
|
1052
|
+
case 32: // space
|
1053
|
+
case 33: // page up
|
1054
|
+
case 34: // page down
|
1055
|
+
// prevent browser from scrolling down while menu is visible
|
1056
|
+
handle.keyStop(e, opt);
|
1057
|
+
return;
|
1058
|
+
|
1059
|
+
case 27: // esc
|
1060
|
+
handle.keyStop(e, opt);
|
1061
|
+
opt.$menu.trigger('contextmenu:hide');
|
1062
|
+
return;
|
1063
|
+
|
1064
|
+
default: // 0-9, a-z
|
1065
|
+
var k = (String.fromCharCode(e.keyCode)).toUpperCase();
|
1066
|
+
if (opt.accesskeys[k]) {
|
1067
|
+
// according to the specs accesskeys must be invoked immediately
|
1068
|
+
opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu
|
1069
|
+
? 'contextmenu:focus'
|
1070
|
+
: 'mouseup'
|
1071
|
+
);
|
1072
|
+
return;
|
1073
|
+
}
|
1074
|
+
break;
|
1075
|
+
}
|
1076
|
+
// pass event to selected item,
|
1077
|
+
// stop propagation to avoid endless recursion
|
1078
|
+
e.stopPropagation();
|
1079
|
+
opt.$selected && opt.$selected.trigger(e);
|
1080
|
+
},
|
1081
|
+
|
1082
|
+
// select previous possible command in menu
|
1083
|
+
prevItem: function(e) {
|
1084
|
+
e.stopPropagation();
|
1085
|
+
var opt = $(this).data('contextMenu') || {};
|
1086
|
+
|
1087
|
+
// obtain currently selected menu
|
1088
|
+
if (opt.$selected) {
|
1089
|
+
var $s = opt.$selected;
|
1090
|
+
opt = opt.$selected.parent().data('contextMenu') || {};
|
1091
|
+
opt.$selected = $s;
|
1092
|
+
}
|
1093
|
+
|
1094
|
+
var $children = opt.$menu.children(),
|
1095
|
+
$prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(),
|
1096
|
+
$round = $prev;
|
1097
|
+
|
1098
|
+
// skip disabled
|
1099
|
+
while ($prev.hasClass('disabled') || $prev.hasClass('not-selectable')) {
|
1100
|
+
if ($prev.prev().length) {
|
1101
|
+
$prev = $prev.prev();
|
1102
|
+
} else {
|
1103
|
+
$prev = $children.last();
|
1104
|
+
}
|
1105
|
+
if ($prev.is($round)) {
|
1106
|
+
// break endless loop
|
1107
|
+
return;
|
1108
|
+
}
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
// leave current
|
1112
|
+
if (opt.$selected) {
|
1113
|
+
handle.itemMouseleave.call(opt.$selected.get(0), e);
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
// activate next
|
1117
|
+
handle.itemMouseenter.call($prev.get(0), e);
|
1118
|
+
|
1119
|
+
// focus input
|
1120
|
+
var $input = $prev.find('input, textarea, select');
|
1121
|
+
if ($input.length) {
|
1122
|
+
$input.focus();
|
1123
|
+
}
|
1124
|
+
},
|
1125
|
+
// select next possible command in menu
|
1126
|
+
nextItem: function(e) {
|
1127
|
+
e.stopPropagation();
|
1128
|
+
var opt = $(this).data('contextMenu') || {};
|
1129
|
+
|
1130
|
+
// obtain currently selected menu
|
1131
|
+
if (opt.$selected) {
|
1132
|
+
var $s = opt.$selected;
|
1133
|
+
opt = opt.$selected.parent().data('contextMenu') || {};
|
1134
|
+
opt.$selected = $s;
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
var $children = opt.$menu.children(),
|
1138
|
+
$next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(),
|
1139
|
+
$round = $next;
|
1140
|
+
|
1141
|
+
// skip disabled
|
1142
|
+
while ($next.hasClass('disabled') || $next.hasClass('not-selectable')) {
|
1143
|
+
if ($next.next().length) {
|
1144
|
+
$next = $next.next();
|
1145
|
+
} else {
|
1146
|
+
$next = $children.first();
|
1147
|
+
}
|
1148
|
+
if ($next.is($round)) {
|
1149
|
+
// break endless loop
|
1150
|
+
return;
|
1151
|
+
}
|
1152
|
+
}
|
1153
|
+
|
1154
|
+
// leave current
|
1155
|
+
if (opt.$selected) {
|
1156
|
+
handle.itemMouseleave.call(opt.$selected.get(0), e);
|
1157
|
+
}
|
1158
|
+
|
1159
|
+
// activate next
|
1160
|
+
handle.itemMouseenter.call($next.get(0), e);
|
1161
|
+
|
1162
|
+
// focus input
|
1163
|
+
var $input = $next.find('input, textarea, select');
|
1164
|
+
if ($input.length) {
|
1165
|
+
$input.focus();
|
1166
|
+
}
|
1167
|
+
},
|
1168
|
+
|
1169
|
+
// flag that we're inside an input so the key handler can act accordingly
|
1170
|
+
focusInput: function(e) {
|
1171
|
+
var $this = $(this).closest('.context-menu-item'),
|
1172
|
+
data = $this.data(),
|
1173
|
+
opt = data.contextMenu,
|
1174
|
+
root = data.contextMenuRoot;
|
1175
|
+
|
1176
|
+
root.$selected = opt.$selected = $this;
|
1177
|
+
root.isInput = opt.isInput = true;
|
1178
|
+
},
|
1179
|
+
// flag that we're inside an input so the key handler can act accordingly
|
1180
|
+
blurInput: function(e) {
|
1181
|
+
var $this = $(this).closest('.context-menu-item'),
|
1182
|
+
data = $this.data(),
|
1183
|
+
opt = data.contextMenu,
|
1184
|
+
root = data.contextMenuRoot;
|
1185
|
+
|
1186
|
+
root.isInput = opt.isInput = false;
|
1187
|
+
},
|
1188
|
+
|
1189
|
+
// :hover on menu
|
1190
|
+
menuMouseenter: function(e) {
|
1191
|
+
var root = $(this).data().contextMenuRoot;
|
1192
|
+
root.hovering = true;
|
1193
|
+
},
|
1194
|
+
// :hover on menu
|
1195
|
+
menuMouseleave: function(e) {
|
1196
|
+
var root = $(this).data().contextMenuRoot;
|
1197
|
+
if (root.$layer && root.$layer.is(e.relatedTarget)) {
|
1198
|
+
root.hovering = false;
|
1199
|
+
}
|
1200
|
+
},
|
1201
|
+
|
1202
|
+
// :hover done manually so key handling is possible
|
1203
|
+
itemMouseenter: function(e) {
|
1204
|
+
var $this = $(this),
|
1205
|
+
data = $this.data(),
|
1206
|
+
opt = data.contextMenu,
|
1207
|
+
root = data.contextMenuRoot;
|
1208
|
+
|
1209
|
+
root.hovering = true;
|
1210
|
+
|
1211
|
+
// abort if we're re-entering
|
1212
|
+
if (e && root.$layer && root.$layer.is(e.relatedTarget)) {
|
1213
|
+
e.preventDefault();
|
1214
|
+
e.stopImmediatePropagation();
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
// make sure only one item is selected
|
1218
|
+
(opt.$menu ? opt : root).$menu
|
1219
|
+
.children('.hover').trigger('contextmenu:blur');
|
1220
|
+
|
1221
|
+
if ($this.hasClass('disabled') || $this.hasClass('not-selectable')) {
|
1222
|
+
opt.$selected = null;
|
1223
|
+
return;
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
$this.trigger('contextmenu:focus');
|
1227
|
+
},
|
1228
|
+
// :hover done manually so key handling is possible
|
1229
|
+
itemMouseleave: function(e) {
|
1230
|
+
var $this = $(this),
|
1231
|
+
data = $this.data(),
|
1232
|
+
opt = data.contextMenu,
|
1233
|
+
root = data.contextMenuRoot;
|
1234
|
+
|
1235
|
+
if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) {
|
1236
|
+
root.$selected && root.$selected.trigger('contextmenu:blur');
|
1237
|
+
e.preventDefault();
|
1238
|
+
e.stopImmediatePropagation();
|
1239
|
+
root.$selected = opt.$selected = opt.$node;
|
1240
|
+
return;
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
$this.trigger('contextmenu:blur');
|
1244
|
+
},
|
1245
|
+
// contextMenu item click
|
1246
|
+
itemClick: function(e) {
|
1247
|
+
var $this = $(this),
|
1248
|
+
data = $this.data(),
|
1249
|
+
opt = data.contextMenu,
|
1250
|
+
root = data.contextMenuRoot,
|
1251
|
+
key = data.contextMenuKey,
|
1252
|
+
callback;
|
1253
|
+
|
1254
|
+
// abort if the key is unknown or disabled or is a menu
|
1255
|
+
if (!opt.items[key] || $this.hasClass('disabled') || $this.hasClass('context-menu-submenu')) {
|
1256
|
+
return;
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
e.preventDefault();
|
1260
|
+
e.stopImmediatePropagation();
|
1261
|
+
|
1262
|
+
if ($.isFunction(root.callbacks[key])) {
|
1263
|
+
// item-specific callback
|
1264
|
+
callback = root.callbacks[key];
|
1265
|
+
} else if ($.isFunction(root.callback)) {
|
1266
|
+
// default callback
|
1267
|
+
callback = root.callback;
|
1268
|
+
} else {
|
1269
|
+
// no callback, no action
|
1270
|
+
return;
|
1271
|
+
}
|
1272
|
+
|
1273
|
+
// hide menu if callback doesn't stop that
|
1274
|
+
if (callback.call(root.$trigger, key, root) !== false) {
|
1275
|
+
root.$menu.trigger('contextmenu:hide');
|
1276
|
+
} else if (root.$menu.parent().length) {
|
1277
|
+
op.update.call(root.$trigger, root);
|
1278
|
+
}
|
1279
|
+
},
|
1280
|
+
// ignore click events on input elements
|
1281
|
+
inputClick: function(e) {
|
1282
|
+
e.stopImmediatePropagation();
|
1283
|
+
},
|
1284
|
+
|
1285
|
+
// hide <menu>
|
1286
|
+
hideMenu: function(e, data) {
|
1287
|
+
var root = $(this).data('contextMenuRoot');
|
1288
|
+
op.hide.call(root.$trigger, root, data && data.force);
|
1289
|
+
},
|
1290
|
+
// focus <command>
|
1291
|
+
focusItem: function(e) {
|
1292
|
+
e.stopPropagation();
|
1293
|
+
var $this = $(this),
|
1294
|
+
data = $this.data(),
|
1295
|
+
opt = data.contextMenu,
|
1296
|
+
root = data.contextMenuRoot;
|
1297
|
+
|
1298
|
+
$this.addClass('hover')
|
1299
|
+
.siblings('.hover').trigger('contextmenu:blur');
|
1300
|
+
|
1301
|
+
// remember selected
|
1302
|
+
opt.$selected = root.$selected = $this;
|
1303
|
+
|
1304
|
+
// position sub-menu - do after show so dumb $.ui.position can keep up
|
1305
|
+
if (opt.$node) {
|
1306
|
+
root.positionSubmenu.call(opt.$node, opt.$menu);
|
1307
|
+
}
|
1308
|
+
},
|
1309
|
+
// blur <command>
|
1310
|
+
blurItem: function(e) {
|
1311
|
+
e.stopPropagation();
|
1312
|
+
var $this = $(this),
|
1313
|
+
data = $this.data(),
|
1314
|
+
opt = data.contextMenu,
|
1315
|
+
root = data.contextMenuRoot;
|
1316
|
+
|
1317
|
+
$this.removeClass('hover');
|
1318
|
+
opt.$selected = null;
|
1319
|
+
}
|
1320
|
+
},
|
1321
|
+
// operations
|
1322
|
+
op = {
|
1323
|
+
show: function(opt, x, y) {
|
1324
|
+
var $this = $(this),
|
1325
|
+
offset,
|
1326
|
+
css = {};
|
1327
|
+
|
1328
|
+
// hide any open menus
|
1329
|
+
$('#context-menu-layer').trigger('mousedown');
|
1330
|
+
|
1331
|
+
// backreference for callbacks
|
1332
|
+
opt.$trigger = $this;
|
1333
|
+
|
1334
|
+
// show event
|
1335
|
+
if (opt.events.show.call($this, opt) === false) {
|
1336
|
+
$currentTrigger = null;
|
1337
|
+
return;
|
1338
|
+
}
|
1339
|
+
|
1340
|
+
// create or update context menu
|
1341
|
+
op.update.call($this, opt);
|
1342
|
+
|
1343
|
+
// position menu
|
1344
|
+
opt.position.call($this, opt, x, y);
|
1345
|
+
|
1346
|
+
// make sure we're in front
|
1347
|
+
if (opt.zIndex) {
|
1348
|
+
css.zIndex = zindex($this) + opt.zIndex;
|
1349
|
+
}
|
1350
|
+
|
1351
|
+
// add layer
|
1352
|
+
op.layer.call(opt.$menu, opt, css.zIndex);
|
1353
|
+
|
1354
|
+
// adjust sub-menu zIndexes
|
1355
|
+
opt.$menu.find('ul').css('zIndex', css.zIndex + 1);
|
1356
|
+
|
1357
|
+
// position and show context menu
|
1358
|
+
opt.$menu.css( css )[opt.animation.show](opt.animation.duration);
|
1359
|
+
// make options available
|
1360
|
+
$this.data('contextMenu', opt);
|
1361
|
+
// register key handler
|
1362
|
+
$(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key);
|
1363
|
+
// register autoHide handler
|
1364
|
+
if (opt.autoHide) {
|
1365
|
+
// trigger element coordinates
|
1366
|
+
var pos = $this.position();
|
1367
|
+
pos.right = pos.left + $this.outerWidth();
|
1368
|
+
pos.bottom = pos.top + this.outerHeight();
|
1369
|
+
// mouse position handler
|
1370
|
+
$(document).on('mousemove.contextMenuAutoHide', function(e) {
|
1371
|
+
if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) {
|
1372
|
+
// if mouse in menu...
|
1373
|
+
opt.$menu.trigger('contextmenu:hide');
|
1374
|
+
}
|
1375
|
+
});
|
1376
|
+
}
|
1377
|
+
},
|
1378
|
+
hide: function(opt, force) {
|
1379
|
+
var $this = $(this);
|
1380
|
+
if (!opt) {
|
1381
|
+
opt = $this.data('contextMenu') || {};
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
// hide event
|
1385
|
+
if (!force && opt.events && opt.events.hide.call($this, opt) === false) {
|
1386
|
+
return;
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
if (opt.$layer) {
|
1390
|
+
// keep layer for a bit so the contextmenu event can be aborted properly by opera
|
1391
|
+
setTimeout((function($layer){ return function(){
|
1392
|
+
$layer.remove();
|
1393
|
+
};
|
1394
|
+
})(opt.$layer), 10);
|
1395
|
+
|
1396
|
+
try {
|
1397
|
+
delete opt.$layer;
|
1398
|
+
} catch(e) {
|
1399
|
+
opt.$layer = null;
|
1400
|
+
}
|
1401
|
+
}
|
1402
|
+
|
1403
|
+
// remove handle
|
1404
|
+
$currentTrigger = null;
|
1405
|
+
// remove selected
|
1406
|
+
opt.$menu.find('.hover').trigger('contextmenu:blur');
|
1407
|
+
opt.$selected = null;
|
1408
|
+
// unregister key and mouse handlers
|
1409
|
+
//$(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705
|
1410
|
+
$(document).off('.contextMenuAutoHide').off('keydown.contextMenu');
|
1411
|
+
// hide menu
|
1412
|
+
opt.$menu && opt.$menu[opt.animation.hide](opt.animation.duration, function (){
|
1413
|
+
// tear down dynamically built menu after animation is completed.
|
1414
|
+
if (opt.build) {
|
1415
|
+
opt.$menu.remove();
|
1416
|
+
$.each(opt, function(key, value) {
|
1417
|
+
switch (key) {
|
1418
|
+
case 'ns':
|
1419
|
+
case 'selector':
|
1420
|
+
case 'build':
|
1421
|
+
case 'trigger':
|
1422
|
+
return true;
|
1423
|
+
|
1424
|
+
default:
|
1425
|
+
opt[key] = undefined;
|
1426
|
+
try {
|
1427
|
+
delete opt[key];
|
1428
|
+
} catch (e) {}
|
1429
|
+
return true;
|
1430
|
+
}
|
1431
|
+
});
|
1432
|
+
}
|
1433
|
+
});
|
1434
|
+
},
|
1435
|
+
create: function(opt, root) {
|
1436
|
+
if (root === undefined) {
|
1437
|
+
root = opt;
|
1438
|
+
}
|
1439
|
+
// create contextMenu
|
1440
|
+
opt.$menu = $('<ul class="context-menu-list ' + (opt.className || "") + '"></ul>').data({
|
1441
|
+
'contextMenu': opt,
|
1442
|
+
'contextMenuRoot': root
|
1443
|
+
});
|
1444
|
+
|
1445
|
+
$.each(['callbacks', 'commands', 'inputs'], function(i,k){
|
1446
|
+
opt[k] = {};
|
1447
|
+
if (!root[k]) {
|
1448
|
+
root[k] = {};
|
1449
|
+
}
|
1450
|
+
});
|
1451
|
+
|
1452
|
+
root.accesskeys || (root.accesskeys = {});
|
1453
|
+
|
1454
|
+
// create contextMenu items
|
1455
|
+
$.each(opt.items, function(key, item){
|
1456
|
+
var $t = $('<li class="context-menu-item ' + (item.className || "") +'"></li>'),
|
1457
|
+
$label = null,
|
1458
|
+
$input = null;
|
1459
|
+
|
1460
|
+
item.$node = $t.data({
|
1461
|
+
'contextMenu': opt,
|
1462
|
+
'contextMenuRoot': root,
|
1463
|
+
'contextMenuKey': key
|
1464
|
+
});
|
1465
|
+
|
1466
|
+
// register accesskey
|
1467
|
+
// NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that
|
1468
|
+
if (item.accesskey) {
|
1469
|
+
var aks = splitAccesskey(item.accesskey);
|
1470
|
+
for (var i=0, ak; ak = aks[i]; i++) {
|
1471
|
+
if (!root.accesskeys[ak]) {
|
1472
|
+
root.accesskeys[ak] = item;
|
1473
|
+
item._name = item.name.replace(new RegExp('(' + ak + ')', 'i'), '<span class="context-menu-accesskey">$1</span>');
|
1474
|
+
break;
|
1475
|
+
}
|
1476
|
+
}
|
1477
|
+
}
|
1478
|
+
|
1479
|
+
if (typeof item == "string") {
|
1480
|
+
$t.addClass('context-menu-separator not-selectable');
|
1481
|
+
} else if (item.type && types[item.type]) {
|
1482
|
+
// run custom type handler
|
1483
|
+
types[item.type].call($t, item, opt, root);
|
1484
|
+
// register commands
|
1485
|
+
$.each([opt, root], function(i,k){
|
1486
|
+
k.commands[key] = item;
|
1487
|
+
if ($.isFunction(item.callback)) {
|
1488
|
+
k.callbacks[key] = item.callback;
|
1489
|
+
}
|
1490
|
+
});
|
1491
|
+
} else {
|
1492
|
+
// add label for input
|
1493
|
+
if (item.type == 'html') {
|
1494
|
+
$t.addClass('context-menu-html not-selectable');
|
1495
|
+
} else if (item.type) {
|
1496
|
+
$label = $('<label></label>').appendTo($t);
|
1497
|
+
$('<span></span>').html(item._name || item.name).appendTo($label);
|
1498
|
+
$t.addClass('context-menu-input');
|
1499
|
+
opt.hasTypes = true;
|
1500
|
+
$.each([opt, root], function(i,k){
|
1501
|
+
k.commands[key] = item;
|
1502
|
+
k.inputs[key] = item;
|
1503
|
+
});
|
1504
|
+
} else if (item.items) {
|
1505
|
+
item.type = 'sub';
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
switch (item.type) {
|
1509
|
+
case 'text':
|
1510
|
+
$input = $('<input type="text" value="1" name="context-menu-input-'+ key +'" value="">')
|
1511
|
+
.val(item.value || "").appendTo($label);
|
1512
|
+
break;
|
1513
|
+
|
1514
|
+
case 'textarea':
|
1515
|
+
$input = $('<textarea name="context-menu-input-'+ key +'"></textarea>')
|
1516
|
+
.val(item.value || "").appendTo($label);
|
1517
|
+
|
1518
|
+
if (item.height) {
|
1519
|
+
$input.height(item.height);
|
1520
|
+
}
|
1521
|
+
break;
|
1522
|
+
|
1523
|
+
case 'checkbox':
|
1524
|
+
$input = $('<input type="checkbox" value="1" name="context-menu-input-'+ key +'" value="">')
|
1525
|
+
.val(item.value || "").prop("checked", !!item.selected).prependTo($label);
|
1526
|
+
break;
|
1527
|
+
|
1528
|
+
case 'radio':
|
1529
|
+
$input = $('<input type="radio" value="1" name="context-menu-input-'+ item.radio +'" value="">')
|
1530
|
+
.val(item.value || "").prop("checked", !!item.selected).prependTo($label);
|
1531
|
+
break;
|
1532
|
+
|
1533
|
+
case 'select':
|
1534
|
+
$input = $('<select name="context-menu-input-'+ key +'">').appendTo($label);
|
1535
|
+
if (item.options) {
|
1536
|
+
$.each(item.options, function(value, text) {
|
1537
|
+
$('<option></option>').val(value).text(text).appendTo($input);
|
1538
|
+
});
|
1539
|
+
$input.val(item.selected);
|
1540
|
+
}
|
1541
|
+
break;
|
1542
|
+
|
1543
|
+
case 'sub':
|
1544
|
+
$('<span></span>').html(item._name || item.name).appendTo($t);
|
1545
|
+
item.appendTo = item.$node;
|
1546
|
+
op.create(item, root);
|
1547
|
+
$t.data('contextMenu', item).addClass('context-menu-submenu');
|
1548
|
+
item.callback = null;
|
1549
|
+
break;
|
1550
|
+
|
1551
|
+
case 'html':
|
1552
|
+
$(item.html).appendTo($t);
|
1553
|
+
break;
|
1554
|
+
|
1555
|
+
default:
|
1556
|
+
$.each([opt, root], function(i,k){
|
1557
|
+
k.commands[key] = item;
|
1558
|
+
if ($.isFunction(item.callback)) {
|
1559
|
+
k.callbacks[key] = item.callback;
|
1560
|
+
}
|
1561
|
+
});
|
1562
|
+
|
1563
|
+
$('<span></span>').html(item._name || item.name || "").appendTo($t);
|
1564
|
+
break;
|
1565
|
+
}
|
1566
|
+
|
1567
|
+
// disable key listener in <input>
|
1568
|
+
if (item.type && item.type != 'sub' && item.type != 'html') {
|
1569
|
+
$input
|
1570
|
+
.on('focus', handle.focusInput)
|
1571
|
+
.on('blur', handle.blurInput);
|
1572
|
+
|
1573
|
+
if (item.events) {
|
1574
|
+
$input.on(item.events, opt);
|
1575
|
+
}
|
1576
|
+
}
|
1577
|
+
|
1578
|
+
// add icons
|
1579
|
+
if (item.icon) {
|
1580
|
+
$t.addClass("icon icon-" + item.icon);
|
1581
|
+
}
|
1582
|
+
}
|
1583
|
+
|
1584
|
+
// cache contained elements
|
1585
|
+
item.$input = $input;
|
1586
|
+
item.$label = $label;
|
1587
|
+
|
1588
|
+
// attach item to menu
|
1589
|
+
$t.appendTo(opt.$menu);
|
1590
|
+
|
1591
|
+
// Disable text selection
|
1592
|
+
if (!opt.hasTypes && $.support.eventSelectstart) {
|
1593
|
+
// browsers support user-select: none,
|
1594
|
+
// IE has a special event for text-selection
|
1595
|
+
// browsers supporting neither will not be preventing text-selection
|
1596
|
+
$t.on('selectstart.disableTextSelect', handle.abortevent);
|
1597
|
+
}
|
1598
|
+
});
|
1599
|
+
// attach contextMenu to <body> (to bypass any possible overflow:hidden issues on parents of the trigger element)
|
1600
|
+
if (!opt.$node) {
|
1601
|
+
opt.$menu.css('display', 'none').addClass('context-menu-root');
|
1602
|
+
}
|
1603
|
+
opt.$menu.appendTo(opt.appendTo || document.body);
|
1604
|
+
},
|
1605
|
+
update: function(opt, root) {
|
1606
|
+
var $this = this;
|
1607
|
+
if (root === undefined) {
|
1608
|
+
root = opt;
|
1609
|
+
// determine widths of submenus, as CSS won't grow them automatically
|
1610
|
+
// position:absolute > position:absolute; min-width:100; max-width:200; results in width: 100;
|
1611
|
+
// kinda sucks hard...
|
1612
|
+
opt.$menu.find('ul').andSelf().css({position: 'static', display: 'block'}).each(function(){
|
1613
|
+
var $this = $(this);
|
1614
|
+
$this.width($this.css('position', 'absolute').width())
|
1615
|
+
.css('position', 'static');
|
1616
|
+
}).css({position: '', display: ''});
|
1617
|
+
}
|
1618
|
+
// re-check disabled for each item
|
1619
|
+
opt.$menu.children().each(function(){
|
1620
|
+
var $item = $(this),
|
1621
|
+
key = $item.data('contextMenuKey'),
|
1622
|
+
item = opt.items[key],
|
1623
|
+
disabled = ($.isFunction(item.disabled) && item.disabled.call($this, key, root)) || item.disabled === true;
|
1624
|
+
|
1625
|
+
// dis- / enable item
|
1626
|
+
$item[disabled ? 'addClass' : 'removeClass']('disabled');
|
1627
|
+
|
1628
|
+
if (item.type) {
|
1629
|
+
// dis- / enable input elements
|
1630
|
+
$item.find('input, select, textarea').prop('disabled', disabled);
|
1631
|
+
|
1632
|
+
// update input states
|
1633
|
+
switch (item.type) {
|
1634
|
+
case 'text':
|
1635
|
+
case 'textarea':
|
1636
|
+
item.$input.val(item.value || "");
|
1637
|
+
break;
|
1638
|
+
|
1639
|
+
case 'checkbox':
|
1640
|
+
case 'radio':
|
1641
|
+
item.$input.val(item.value || "").prop('checked', !!item.selected);
|
1642
|
+
break;
|
1643
|
+
|
1644
|
+
case 'select':
|
1645
|
+
item.$input.val(item.selected || "");
|
1646
|
+
break;
|
1647
|
+
}
|
1648
|
+
}
|
1649
|
+
|
1650
|
+
if (item.$menu) {
|
1651
|
+
// update sub-menu
|
1652
|
+
op.update.call($this, item, root);
|
1653
|
+
}
|
1654
|
+
});
|
1655
|
+
},
|
1656
|
+
layer: function(opt, zIndex) {
|
1657
|
+
// add transparent layer for click area
|
1658
|
+
// filter and background for Internet Explorer, Issue #23
|
1659
|
+
var $layer = opt.$layer = $('<div id="context-menu-layer" style="position:fixed; z-index:' + zIndex + '; top:0; left:0; opacity: 0; filter: alpha(opacity=0); background-color: #000;"></div>')
|
1660
|
+
.css({height: $win.height(), width: $win.width(), display: 'block'})
|
1661
|
+
.data('contextMenuRoot', opt)
|
1662
|
+
.insertBefore(this)
|
1663
|
+
.on('contextmenu', handle.abortevent)
|
1664
|
+
.on('mousedown', handle.layerClick);
|
1665
|
+
|
1666
|
+
// IE6 doesn't know position:fixed;
|
1667
|
+
if (!$.support.fixedPosition) {
|
1668
|
+
$layer.css({
|
1669
|
+
'position' : 'absolute',
|
1670
|
+
'height' : $(document).height()
|
1671
|
+
});
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
return $layer;
|
1675
|
+
}
|
1676
|
+
};
|
1677
|
+
|
1678
|
+
// split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key
|
1679
|
+
function splitAccesskey(val) {
|
1680
|
+
var t = val.split(/\s+/),
|
1681
|
+
keys = [];
|
1682
|
+
|
1683
|
+
for (var i=0, k; k = t[i]; i++) {
|
1684
|
+
k = k[0].toUpperCase(); // first character only
|
1685
|
+
// theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it.
|
1686
|
+
// a map to look up already used access keys would be nice
|
1687
|
+
keys.push(k);
|
1688
|
+
}
|
1689
|
+
|
1690
|
+
return keys;
|
1691
|
+
}
|
1692
|
+
|
1693
|
+
// handle contextMenu triggers
|
1694
|
+
$.fn.contextMenu = function(operation) {
|
1695
|
+
if (operation === undefined) {
|
1696
|
+
this.first().trigger('contextmenu');
|
1697
|
+
} else if (operation.x && operation.y) {
|
1698
|
+
this.first().trigger($.Event("contextmenu", {pageX: operation.x, pageY: operation.y}));
|
1699
|
+
} else if (operation === "hide") {
|
1700
|
+
var $menu = this.data('contextMenu').$menu;
|
1701
|
+
$menu && $menu.trigger('contextmenu:hide');
|
1702
|
+
} else if (operation) {
|
1703
|
+
this.removeClass('context-menu-disabled');
|
1704
|
+
} else if (!operation) {
|
1705
|
+
this.addClass('context-menu-disabled');
|
1706
|
+
}
|
1707
|
+
|
1708
|
+
return this;
|
1709
|
+
};
|
1710
|
+
|
1711
|
+
// manage contextMenu instances
|
1712
|
+
$.contextMenu = function(operation, options) {
|
1713
|
+
if (typeof operation != 'string') {
|
1714
|
+
options = operation;
|
1715
|
+
operation = 'create';
|
1716
|
+
}
|
1717
|
+
|
1718
|
+
if (typeof options == 'string') {
|
1719
|
+
options = {selector: options};
|
1720
|
+
} else if (options === undefined) {
|
1721
|
+
options = {};
|
1722
|
+
}
|
1723
|
+
|
1724
|
+
// merge with default options
|
1725
|
+
var o = $.extend(true, {}, defaults, options || {}),
|
1726
|
+
$document = $(document);
|
1727
|
+
|
1728
|
+
switch (operation) {
|
1729
|
+
case 'create':
|
1730
|
+
// no selector no joy
|
1731
|
+
if (!o.selector) {
|
1732
|
+
throw new Error('No selector specified');
|
1733
|
+
}
|
1734
|
+
// make sure internal classes are not bound to
|
1735
|
+
if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) {
|
1736
|
+
throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className');
|
1737
|
+
}
|
1738
|
+
if (!o.build && (!o.items || $.isEmptyObject(o.items))) {
|
1739
|
+
throw new Error('No Items sepcified');
|
1740
|
+
}
|
1741
|
+
counter ++;
|
1742
|
+
o.ns = '.contextMenu' + counter;
|
1743
|
+
namespaces[o.selector] = o.ns;
|
1744
|
+
menus[o.ns] = o;
|
1745
|
+
|
1746
|
+
// default to right click
|
1747
|
+
if (!o.trigger) {
|
1748
|
+
o.trigger = 'right';
|
1749
|
+
}
|
1750
|
+
|
1751
|
+
if (!initialized) {
|
1752
|
+
// make sure item click is registered first
|
1753
|
+
$document
|
1754
|
+
.on({
|
1755
|
+
'contextmenu:hide.contextMenu': handle.hideMenu,
|
1756
|
+
'prevcommand.contextMenu': handle.prevItem,
|
1757
|
+
'nextcommand.contextMenu': handle.nextItem,
|
1758
|
+
'contextmenu.contextMenu': handle.abortevent,
|
1759
|
+
'mouseenter.contextMenu': handle.menuMouseenter,
|
1760
|
+
'mouseleave.contextMenu': handle.menuMouseleave
|
1761
|
+
}, '.context-menu-list')
|
1762
|
+
.on('mouseup.contextMenu', '.context-menu-input', handle.inputClick)
|
1763
|
+
.on({
|
1764
|
+
'mouseup.contextMenu': handle.itemClick,
|
1765
|
+
'contextmenu:focus.contextMenu': handle.focusItem,
|
1766
|
+
'contextmenu:blur.contextMenu': handle.blurItem,
|
1767
|
+
'contextmenu.contextMenu': handle.abortevent,
|
1768
|
+
'mouseenter.contextMenu': handle.itemMouseenter,
|
1769
|
+
'mouseleave.contextMenu': handle.itemMouseleave
|
1770
|
+
}, '.context-menu-item');
|
1771
|
+
|
1772
|
+
initialized = true;
|
1773
|
+
}
|
1774
|
+
|
1775
|
+
// engage native contextmenu event
|
1776
|
+
$document
|
1777
|
+
.on('contextmenu' + o.ns, o.selector, o, handle.contextmenu);
|
1778
|
+
|
1779
|
+
switch (o.trigger) {
|
1780
|
+
case 'hover':
|
1781
|
+
$document
|
1782
|
+
.on('mouseenter' + o.ns, o.selector, o, handle.mouseenter)
|
1783
|
+
.on('mouseleave' + o.ns, o.selector, o, handle.mouseleave);
|
1784
|
+
break;
|
1785
|
+
|
1786
|
+
case 'left':
|
1787
|
+
$document.on('click' + o.ns, o.selector, o, handle.click);
|
1788
|
+
break;
|
1789
|
+
/*
|
1790
|
+
default:
|
1791
|
+
// http://www.quirksmode.org/dom/events/contextmenu.html
|
1792
|
+
$document
|
1793
|
+
.on('mousedown' + o.ns, o.selector, o, handle.mousedown)
|
1794
|
+
.on('mouseup' + o.ns, o.selector, o, handle.mouseup);
|
1795
|
+
break;
|
1796
|
+
*/
|
1797
|
+
}
|
1798
|
+
|
1799
|
+
// create menu
|
1800
|
+
if (!o.build) {
|
1801
|
+
op.create(o);
|
1802
|
+
}
|
1803
|
+
break;
|
1804
|
+
|
1805
|
+
case 'destroy':
|
1806
|
+
if (!o.selector) {
|
1807
|
+
$document.off('.contextMenu .contextMenuAutoHide');
|
1808
|
+
$.each(namespaces, function(key, value) {
|
1809
|
+
$document.off(value);
|
1810
|
+
});
|
1811
|
+
|
1812
|
+
namespaces = {};
|
1813
|
+
menus = {};
|
1814
|
+
counter = 0;
|
1815
|
+
initialized = false;
|
1816
|
+
|
1817
|
+
$('#context-menu-layer, .context-menu-list').remove();
|
1818
|
+
} else if (namespaces[o.selector]) {
|
1819
|
+
var $visibleMenu = $('.context-menu-list').filter(':visible');
|
1820
|
+
if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) {
|
1821
|
+
$visibleMenu.trigger('contextmenu:hide', {force: true});
|
1822
|
+
}
|
1823
|
+
|
1824
|
+
try {
|
1825
|
+
if (menus[namespaces[o.selector]].$menu) {
|
1826
|
+
menus[namespaces[o.selector]].$menu.remove();
|
1827
|
+
}
|
1828
|
+
|
1829
|
+
delete menus[namespaces[o.selector]];
|
1830
|
+
} catch(e) {
|
1831
|
+
menus[namespaces[o.selector]] = null;
|
1832
|
+
}
|
1833
|
+
|
1834
|
+
$document.off(namespaces[o.selector]);
|
1835
|
+
}
|
1836
|
+
break;
|
1837
|
+
|
1838
|
+
case 'html5':
|
1839
|
+
// if <command> or <menuitem> are not handled by the browser,
|
1840
|
+
// or options was a bool true,
|
1841
|
+
// initialize $.contextMenu for them
|
1842
|
+
if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options == "boolean" && options)) {
|
1843
|
+
$('menu[type="context"]').each(function() {
|
1844
|
+
if (this.id) {
|
1845
|
+
$.contextMenu({
|
1846
|
+
selector: '[contextmenu=' + this.id +']',
|
1847
|
+
items: $.contextMenu.fromMenu(this)
|
1848
|
+
});
|
1849
|
+
}
|
1850
|
+
}).css('display', 'none');
|
1851
|
+
}
|
1852
|
+
break;
|
1853
|
+
|
1854
|
+
default:
|
1855
|
+
throw new Error('Unknown operation "' + operation + '"');
|
1856
|
+
}
|
1857
|
+
|
1858
|
+
return this;
|
1859
|
+
};
|
1860
|
+
|
1861
|
+
// import values into <input> commands
|
1862
|
+
$.contextMenu.setInputValues = function(opt, data) {
|
1863
|
+
if (data === undefined) {
|
1864
|
+
data = {};
|
1865
|
+
}
|
1866
|
+
|
1867
|
+
$.each(opt.inputs, function(key, item) {
|
1868
|
+
switch (item.type) {
|
1869
|
+
case 'text':
|
1870
|
+
case 'textarea':
|
1871
|
+
item.value = data[key] || "";
|
1872
|
+
break;
|
1873
|
+
|
1874
|
+
case 'checkbox':
|
1875
|
+
item.selected = data[key] ? true : false;
|
1876
|
+
break;
|
1877
|
+
|
1878
|
+
case 'radio':
|
1879
|
+
item.selected = (data[item.radio] || "") == item.value ? true : false;
|
1880
|
+
break;
|
1881
|
+
|
1882
|
+
case 'select':
|
1883
|
+
item.selected = data[key] || "";
|
1884
|
+
break;
|
1885
|
+
}
|
1886
|
+
});
|
1887
|
+
};
|
1888
|
+
|
1889
|
+
// export values from <input> commands
|
1890
|
+
$.contextMenu.getInputValues = function(opt, data) {
|
1891
|
+
if (data === undefined) {
|
1892
|
+
data = {};
|
1893
|
+
}
|
1894
|
+
|
1895
|
+
$.each(opt.inputs, function(key, item) {
|
1896
|
+
switch (item.type) {
|
1897
|
+
case 'text':
|
1898
|
+
case 'textarea':
|
1899
|
+
case 'select':
|
1900
|
+
data[key] = item.$input.val();
|
1901
|
+
break;
|
1902
|
+
|
1903
|
+
case 'checkbox':
|
1904
|
+
data[key] = item.$input.prop('checked');
|
1905
|
+
break;
|
1906
|
+
|
1907
|
+
case 'radio':
|
1908
|
+
if (item.$input.prop('checked')) {
|
1909
|
+
data[item.radio] = item.value;
|
1910
|
+
}
|
1911
|
+
break;
|
1912
|
+
}
|
1913
|
+
});
|
1914
|
+
|
1915
|
+
return data;
|
1916
|
+
};
|
1917
|
+
|
1918
|
+
// find <label for="xyz">
|
1919
|
+
function inputLabel(node) {
|
1920
|
+
return (node.id && $('label[for="'+ node.id +'"]').val()) || node.name;
|
1921
|
+
}
|
1922
|
+
|
1923
|
+
// convert <menu> to items object
|
1924
|
+
function menuChildren(items, $children, counter) {
|
1925
|
+
if (!counter) {
|
1926
|
+
counter = 0;
|
1927
|
+
}
|
1928
|
+
|
1929
|
+
$children.each(function() {
|
1930
|
+
var $node = $(this),
|
1931
|
+
node = this,
|
1932
|
+
nodeName = this.nodeName.toLowerCase(),
|
1933
|
+
label,
|
1934
|
+
item;
|
1935
|
+
|
1936
|
+
// extract <label><input>
|
1937
|
+
if (nodeName == 'label' && $node.find('input, textarea, select').length) {
|
1938
|
+
label = $node.text();
|
1939
|
+
$node = $node.children().first();
|
1940
|
+
node = $node.get(0);
|
1941
|
+
nodeName = node.nodeName.toLowerCase();
|
1942
|
+
}
|
1943
|
+
|
1944
|
+
/*
|
1945
|
+
* <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items.
|
1946
|
+
* Not being the sadistic kind, $.contextMenu only accepts:
|
1947
|
+
* <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>.
|
1948
|
+
* Everything else will be imported as an html node, which is not interfaced with contextMenu.
|
1949
|
+
*/
|
1950
|
+
|
1951
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command
|
1952
|
+
switch (nodeName) {
|
1953
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element
|
1954
|
+
case 'menu':
|
1955
|
+
item = {name: $node.attr('label'), items: {}};
|
1956
|
+
counter = menuChildren(item.items, $node.children(), counter);
|
1957
|
+
break;
|
1958
|
+
|
1959
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command
|
1960
|
+
case 'a':
|
1961
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command
|
1962
|
+
case 'button':
|
1963
|
+
item = {
|
1964
|
+
name: $node.text(),
|
1965
|
+
disabled: !!$node.attr('disabled'),
|
1966
|
+
callback: (function(){ return function(){ $node.click(); }; })()
|
1967
|
+
};
|
1968
|
+
break;
|
1969
|
+
|
1970
|
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command
|
1971
|
+
|
1972
|
+
case 'menuitem':
|
1973
|
+
case 'command':
|
1974
|
+
switch ($node.attr('type')) {
|
1975
|
+
case undefined:
|
1976
|
+
case 'command':
|
1977
|
+
case 'menuitem':
|
1978
|
+
item = {
|
1979
|
+
name: $node.attr('label'),
|
1980
|
+
disabled: !!$node.attr('disabled'),
|
1981
|
+
callback: (function(){ return function(){ $node.click(); }; })()
|
1982
|
+
};
|
1983
|
+
break;
|
1984
|
+
|
1985
|
+
case 'checkbox':
|
1986
|
+
item = {
|
1987
|
+
type: 'checkbox',
|
1988
|
+
disabled: !!$node.attr('disabled'),
|
1989
|
+
name: $node.attr('label'),
|
1990
|
+
selected: !!$node.attr('checked')
|
1991
|
+
};
|
1992
|
+
break;
|
1993
|
+
|
1994
|
+
case 'radio':
|
1995
|
+
item = {
|
1996
|
+
type: 'radio',
|
1997
|
+
disabled: !!$node.attr('disabled'),
|
1998
|
+
name: $node.attr('label'),
|
1999
|
+
radio: $node.attr('radiogroup'),
|
2000
|
+
value: $node.attr('id'),
|
2001
|
+
selected: !!$node.attr('checked')
|
2002
|
+
};
|
2003
|
+
break;
|
2004
|
+
|
2005
|
+
default:
|
2006
|
+
item = undefined;
|
2007
|
+
}
|
2008
|
+
break;
|
2009
|
+
|
2010
|
+
case 'hr':
|
2011
|
+
item = '-------';
|
2012
|
+
break;
|
2013
|
+
|
2014
|
+
case 'input':
|
2015
|
+
switch ($node.attr('type')) {
|
2016
|
+
case 'text':
|
2017
|
+
item = {
|
2018
|
+
type: 'text',
|
2019
|
+
name: label || inputLabel(node),
|
2020
|
+
disabled: !!$node.attr('disabled'),
|
2021
|
+
value: $node.val()
|
2022
|
+
};
|
2023
|
+
break;
|
2024
|
+
|
2025
|
+
case 'checkbox':
|
2026
|
+
item = {
|
2027
|
+
type: 'checkbox',
|
2028
|
+
name: label || inputLabel(node),
|
2029
|
+
disabled: !!$node.attr('disabled'),
|
2030
|
+
selected: !!$node.attr('checked')
|
2031
|
+
};
|
2032
|
+
break;
|
2033
|
+
|
2034
|
+
case 'radio':
|
2035
|
+
item = {
|
2036
|
+
type: 'radio',
|
2037
|
+
name: label || inputLabel(node),
|
2038
|
+
disabled: !!$node.attr('disabled'),
|
2039
|
+
radio: !!$node.attr('name'),
|
2040
|
+
value: $node.val(),
|
2041
|
+
selected: !!$node.attr('checked')
|
2042
|
+
};
|
2043
|
+
break;
|
2044
|
+
|
2045
|
+
default:
|
2046
|
+
item = undefined;
|
2047
|
+
break;
|
2048
|
+
}
|
2049
|
+
break;
|
2050
|
+
|
2051
|
+
case 'select':
|
2052
|
+
item = {
|
2053
|
+
type: 'select',
|
2054
|
+
name: label || inputLabel(node),
|
2055
|
+
disabled: !!$node.attr('disabled'),
|
2056
|
+
selected: $node.val(),
|
2057
|
+
options: {}
|
2058
|
+
};
|
2059
|
+
$node.children().each(function(){
|
2060
|
+
item.options[this.value] = $(this).text();
|
2061
|
+
});
|
2062
|
+
break;
|
2063
|
+
|
2064
|
+
case 'textarea':
|
2065
|
+
item = {
|
2066
|
+
type: 'textarea',
|
2067
|
+
name: label || inputLabel(node),
|
2068
|
+
disabled: !!$node.attr('disabled'),
|
2069
|
+
value: $node.val()
|
2070
|
+
};
|
2071
|
+
break;
|
2072
|
+
|
2073
|
+
case 'label':
|
2074
|
+
break;
|
2075
|
+
|
2076
|
+
default:
|
2077
|
+
item = {type: 'html', html: $node.clone(true)};
|
2078
|
+
break;
|
2079
|
+
}
|
2080
|
+
|
2081
|
+
if (item) {
|
2082
|
+
counter++;
|
2083
|
+
items['key' + counter] = item;
|
2084
|
+
}
|
2085
|
+
});
|
2086
|
+
|
2087
|
+
return counter;
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
// convert html5 menu
|
2091
|
+
$.contextMenu.fromMenu = function(element) {
|
2092
|
+
var $this = $(element),
|
2093
|
+
items = {};
|
2094
|
+
|
2095
|
+
menuChildren(items, $this.children());
|
2096
|
+
|
2097
|
+
return items;
|
2098
|
+
};
|
2099
|
+
|
2100
|
+
// make defaults accessible
|
2101
|
+
$.contextMenu.defaults = defaults;
|
2102
|
+
$.contextMenu.types = types;
|
2103
|
+
|
2104
|
+
})(jQuery);
|