alertifyjs-rails 0.1.0
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/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/alertifyjs-rails.gemspec +19 -0
- data/app/helpers/alertifyjs_helper.rb +23 -0
- data/lib/alertifyjs-rails.rb +2 -0
- data/lib/alertifyjs/rails.rb +9 -0
- data/lib/alertifyjs/rails/engine.rb +6 -0
- data/lib/alertifyjs/rails/railtie.rb +11 -0
- data/lib/alertifyjs/rails/version.rb +6 -0
- data/vendor/assets/javascripts/alertify.js +2950 -0
- data/vendor/assets/stylesheets/alertify.css +798 -0
- data/vendor/assets/stylesheets/alertify.rtl.css +798 -0
- data/vendor/assets/stylesheets/alertify/bootstrap.css +34 -0
- data/vendor/assets/stylesheets/alertify/bootstrap.rtl.css +34 -0
- data/vendor/assets/stylesheets/alertify/default.css +63 -0
- data/vendor/assets/stylesheets/alertify/default.rtl.css +63 -0
- data/vendor/assets/stylesheets/alertify/semantic.css +59 -0
- data/vendor/assets/stylesheets/alertify/semantic.rtl.css +59 -0
- metadata +65 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 mkhairi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# alertifyjs-rails
|
2
|
+
|
3
|
+
This gem provides [alertify.js](http://alertifyjs.com/) (v0.4.0) for Rails.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
In your Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'alertifyjs-rails'
|
12
|
+
```
|
13
|
+
|
14
|
+
or system wide:
|
15
|
+
|
16
|
+
```console
|
17
|
+
$ gem install alertifyjs-rails
|
18
|
+
```
|
19
|
+
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The alertify files will be added to the asset pipeline and available for you to use. Add the following line to `app/assets/javascripts/application.js`
|
24
|
+
|
25
|
+
```javascript
|
26
|
+
//= require alertify
|
27
|
+
```
|
28
|
+
|
29
|
+
In order to get the CSS, add the following line to `app/assets/stylesheets/application.css.scss`
|
30
|
+
|
31
|
+
```css
|
32
|
+
/*
|
33
|
+
*= require alertify
|
34
|
+
*= require alertify/default
|
35
|
+
*= require alertify/bootstrap
|
36
|
+
*/
|
37
|
+
```
|
38
|
+
|
39
|
+
flash helper, add the following line in layout
|
40
|
+
|
41
|
+
```html
|
42
|
+
<div id="flash_messages"><%= alertify_flash %></div>
|
43
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'alertifyjs/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "alertifyjs-rails"
|
8
|
+
gem.version = Alertifyjs::Rails::VERSION
|
9
|
+
gem.authors = ["mkhairi"]
|
10
|
+
|
11
|
+
gem.description = %q{Use Alertify.js (alertifyjs.com) with Rails 3 and 4}
|
12
|
+
gem.summary = %q{This gem provides the Alertify.js (alertifyjs.com) for Rails applications}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AlertifyjsHelper
|
2
|
+
ALERT_TYPES = [:error, :info, :success, :warning] unless const_defined?(:ALERT_TYPES)
|
3
|
+
|
4
|
+
def alertify_flash
|
5
|
+
jsReturn = javascript_tag()
|
6
|
+
flash.each do |type, message|
|
7
|
+
# Skip empty messages, e.g. for devise messages set to nothing in a locale file.
|
8
|
+
next if message.blank?
|
9
|
+
|
10
|
+
type = :success if type == :notice
|
11
|
+
type = :error if type == :alert
|
12
|
+
next unless ALERT_TYPES.include?(type)
|
13
|
+
|
14
|
+
js_alertify = ""
|
15
|
+
Array(message).each do |msg|
|
16
|
+
js_alertify << "alertify.#{type}('#{j(msg)}');\n" if msg;
|
17
|
+
end
|
18
|
+
jsReturn = javascript_tag(js_alertify)
|
19
|
+
end
|
20
|
+
flash.clear
|
21
|
+
jsReturn.html_safe()
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Alertifyjs #:nodoc:
|
2
|
+
module Rails #:nodoc:
|
3
|
+
class Railtie < ::Rails::Railtie
|
4
|
+
config.before_configuration do
|
5
|
+
if config.action_view.javascript_expansions
|
6
|
+
config.action_view.javascript_expansions[:defaults] << 'alertify'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,2950 @@
|
|
1
|
+
/**
|
2
|
+
* AlertifyJS
|
3
|
+
* AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
|
4
|
+
*
|
5
|
+
* @author Mohammad Younes <Mohammad@alertifyjs.com> (http://alertifyjs.com)
|
6
|
+
* @copyright 2014
|
7
|
+
* @license MIT <http://opensource.org/licenses/mit-license.php>
|
8
|
+
* @link http://alertifyjs.com
|
9
|
+
* @module AlertifyJS
|
10
|
+
* @version 0.4.0
|
11
|
+
*/
|
12
|
+
( function ( window ) {
|
13
|
+
'use strict';
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Keys enum
|
17
|
+
* @type {Object}
|
18
|
+
*/
|
19
|
+
var keys = {
|
20
|
+
ENTER: 13,
|
21
|
+
ESC: 27,
|
22
|
+
F1: 112,
|
23
|
+
F12: 123
|
24
|
+
};
|
25
|
+
/**
|
26
|
+
* Default options
|
27
|
+
* @type {Object}
|
28
|
+
*/
|
29
|
+
var defaults = {
|
30
|
+
modal:true,
|
31
|
+
movable:true,
|
32
|
+
resizable:true,
|
33
|
+
closable:true,
|
34
|
+
maximizable:true,
|
35
|
+
pinnable:true,
|
36
|
+
pinned:true,
|
37
|
+
transition:'pulse',
|
38
|
+
padding: true,
|
39
|
+
overflow:true,
|
40
|
+
notifier:{
|
41
|
+
delay:5,
|
42
|
+
position:'bottom-right'
|
43
|
+
},
|
44
|
+
glossary:{
|
45
|
+
title:'AlertifyJS',
|
46
|
+
ok: 'OK',
|
47
|
+
cancel: 'Cancel',
|
48
|
+
acccpt: 'Accept',
|
49
|
+
deny: 'Deny',
|
50
|
+
confirm: 'Confirm',
|
51
|
+
decline: 'Decline',
|
52
|
+
close: 'Close',
|
53
|
+
maximize: 'Maximize',
|
54
|
+
restore: 'Restore',
|
55
|
+
},
|
56
|
+
theme:{
|
57
|
+
input:'ajs-input',
|
58
|
+
ok:'ajs-ok',
|
59
|
+
cancel:'ajs-cancel',
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
/**
|
64
|
+
* [Helper] Adds the specified class(es) to the element.
|
65
|
+
*
|
66
|
+
* @element {node} The element
|
67
|
+
* @className {string} One or more space-separated classes to be added to the class attribute of the element.
|
68
|
+
*
|
69
|
+
* @return {undefined}
|
70
|
+
*/
|
71
|
+
function addClass(element,classNames){
|
72
|
+
element.className += ' ' + classNames;
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* [Helper] Removes the specified class(es) from the element.
|
77
|
+
*
|
78
|
+
* @element {node} The element
|
79
|
+
* @className {string} One or more space-separated classes to be removed from the class attribute of the element.
|
80
|
+
*
|
81
|
+
* @return {undefined}
|
82
|
+
*/
|
83
|
+
function removeClass(element,classNames){
|
84
|
+
var classes = classNames.split(' ');
|
85
|
+
for(var x=0;x<classes.length;x+=1){
|
86
|
+
element.className = element.className.replace(' ' + classes[x], '');
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* [Helper] Checks if the document is RTL
|
92
|
+
*
|
93
|
+
* @return {Boolean} True if the document is RTL, false otherwise.
|
94
|
+
*/
|
95
|
+
function isRightToLeft(){
|
96
|
+
return window.getComputedStyle(document.body).direction === 'rtl';
|
97
|
+
}
|
98
|
+
/**
|
99
|
+
* [Helper] Get the document current scrollTop
|
100
|
+
*
|
101
|
+
* @return {Number} current document scrollTop value
|
102
|
+
*/
|
103
|
+
function getScrollTop(){
|
104
|
+
return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* [Helper] Get the document current scrollLeft
|
109
|
+
*
|
110
|
+
* @return {Number} current document scrollLeft value
|
111
|
+
*/
|
112
|
+
function getScrollLeft(){
|
113
|
+
return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Use a closure to return proper event listener method. Try to use
|
118
|
+
* `addEventListener` by default but fallback to `attachEvent` for
|
119
|
+
* unsupported browser. The closure simply ensures that the test doesn't
|
120
|
+
* happen every time the method is called.
|
121
|
+
*
|
122
|
+
* @param {Node} el Node element
|
123
|
+
* @param {String} event Event type
|
124
|
+
* @param {Function} fn Callback of event
|
125
|
+
* @return {Function}
|
126
|
+
*/
|
127
|
+
var on = (function () {
|
128
|
+
if (document.addEventListener) {
|
129
|
+
return function (el, event, fn, useCapture) {
|
130
|
+
el.addEventListener(event, fn, useCapture === true);
|
131
|
+
};
|
132
|
+
} else if (document.attachEvent) {
|
133
|
+
return function (el, event, fn) {
|
134
|
+
el.attachEvent('on' + event, fn);
|
135
|
+
};
|
136
|
+
}
|
137
|
+
}());
|
138
|
+
|
139
|
+
/**
|
140
|
+
* Use a closure to return proper event listener method. Try to use
|
141
|
+
* `removeEventListener` by default but fallback to `detachEvent` for
|
142
|
+
* unsupported browser. The closure simply ensures that the test doesn't
|
143
|
+
* happen every time the method is called.
|
144
|
+
*
|
145
|
+
* @param {Node} el Node element
|
146
|
+
* @param {String} event Event type
|
147
|
+
* @param {Function} fn Callback of event
|
148
|
+
* @return {Function}
|
149
|
+
*/
|
150
|
+
var off = (function () {
|
151
|
+
if (document.removeEventListener) {
|
152
|
+
return function (el, event, fn, useCapture) {
|
153
|
+
el.removeEventListener(event, fn, useCapture === true);
|
154
|
+
};
|
155
|
+
} else if (document.detachEvent) {
|
156
|
+
return function (el, event, fn) {
|
157
|
+
el.detachEvent('on' + event, fn);
|
158
|
+
};
|
159
|
+
}
|
160
|
+
}());
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Prevent default event from firing
|
164
|
+
*
|
165
|
+
* @param {Event} event Event object
|
166
|
+
* @return {undefined}
|
167
|
+
|
168
|
+
function prevent ( event ) {
|
169
|
+
if ( event ) {
|
170
|
+
if ( event.preventDefault ) {
|
171
|
+
event.preventDefault();
|
172
|
+
} else {
|
173
|
+
event.returnValue = false;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
*/
|
178
|
+
var transition = (function () {
|
179
|
+
var t, type;
|
180
|
+
var supported = false;
|
181
|
+
var el = document.createElement('fakeelement');
|
182
|
+
var transitions = {
|
183
|
+
'WebkitTransition': 'webkitTransitionEnd',
|
184
|
+
'MozTransition': 'transitionend',
|
185
|
+
'OTransition': 'otransitionend',
|
186
|
+
'transition': 'transitionend'
|
187
|
+
};
|
188
|
+
|
189
|
+
for (t in transitions) {
|
190
|
+
if (el.style[t] !== undefined) {
|
191
|
+
type = transitions[t];
|
192
|
+
supported = true;
|
193
|
+
break;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
return {
|
198
|
+
type: type,
|
199
|
+
supported: supported
|
200
|
+
};
|
201
|
+
}());
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Creates event handler delegate that sends the instance as last argument.
|
205
|
+
*
|
206
|
+
* @return {Function} a function wrapper which sends the instance as last argument.
|
207
|
+
*/
|
208
|
+
function delegate(context, method) {
|
209
|
+
return function () {
|
210
|
+
if (arguments.length > 0) {
|
211
|
+
var args = [];
|
212
|
+
for (var x = 0; x < arguments.length; x += 1) {
|
213
|
+
args.push(arguments[x]);
|
214
|
+
}
|
215
|
+
args.push(context);
|
216
|
+
return method.apply(context, args);
|
217
|
+
}
|
218
|
+
return method.apply(context, [null, context]);
|
219
|
+
};
|
220
|
+
}
|
221
|
+
/**
|
222
|
+
* Helper for creating a dialog close event.
|
223
|
+
*
|
224
|
+
* @return {object}
|
225
|
+
*/
|
226
|
+
function createCloseEvent(index, button) {
|
227
|
+
return {
|
228
|
+
index: index,
|
229
|
+
button: button,
|
230
|
+
cancel: false
|
231
|
+
};
|
232
|
+
}
|
233
|
+
|
234
|
+
|
235
|
+
/**
|
236
|
+
* Super class for all dialogs
|
237
|
+
*
|
238
|
+
* @return {Object} base dialog prototype
|
239
|
+
*/
|
240
|
+
var dialog = (function () {
|
241
|
+
//holds open dialogs instances
|
242
|
+
var openInstances = [],
|
243
|
+
//holds the list of used keys.
|
244
|
+
usedKeys = [],
|
245
|
+
//dummy variable, used to trigger dom reflow.
|
246
|
+
reflow = null,
|
247
|
+
//condition for detecting safari
|
248
|
+
isSafari = window.navigator.userAgent.indexOf('Safari') > -1 && window.navigator.userAgent.indexOf('Chrome') < 0,
|
249
|
+
//dialog building blocks
|
250
|
+
templates = {
|
251
|
+
dimmer:'<div class="ajs-dimmer"></div>',
|
252
|
+
/*tab index required to fire click event before body focus*/
|
253
|
+
modal: '<div class="ajs-modal" tabindex="0"></div>',
|
254
|
+
dialog: '<div class="ajs-dialog" tabindex="0"></div>',
|
255
|
+
reset: '<a class="ajs-reset" href="#"></a>',
|
256
|
+
commands: '<div class="ajs-commands"><button class="ajs-pin"></button><button class="ajs-maximize"></button><button class="ajs-close"></button></div>',
|
257
|
+
header: '<div class="ajs-header"></div>',
|
258
|
+
body: '<div class="ajs-body"></div>',
|
259
|
+
content: '<div class="ajs-content"></div>',
|
260
|
+
footer: '<div class="ajs-footer"></div>',
|
261
|
+
buttons: { primary: '<div class="ajs-primary ajs-buttons"></div>', auxiliary: '<div class="ajs-auxiliary ajs-buttons"></div>' },
|
262
|
+
button: '<button class="ajs-button"></button>',
|
263
|
+
resizeHandle: '<div class="ajs-handle"></div>',
|
264
|
+
},
|
265
|
+
//common class names
|
266
|
+
classes = {
|
267
|
+
base: 'alertify',
|
268
|
+
prefix: 'ajs-',
|
269
|
+
hidden: 'ajs-hidden',
|
270
|
+
noSelection: 'ajs-no-selection',
|
271
|
+
noOverflow: 'ajs-no-overflow',
|
272
|
+
noPadding:'ajs-no-padding',
|
273
|
+
modeless: 'ajs-modeless',
|
274
|
+
movable: 'ajs-movable',
|
275
|
+
resizable: 'ajs-resizable',
|
276
|
+
fixed: 'ajs-fixed',
|
277
|
+
closable:'ajs-closable',
|
278
|
+
maximizable:'ajs-maximizable',
|
279
|
+
maximize: 'ajs-maximize',
|
280
|
+
restore: 'ajs-restore',
|
281
|
+
pinnable:'ajs-pinnable',
|
282
|
+
unpinned:'ajs-unpinned',
|
283
|
+
pin:'ajs-pin',
|
284
|
+
maximized: 'ajs-maximized',
|
285
|
+
animationIn: 'ajs-in',
|
286
|
+
animationOut: 'ajs-out',
|
287
|
+
shake:'ajs-shake'
|
288
|
+
};
|
289
|
+
|
290
|
+
/**
|
291
|
+
* Helper: initializes the dialog instance
|
292
|
+
*
|
293
|
+
* @return {Number} The total count of currently open modals.
|
294
|
+
*/
|
295
|
+
function initialize(instance){
|
296
|
+
|
297
|
+
if(!instance.__internal){
|
298
|
+
|
299
|
+
//no need to expose init after this.
|
300
|
+
delete instance.__init;
|
301
|
+
|
302
|
+
//in case the script was included before body.
|
303
|
+
//after first dialog gets initialized, it won't be null anymore!
|
304
|
+
if(null === reflow){
|
305
|
+
// set tabindex attribute on body element this allows script to give it
|
306
|
+
// focus after the dialog is closed
|
307
|
+
document.body.setAttribute( 'tabindex', '0' );
|
308
|
+
}
|
309
|
+
|
310
|
+
//get dialog buttons/focus setup
|
311
|
+
var setup;
|
312
|
+
if(typeof instance.setup === 'function'){
|
313
|
+
setup = instance.setup();
|
314
|
+
setup.options = setup.options || {};
|
315
|
+
setup.focus = setup.focus || {};
|
316
|
+
}else{
|
317
|
+
setup = {
|
318
|
+
buttons:[],
|
319
|
+
focus:{
|
320
|
+
element:null,
|
321
|
+
select:false
|
322
|
+
},
|
323
|
+
options:{
|
324
|
+
}
|
325
|
+
};
|
326
|
+
}
|
327
|
+
|
328
|
+
var internal = instance.__internal = {
|
329
|
+
/**
|
330
|
+
* Flag holding the open state of the dialog
|
331
|
+
*
|
332
|
+
* @type {Boolean}
|
333
|
+
*/
|
334
|
+
isOpen:false,
|
335
|
+
/**
|
336
|
+
* Active element is the element that will receive focus after
|
337
|
+
* closing the dialog. It defaults as the body tag, but gets updated
|
338
|
+
* to the last focused element before the dialog was opened.
|
339
|
+
*
|
340
|
+
* @type {Node}
|
341
|
+
*/
|
342
|
+
activeElement:document.body,
|
343
|
+
buttons: setup.buttons,
|
344
|
+
focus: setup.focus,
|
345
|
+
options: {
|
346
|
+
title: undefined,
|
347
|
+
modal: undefined,
|
348
|
+
pinned: undefined,
|
349
|
+
movable: undefined,
|
350
|
+
resizable: undefined,
|
351
|
+
closable: undefined,
|
352
|
+
maximizable: undefined,
|
353
|
+
pinnable: undefined,
|
354
|
+
transition: undefined,
|
355
|
+
padding:undefined,
|
356
|
+
overflow:undefined,
|
357
|
+
onshow:undefined,
|
358
|
+
onclose:undefined,
|
359
|
+
onfocus:undefined,
|
360
|
+
},
|
361
|
+
resetHandler:undefined,
|
362
|
+
beginMoveHandler:undefined,
|
363
|
+
beginResizeHandler:undefined,
|
364
|
+
bringToFrontHandler:undefined,
|
365
|
+
modalClickHandler:undefined,
|
366
|
+
buttonsClickHandler:undefined,
|
367
|
+
commandsClickHandler:undefined,
|
368
|
+
transitionInHandler:undefined,
|
369
|
+
transitionOutHandler:undefined
|
370
|
+
};
|
371
|
+
|
372
|
+
|
373
|
+
var elements = {};
|
374
|
+
//root node
|
375
|
+
elements.root = document.createElement('div');
|
376
|
+
|
377
|
+
elements.root.className = classes.base + ' ' + classes.hidden + ' ';
|
378
|
+
|
379
|
+
elements.root.innerHTML = templates.dimmer + templates.modal;
|
380
|
+
|
381
|
+
//dimmer
|
382
|
+
elements.dimmer = elements.root.firstChild;
|
383
|
+
|
384
|
+
//dialog
|
385
|
+
elements.modal = elements.root.lastChild;
|
386
|
+
elements.modal.innerHTML = templates.dialog;
|
387
|
+
elements.dialog = elements.modal.firstChild;
|
388
|
+
elements.dialog.innerHTML = templates.reset + templates.commands + templates.header + templates.body + templates.footer + templates.reset;
|
389
|
+
|
390
|
+
//reset links
|
391
|
+
elements.reset = [];
|
392
|
+
elements.reset.push(elements.dialog.firstChild);
|
393
|
+
elements.reset.push(elements.dialog.lastChild);
|
394
|
+
|
395
|
+
//commands
|
396
|
+
elements.commands = {};
|
397
|
+
elements.commands.container = elements.reset[0].nextSibling;
|
398
|
+
elements.commands.pin = elements.commands.container.firstChild;
|
399
|
+
elements.commands.maximize = elements.commands.pin.nextSibling;
|
400
|
+
elements.commands.close = elements.commands.maximize.nextSibling;
|
401
|
+
|
402
|
+
//header
|
403
|
+
elements.header = elements.commands.container.nextSibling;
|
404
|
+
|
405
|
+
//body
|
406
|
+
elements.body = elements.header.nextSibling;
|
407
|
+
elements.body.innerHTML = templates.content;
|
408
|
+
elements.content = elements.body.firstChild;
|
409
|
+
|
410
|
+
//footer
|
411
|
+
elements.footer = elements.body.nextSibling;
|
412
|
+
elements.footer.innerHTML = templates.buttons.auxiliary + templates.buttons.primary + templates.resizeHandle;
|
413
|
+
elements.resizeHandle = elements.footer.lastChild;
|
414
|
+
|
415
|
+
//buttons
|
416
|
+
elements.buttons = {};
|
417
|
+
elements.buttons.auxiliary = elements.footer.firstChild;
|
418
|
+
elements.buttons.primary = elements.buttons.auxiliary.nextSibling;
|
419
|
+
elements.buttons.primary.innerHTML = templates.button;
|
420
|
+
elements.buttonTemplate = elements.buttons.primary.firstChild;
|
421
|
+
//remove button template
|
422
|
+
elements.buttons.primary.removeChild(elements.buttonTemplate);
|
423
|
+
|
424
|
+
for(var x=0; x < instance.__internal.buttons.length; x+=1) {
|
425
|
+
var button = instance.__internal.buttons[x];
|
426
|
+
|
427
|
+
// add to the list of used keys.
|
428
|
+
if(usedKeys.indexOf(button.key) < 0){
|
429
|
+
usedKeys.push(button.key);
|
430
|
+
}
|
431
|
+
|
432
|
+
button.element = elements.buttonTemplate.cloneNode();
|
433
|
+
button.element.innerHTML = button.text;
|
434
|
+
if(typeof button.className === 'string' && button.className !== ''){
|
435
|
+
addClass(button.element, button.className);
|
436
|
+
}
|
437
|
+
for(var key in button.attrs){
|
438
|
+
if(key !== 'className' && button.attrs.hasOwnProperty(key)){
|
439
|
+
button.element.setAttribute(key, button.attrs[key]);
|
440
|
+
}
|
441
|
+
}
|
442
|
+
if(button.scope === 'auxiliary'){
|
443
|
+
elements.buttons.auxiliary.appendChild(button.element);
|
444
|
+
}else{
|
445
|
+
elements.buttons.primary.appendChild(button.element);
|
446
|
+
}
|
447
|
+
}
|
448
|
+
//make elements pubic
|
449
|
+
instance.elements = elements;
|
450
|
+
|
451
|
+
//save event handlers delegates
|
452
|
+
internal.resetHandler = delegate(instance, onReset);
|
453
|
+
internal.beginMoveHandler = delegate(instance, beginMove);
|
454
|
+
internal.beginResizeHandler = delegate(instance, beginResize);
|
455
|
+
internal.bringToFrontHandler = delegate(instance, bringToFront);
|
456
|
+
internal.modalClickHandler = delegate(instance, modalClickHandler);
|
457
|
+
internal.buttonsClickHandler = delegate(instance, buttonsClickHandler);
|
458
|
+
internal.commandsClickHandler = delegate(instance, commandsClickHandler);
|
459
|
+
internal.transitionInHandler = delegate(instance, handleTransitionInEvent);
|
460
|
+
internal.transitionOutHandler = delegate(instance, handleTransitionOutEvent);
|
461
|
+
|
462
|
+
|
463
|
+
//settings
|
464
|
+
instance.setting('title', setup.options.title === undefined ? alertify.defaults.glossary.title : setup.options.title);
|
465
|
+
|
466
|
+
instance.setting('modal', setup.options.modal === undefined ? alertify.defaults.modal : setup.options.modal);
|
467
|
+
|
468
|
+
instance.setting('movable', setup.options.movable === undefined ? alertify.defaults.movable : setup.options.movable);
|
469
|
+
instance.setting('resizable', setup.options.resizable === undefined ? alertify.defaults.resizable : setup.options.resizable);
|
470
|
+
|
471
|
+
instance.setting('closable', setup.options.closable === undefined ? alertify.defaults.closable : setup.options.closable);
|
472
|
+
instance.setting('maximizable', setup.options.maximizable === undefined ? alertify.defaults.maximizable : setup.options.maximizable);
|
473
|
+
|
474
|
+
instance.setting('pinnable', setup.options.pinnable === undefined ? alertify.defaults.pinnable : setup.options.pinnable);
|
475
|
+
instance.setting('pinned', setup.options.pinned === undefined ? alertify.defaults.pinned : setup.options.pinned);
|
476
|
+
|
477
|
+
instance.setting('transition', setup.options.transition === undefined ? alertify.defaults.transition : setup.options.transition);
|
478
|
+
|
479
|
+
instance.setting('padding', setup.options.padding === undefined ? alertify.defaults.padding : setup.options.padding);
|
480
|
+
instance.setting('overflow', setup.options.overflow === undefined ? alertify.defaults.overflow : setup.options.overflow);
|
481
|
+
|
482
|
+
|
483
|
+
// allow dom customization
|
484
|
+
if(typeof instance.build === 'function'){
|
485
|
+
instance.build();
|
486
|
+
}
|
487
|
+
|
488
|
+
}
|
489
|
+
|
490
|
+
//add to DOM tree.
|
491
|
+
document.body.appendChild(instance.elements.root);
|
492
|
+
}
|
493
|
+
|
494
|
+
/**
|
495
|
+
* Helper: adds/removes no-overflow class from body
|
496
|
+
*
|
497
|
+
*/
|
498
|
+
function ensureNoOverflow(){
|
499
|
+
var requiresNoOverflow = 0;
|
500
|
+
for(var x=0;x<openInstances.length;x+=1){
|
501
|
+
var instance = openInstances[x];
|
502
|
+
if(instance.isModal() || instance.isMaximized()){
|
503
|
+
requiresNoOverflow+=1;
|
504
|
+
}
|
505
|
+
}
|
506
|
+
if(requiresNoOverflow === 0){
|
507
|
+
//last open modal or last maximized one
|
508
|
+
removeClass(document.body, classes.noOverflow);
|
509
|
+
}else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
|
510
|
+
//first open modal or first maximized one
|
511
|
+
addClass(document.body, classes.noOverflow);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
515
|
+
/**
|
516
|
+
* Sets the name of the transition used to show/hide the dialog
|
517
|
+
*
|
518
|
+
* @param {Object} instance The dilog instance.
|
519
|
+
*
|
520
|
+
*/
|
521
|
+
function updateTransition(instance, value, oldValue){
|
522
|
+
if(typeof oldValue === 'string'){
|
523
|
+
removeClass(instance.elements.root,classes.prefix + oldValue);
|
524
|
+
}
|
525
|
+
addClass(instance.elements.root, classes.prefix + value);
|
526
|
+
reflow = instance.elements.root.offsetWidth;
|
527
|
+
}
|
528
|
+
|
529
|
+
/**
|
530
|
+
* Toggles the dialog display mode
|
531
|
+
*
|
532
|
+
* @param {Object} instance The dilog instance.
|
533
|
+
* @param {Boolean} on True to make it modal, false otherwise.
|
534
|
+
*
|
535
|
+
* @return {undefined}
|
536
|
+
*/
|
537
|
+
function updateDisplayMode(instance){
|
538
|
+
if(instance.setting('modal')){
|
539
|
+
|
540
|
+
//make modal
|
541
|
+
removeClass(instance.elements.root, classes.modeless);
|
542
|
+
|
543
|
+
//only if open
|
544
|
+
if(instance.isOpen()){
|
545
|
+
unbindModelessEvents(instance);
|
546
|
+
|
547
|
+
//in case a pinned modless dialog was made modal while open.
|
548
|
+
updateAbsPositionFix(instance);
|
549
|
+
|
550
|
+
ensureNoOverflow();
|
551
|
+
}
|
552
|
+
}else{
|
553
|
+
//make modelss
|
554
|
+
addClass(instance.elements.root, classes.modeless);
|
555
|
+
|
556
|
+
//only if open
|
557
|
+
if(instance.isOpen()){
|
558
|
+
bindModelessEvents(instance);
|
559
|
+
|
560
|
+
//in case pin/unpin was called while a modal is open
|
561
|
+
updateAbsPositionFix(instance);
|
562
|
+
|
563
|
+
ensureNoOverflow();
|
564
|
+
}
|
565
|
+
}
|
566
|
+
}
|
567
|
+
|
568
|
+
/**
|
569
|
+
* Helper: Brings the modeless dialog to front, attached to modeless dialogs.
|
570
|
+
*
|
571
|
+
* @param {Event} event Focus event
|
572
|
+
* @param {Object} instance The dilog instance.
|
573
|
+
*
|
574
|
+
* @return {undefined}
|
575
|
+
*/
|
576
|
+
function bringToFront(event, instance){
|
577
|
+
|
578
|
+
// Do not bring to front if preceeded by an open modal
|
579
|
+
var index = openInstances.indexOf(instance);
|
580
|
+
for(var x=index+1;x<openInstances.length;x+=1){
|
581
|
+
if(openInstances[x].isModal()){
|
582
|
+
return;
|
583
|
+
}
|
584
|
+
}
|
585
|
+
|
586
|
+
// Bring to front by making it the last child.
|
587
|
+
if(document.body.lastChild !== instance.elements.root){
|
588
|
+
document.body.appendChild(instance.elements.root);
|
589
|
+
setFocus(instance);
|
590
|
+
}
|
591
|
+
|
592
|
+
return false;
|
593
|
+
}
|
594
|
+
|
595
|
+
/**
|
596
|
+
* Helper: reflects dialogs options updates
|
597
|
+
*
|
598
|
+
* @param {Object} instance The dilog instance.
|
599
|
+
* @param {String} option The updated option name.
|
600
|
+
*
|
601
|
+
* @return {undefined}
|
602
|
+
*/
|
603
|
+
function optionUpdated(instance, option, oldValue, newValue){
|
604
|
+
switch(option){
|
605
|
+
case 'title':
|
606
|
+
instance.setHeader(newValue);
|
607
|
+
break;
|
608
|
+
case 'modal':
|
609
|
+
updateDisplayMode(instance);
|
610
|
+
break;
|
611
|
+
case 'pinned':
|
612
|
+
updatePinned(instance);
|
613
|
+
break;
|
614
|
+
case 'closable':
|
615
|
+
updateClosable(instance);
|
616
|
+
break;
|
617
|
+
case 'maximizable':
|
618
|
+
updateMaximizable(instance);
|
619
|
+
break;
|
620
|
+
case 'pinnable':
|
621
|
+
updatePinnable(instance);
|
622
|
+
break;
|
623
|
+
case 'movable':
|
624
|
+
updateMovable(instance);
|
625
|
+
break;
|
626
|
+
case 'resizable':
|
627
|
+
updateResizable(instance);
|
628
|
+
break;
|
629
|
+
case 'transition':
|
630
|
+
updateTransition(instance,newValue, oldValue);
|
631
|
+
break;
|
632
|
+
case 'padding':
|
633
|
+
if(newValue){
|
634
|
+
removeClass(instance.elements.root, classes.noPadding);
|
635
|
+
}else if(instance.elements.root.className.indexOf(classes.noPadding) < 0){
|
636
|
+
addClass(instance.elements.root, classes.noPadding);
|
637
|
+
}
|
638
|
+
break;
|
639
|
+
case 'overflow':
|
640
|
+
if(newValue){
|
641
|
+
removeClass(instance.elements.root, classes.noOverflow);
|
642
|
+
}else if(instance.elements.root.className.indexOf(classes.noOverflow) < 0){
|
643
|
+
addClass(instance.elements.root, classes.noOverflow);
|
644
|
+
}
|
645
|
+
break;
|
646
|
+
case 'transition':
|
647
|
+
updateTransition(instance,newValue, oldValue);
|
648
|
+
break;
|
649
|
+
}
|
650
|
+
}
|
651
|
+
|
652
|
+
/**
|
653
|
+
* Helper: reflects dialogs options updates
|
654
|
+
*
|
655
|
+
* @param {Object} instance The dilog instance.
|
656
|
+
* @param {Object} obj The object to set/get a value on/from.
|
657
|
+
* @param {Function} callback The callback function to call if the key was found.
|
658
|
+
* @param {String|Object} key A string specifying a propery name or a collection of key value pairs.
|
659
|
+
* @param {Object} value Optional, the value associated with the key (in case it was a string).
|
660
|
+
* @param {String} option The updated option name.
|
661
|
+
*
|
662
|
+
* @return {Object} result object
|
663
|
+
* The result objects has an 'op' property, indicating of this is a SET or GET operation.
|
664
|
+
* GET:
|
665
|
+
* - found: a flag indicating if the key was found or not.
|
666
|
+
* - value: the property value.
|
667
|
+
* SET:
|
668
|
+
* - items: a list of key value pairs of the properties being set.
|
669
|
+
* each contains:
|
670
|
+
* - found: a flag indicating if the key was found or not.
|
671
|
+
* - key: the property key.
|
672
|
+
* - value: the property value.
|
673
|
+
*/
|
674
|
+
function update(instance, obj, callback, key, value){
|
675
|
+
var result = {op:undefined, items: [] };
|
676
|
+
if(typeof value === 'undefined' && typeof key === 'string') {
|
677
|
+
//get
|
678
|
+
result.op = 'get';
|
679
|
+
if(obj.hasOwnProperty(key)){
|
680
|
+
result.found = true;
|
681
|
+
result.value = obj[key];
|
682
|
+
}else{
|
683
|
+
result.found = false;
|
684
|
+
result.value = undefined;
|
685
|
+
}
|
686
|
+
}
|
687
|
+
else
|
688
|
+
{
|
689
|
+
var old;
|
690
|
+
//set
|
691
|
+
result.op = 'set';
|
692
|
+
if(typeof key === 'object'){
|
693
|
+
//set multiple
|
694
|
+
var args = key;
|
695
|
+
for (var prop in args) {
|
696
|
+
if (obj.hasOwnProperty(prop)) {
|
697
|
+
if(obj[prop] !== args[prop]){
|
698
|
+
old = obj[prop];
|
699
|
+
obj[prop] = args[prop];
|
700
|
+
callback.call(instance,prop, old, args[prop]);
|
701
|
+
}
|
702
|
+
result.items.push({ 'key': prop, 'value': args[prop], 'found':true});
|
703
|
+
}else{
|
704
|
+
result.items.push({ 'key': prop, 'value': args[prop], 'found':false});
|
705
|
+
}
|
706
|
+
}
|
707
|
+
} else if (typeof key === 'string'){
|
708
|
+
//set single
|
709
|
+
if (obj.hasOwnProperty(key)) {
|
710
|
+
if(obj[key] !== value){
|
711
|
+
old = obj[key];
|
712
|
+
obj[key] = value;
|
713
|
+
callback.call(instance,key, old, value);
|
714
|
+
}
|
715
|
+
result.items.push({'key': key, 'value': value , 'found':true});
|
716
|
+
|
717
|
+
}else{
|
718
|
+
result.items.push({'key': key, 'value': value , 'found':false});
|
719
|
+
}
|
720
|
+
} else {
|
721
|
+
//invalid params
|
722
|
+
throw new Error('args must be a string or object');
|
723
|
+
}
|
724
|
+
}
|
725
|
+
return result;
|
726
|
+
}
|
727
|
+
|
728
|
+
|
729
|
+
/**
|
730
|
+
* Triggers a close event.
|
731
|
+
*
|
732
|
+
* @param {Object} instance The dilog instance.
|
733
|
+
*
|
734
|
+
* @return {undefined}
|
735
|
+
*/
|
736
|
+
function triggerClose(instance) {
|
737
|
+
var found;
|
738
|
+
triggerCallback(instance, function (button) {
|
739
|
+
return found = (button.invokeOnClose === true);
|
740
|
+
});
|
741
|
+
//none of the buttons registered as onclose callback
|
742
|
+
//close the dialog
|
743
|
+
if (!found && instance.isOpen()) {
|
744
|
+
instance.close();
|
745
|
+
}
|
746
|
+
}
|
747
|
+
|
748
|
+
/**
|
749
|
+
* Dialogs commands event handler, attached to the dialog commands element.
|
750
|
+
*
|
751
|
+
* @param {Event} event DOM event object.
|
752
|
+
* @param {Object} instance The dilog instance.
|
753
|
+
*
|
754
|
+
* @return {undefined}
|
755
|
+
*/
|
756
|
+
function commandsClickHandler(event, instance) {
|
757
|
+
var target = event.srcElement || event.target;
|
758
|
+
switch (target) {
|
759
|
+
case instance.elements.commands.pin:
|
760
|
+
if (!instance.isPinned()) {
|
761
|
+
pin(instance);
|
762
|
+
} else {
|
763
|
+
unpin(instance);
|
764
|
+
}
|
765
|
+
break;
|
766
|
+
case instance.elements.commands.maximize:
|
767
|
+
if (!instance.isMaximized()) {
|
768
|
+
maximize(instance);
|
769
|
+
} else {
|
770
|
+
restore(instance);
|
771
|
+
}
|
772
|
+
break;
|
773
|
+
case instance.elements.commands.close:
|
774
|
+
triggerClose(instance);
|
775
|
+
break;
|
776
|
+
}
|
777
|
+
return false;
|
778
|
+
}
|
779
|
+
|
780
|
+
/**
|
781
|
+
* Helper: pins the modeless dialog.
|
782
|
+
*
|
783
|
+
* @param {Object} instance The dialog instance.
|
784
|
+
*
|
785
|
+
* @return {undefined}
|
786
|
+
*/
|
787
|
+
function pin(instance) {
|
788
|
+
//pin the dialog
|
789
|
+
instance.setting('pinned', true);
|
790
|
+
}
|
791
|
+
|
792
|
+
/**
|
793
|
+
* Helper: unpins the modeless dialog.
|
794
|
+
*
|
795
|
+
* @param {Object} instance The dilog instance.
|
796
|
+
*
|
797
|
+
* @return {undefined}
|
798
|
+
*/
|
799
|
+
function unpin(instance) {
|
800
|
+
//unpin the dialog
|
801
|
+
instance.setting('pinned', false);
|
802
|
+
}
|
803
|
+
|
804
|
+
|
805
|
+
/**
|
806
|
+
* Helper: enlarges the dialog to fill the entire screen.
|
807
|
+
*
|
808
|
+
* @param {Object} instance The dilog instance.
|
809
|
+
*
|
810
|
+
* @return {undefined}
|
811
|
+
*/
|
812
|
+
function maximize(instance) {
|
813
|
+
//maximize the dialog
|
814
|
+
addClass(instance.elements.root, classes.maximized);
|
815
|
+
if (instance.isOpen()) {
|
816
|
+
ensureNoOverflow();
|
817
|
+
}
|
818
|
+
}
|
819
|
+
|
820
|
+
/**
|
821
|
+
* Helper: returns the dialog to its former size.
|
822
|
+
*
|
823
|
+
* @param {Object} instance The dilog instance.
|
824
|
+
*
|
825
|
+
* @return {undefined}
|
826
|
+
*/
|
827
|
+
function restore(instance) {
|
828
|
+
//maximize the dialog
|
829
|
+
removeClass(instance.elements.root, classes.maximized);
|
830
|
+
if (instance.isOpen()) {
|
831
|
+
ensureNoOverflow();
|
832
|
+
}
|
833
|
+
}
|
834
|
+
|
835
|
+
/**
|
836
|
+
* Show or hide the maximize box.
|
837
|
+
*
|
838
|
+
* @param {Object} instance The dilog instance.
|
839
|
+
* @param {Boolean} on True to add the behavior, removes it otherwise.
|
840
|
+
*
|
841
|
+
* @return {undefined}
|
842
|
+
*/
|
843
|
+
function updatePinnable(instance) {
|
844
|
+
if (instance.setting('pinnable')) {
|
845
|
+
// add class
|
846
|
+
addClass(instance.elements.root, classes.pinnable);
|
847
|
+
} else {
|
848
|
+
// remove class
|
849
|
+
removeClass(instance.elements.root, classes.pinnable);
|
850
|
+
}
|
851
|
+
}
|
852
|
+
|
853
|
+
/**
|
854
|
+
* Helper: Fixes the absolutly positioned modal div position.
|
855
|
+
*
|
856
|
+
* @param {Object} instance The dialog instance.
|
857
|
+
*
|
858
|
+
* @return {undefined}
|
859
|
+
*/
|
860
|
+
function addAbsPositionFix(instance) {
|
861
|
+
var scrollLeft = getScrollLeft();
|
862
|
+
instance.elements.modal.style.marginTop = getScrollTop() + 'px';
|
863
|
+
instance.elements.modal.style.marginLeft = scrollLeft + 'px';
|
864
|
+
instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
|
865
|
+
}
|
866
|
+
|
867
|
+
/**
|
868
|
+
* Helper: Removes the absolutly positioned modal div position fix.
|
869
|
+
*
|
870
|
+
* @param {Object} instance The dialog instance.
|
871
|
+
*
|
872
|
+
* @return {undefined}
|
873
|
+
*/
|
874
|
+
function removeAbsPositionFix(instance) {
|
875
|
+
var marginTop = parseInt(instance.elements.modal.style.marginTop, 10);
|
876
|
+
var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10);
|
877
|
+
instance.elements.modal.style.marginTop = '';
|
878
|
+
instance.elements.modal.style.marginLeft = '';
|
879
|
+
instance.elements.modal.style.marginRight = '';
|
880
|
+
|
881
|
+
if (instance.isOpen()) {
|
882
|
+
var top = 0,
|
883
|
+
left = 0
|
884
|
+
;
|
885
|
+
if (instance.elements.dialog.style.top !== '') {
|
886
|
+
top = parseInt(instance.elements.dialog.style.top, 10);
|
887
|
+
}
|
888
|
+
instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';
|
889
|
+
|
890
|
+
if (instance.elements.dialog.style.left !== '') {
|
891
|
+
left = parseInt(instance.elements.dialog.style.left, 10);
|
892
|
+
}
|
893
|
+
instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
|
894
|
+
}
|
895
|
+
}
|
896
|
+
/**
|
897
|
+
* Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
|
898
|
+
*
|
899
|
+
* @param {Object} instance The dialog instance.
|
900
|
+
*
|
901
|
+
* @return {undefined}
|
902
|
+
*/
|
903
|
+
function updateAbsPositionFix(instance) {
|
904
|
+
// if modeless and unpinned add fix
|
905
|
+
if (!instance.setting('modal') && !instance.setting('pinned')) {
|
906
|
+
addAbsPositionFix(instance);
|
907
|
+
} else {
|
908
|
+
removeAbsPositionFix(instance);
|
909
|
+
}
|
910
|
+
}
|
911
|
+
/**
|
912
|
+
* Toggles the dialog position lock | modeless only.
|
913
|
+
*
|
914
|
+
* @param {Object} instance The dilog instance.
|
915
|
+
* @param {Boolean} on True to make it modal, false otherwise.
|
916
|
+
*
|
917
|
+
* @return {undefined}
|
918
|
+
*/
|
919
|
+
function updatePinned(instance) {
|
920
|
+
if (instance.setting('pinned')) {
|
921
|
+
removeClass(instance.elements.root, classes.unpinned);
|
922
|
+
if (instance.isOpen()) {
|
923
|
+
removeAbsPositionFix(instance);
|
924
|
+
}
|
925
|
+
} else {
|
926
|
+
addClass(instance.elements.root, classes.unpinned);
|
927
|
+
if (instance.isOpen() && !instance.isModal()) {
|
928
|
+
addAbsPositionFix(instance);
|
929
|
+
}
|
930
|
+
}
|
931
|
+
}
|
932
|
+
|
933
|
+
/**
|
934
|
+
* Show or hide the maximize box.
|
935
|
+
*
|
936
|
+
* @param {Object} instance The dilog instance.
|
937
|
+
* @param {Boolean} on True to add the behavior, removes it otherwise.
|
938
|
+
*
|
939
|
+
* @return {undefined}
|
940
|
+
*/
|
941
|
+
function updateMaximizable(instance) {
|
942
|
+
if (instance.setting('maximizable')) {
|
943
|
+
// add class
|
944
|
+
addClass(instance.elements.root, classes.maximizable);
|
945
|
+
} else {
|
946
|
+
// remove class
|
947
|
+
removeClass(instance.elements.root, classes.maximizable);
|
948
|
+
}
|
949
|
+
}
|
950
|
+
|
951
|
+
/**
|
952
|
+
* Show or hide the close box.
|
953
|
+
*
|
954
|
+
* @param {Object} instance The dilog instance.
|
955
|
+
* @param {Boolean} on True to add the behavior, removes it otherwise.
|
956
|
+
*
|
957
|
+
* @return {undefined}
|
958
|
+
*/
|
959
|
+
function updateClosable(instance) {
|
960
|
+
if (instance.setting('closable')) {
|
961
|
+
// add class
|
962
|
+
addClass(instance.elements.root, classes.closable);
|
963
|
+
bindClosableEvents(instance);
|
964
|
+
} else {
|
965
|
+
// remove class
|
966
|
+
removeClass(instance.elements.root, classes.closable);
|
967
|
+
unbindClosableEvents(instance);
|
968
|
+
}
|
969
|
+
}
|
970
|
+
|
971
|
+
// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
|
972
|
+
var cancelClick = false;
|
973
|
+
|
974
|
+
/**
|
975
|
+
* Helper: closes the modal dialog when clicking the modal
|
976
|
+
*
|
977
|
+
* @param {Event} event DOM event object.
|
978
|
+
* @param {Object} instance The dilog instance.
|
979
|
+
*
|
980
|
+
* @return {undefined}
|
981
|
+
*/
|
982
|
+
function modalClickHandler(event, instance) {
|
983
|
+
var target = event.srcElement || event.target;
|
984
|
+
if (!cancelClick && target === instance.elements.modal) {
|
985
|
+
triggerClose(instance);
|
986
|
+
}
|
987
|
+
cancelClick = false;
|
988
|
+
return false;
|
989
|
+
}
|
990
|
+
|
991
|
+
// flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
|
992
|
+
var cancelKeyup = false;
|
993
|
+
/**
|
994
|
+
* Helper: triggers a button callback
|
995
|
+
*
|
996
|
+
* @param {Object} The dilog instance.
|
997
|
+
* @param {Function} Callback to check which button triggered the event.
|
998
|
+
*
|
999
|
+
* @return {undefined}
|
1000
|
+
*/
|
1001
|
+
function triggerCallback(instance, check) {
|
1002
|
+
for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) {
|
1003
|
+
var button = instance.__internal.buttons[idx];
|
1004
|
+
if (!button.element.disabled && check(button)) {
|
1005
|
+
var closeEvent = createCloseEvent(idx, button);
|
1006
|
+
if (typeof instance.callback === 'function') {
|
1007
|
+
instance.callback.apply(instance, [closeEvent]);
|
1008
|
+
}
|
1009
|
+
//close the dialog only if not canceled.
|
1010
|
+
if (closeEvent.cancel === false) {
|
1011
|
+
instance.close();
|
1012
|
+
}
|
1013
|
+
break;
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
/**
|
1019
|
+
* Clicks event handler, attached to the dialog footer.
|
1020
|
+
*
|
1021
|
+
* @param {Event} DOM event object.
|
1022
|
+
* @param {Object} The dilog instance.
|
1023
|
+
*
|
1024
|
+
* @return {undefined}
|
1025
|
+
*/
|
1026
|
+
function buttonsClickHandler(event, instance) {
|
1027
|
+
var target = event.srcElement || event.target;
|
1028
|
+
triggerCallback(instance, function (button) {
|
1029
|
+
// if this button caused the click, cancel keyup event
|
1030
|
+
return button.element === target && (cancelKeyup = true);
|
1031
|
+
});
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
/**
|
1035
|
+
* Keyup event handler, attached to the document.body
|
1036
|
+
*
|
1037
|
+
* @param {Event} DOM event object.
|
1038
|
+
* @param {Object} The dilog instance.
|
1039
|
+
*
|
1040
|
+
* @return {undefined}
|
1041
|
+
*/
|
1042
|
+
function keyupHandler(event) {
|
1043
|
+
//hitting enter while button has focus will trigger keyup too.
|
1044
|
+
//ignore if handled by clickHandler
|
1045
|
+
if (cancelKeyup) {
|
1046
|
+
cancelKeyup = false;
|
1047
|
+
return;
|
1048
|
+
}
|
1049
|
+
var instance = openInstances[openInstances.length - 1];
|
1050
|
+
var keyCode = event.keyCode;
|
1051
|
+
if (usedKeys.indexOf(keyCode) > -1) {
|
1052
|
+
triggerCallback(instance, function (button) {
|
1053
|
+
return button.key === keyCode;
|
1054
|
+
});
|
1055
|
+
return false;
|
1056
|
+
}
|
1057
|
+
}
|
1058
|
+
/**
|
1059
|
+
* Keydown event handler, attached to the document.body
|
1060
|
+
*
|
1061
|
+
* @param {Event} DOM event object.
|
1062
|
+
* @param {Object} The dilog instance.
|
1063
|
+
*
|
1064
|
+
* @return {undefined}
|
1065
|
+
*/
|
1066
|
+
function keydownHandler(event) {
|
1067
|
+
var instance = openInstances[openInstances.length - 1];
|
1068
|
+
var keyCode = event.keyCode;
|
1069
|
+
if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) {
|
1070
|
+
event.preventDefault();
|
1071
|
+
event.stopPropagation();
|
1072
|
+
triggerCallback(instance, function (button) {
|
1073
|
+
return button.key === keyCode;
|
1074
|
+
});
|
1075
|
+
return false;
|
1076
|
+
}
|
1077
|
+
}
|
1078
|
+
|
1079
|
+
|
1080
|
+
/**
|
1081
|
+
* Sets focus to proper dialog element
|
1082
|
+
*
|
1083
|
+
* @param {Object} instance The dilog instance.
|
1084
|
+
* @param {Node} [resetTarget=undefined] DOM element to reset focus to.
|
1085
|
+
*
|
1086
|
+
* @return {undefined}
|
1087
|
+
*/
|
1088
|
+
function setFocus(instance, resetTarget) {
|
1089
|
+
// reset target has already been determined.
|
1090
|
+
if (resetTarget) {
|
1091
|
+
resetTarget.focus();
|
1092
|
+
} else {
|
1093
|
+
// current instance focus settings
|
1094
|
+
var focus = instance.__internal.focus;
|
1095
|
+
// the focus element.
|
1096
|
+
var element = focus.element;
|
1097
|
+
// a number means a button index
|
1098
|
+
if (typeof focus.element === 'number') {
|
1099
|
+
element = instance.__internal.buttons[focus.element].element;
|
1100
|
+
}
|
1101
|
+
// focus
|
1102
|
+
if (element && element.focus) {
|
1103
|
+
element.focus();
|
1104
|
+
// if selectable
|
1105
|
+
if (focus.select && element.select) {
|
1106
|
+
element.select();
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
}
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
/**
|
1113
|
+
* Focus event handler, attached to document.body and dialogs own reset links.
|
1114
|
+
* handles the focus for modal dialogs only.
|
1115
|
+
*
|
1116
|
+
* @param {Event} event DOM focus event object.
|
1117
|
+
* @param {Object} instance The dilog instance.
|
1118
|
+
*
|
1119
|
+
* @return {undefined}
|
1120
|
+
*/
|
1121
|
+
function onReset(event, instance) {
|
1122
|
+
|
1123
|
+
// should work on last modal if triggered from document.body
|
1124
|
+
if (!instance) {
|
1125
|
+
for (var x = openInstances.length - 1; x > -1; x -= 1) {
|
1126
|
+
if (openInstances[x].isModal()) {
|
1127
|
+
instance = openInstances[x];
|
1128
|
+
break;
|
1129
|
+
}
|
1130
|
+
}
|
1131
|
+
}
|
1132
|
+
// if modal
|
1133
|
+
if (instance && instance.isModal()) {
|
1134
|
+
// determine reset target to enable forward/backward tab cycle.
|
1135
|
+
var resetTarget, target = event.srcElement || event.target;
|
1136
|
+
var lastResetLink = target === instance.elements.reset[1];
|
1137
|
+
|
1138
|
+
// if last reset link, then go to maximize or close
|
1139
|
+
if (lastResetLink) {
|
1140
|
+
if (instance.setting('maximizable')) {
|
1141
|
+
resetTarget = instance.elements.commands.maximize;
|
1142
|
+
} else if (instance.setting('closable')) {
|
1143
|
+
resetTarget = instance.elements.commands.close;
|
1144
|
+
}
|
1145
|
+
}
|
1146
|
+
// if no reset target found, try finding the best button
|
1147
|
+
if (resetTarget === undefined) {
|
1148
|
+
if (typeof instance.__internal.focus.element === 'number') {
|
1149
|
+
// button focus element, go to first available button
|
1150
|
+
if (target === instance.elements.reset[0]) {
|
1151
|
+
resetTarget = instance.elements.buttons.auxiliary.firstChild || instance.elements.buttons.primary.firstChild;
|
1152
|
+
} else if (lastResetLink) {
|
1153
|
+
//restart the cycle by going to first reset link
|
1154
|
+
resetTarget = instance.elements.reset[0];
|
1155
|
+
}
|
1156
|
+
} else {
|
1157
|
+
// will reach here when tapping backwards, so go to last child
|
1158
|
+
// The focus element SHOULD NOT be a button (logically!).
|
1159
|
+
if (target === instance.elements.reset[0]) {
|
1160
|
+
resetTarget = instance.elements.buttons.primary.lastChild || instance.elements.buttons.auxiliary.lastChild;
|
1161
|
+
}
|
1162
|
+
}
|
1163
|
+
}
|
1164
|
+
// focus
|
1165
|
+
setFocus(instance, resetTarget);
|
1166
|
+
}
|
1167
|
+
}
|
1168
|
+
//animation timers
|
1169
|
+
var transitionInTimeout, transitionOutTimeout;
|
1170
|
+
/**
|
1171
|
+
* Transition in transitionend event handler.
|
1172
|
+
*
|
1173
|
+
* @param {Event} TransitionEnd event object.
|
1174
|
+
* @param {Object} The dilog instance.
|
1175
|
+
*
|
1176
|
+
* @return {undefined}
|
1177
|
+
*/
|
1178
|
+
function handleTransitionInEvent(event, instance) {
|
1179
|
+
// clear the timer
|
1180
|
+
clearTimeout(transitionInTimeout);
|
1181
|
+
|
1182
|
+
// once transition is complete, set focus
|
1183
|
+
setFocus(instance);
|
1184
|
+
|
1185
|
+
// allow handling key up after transition ended.
|
1186
|
+
cancelKeyup = false;
|
1187
|
+
|
1188
|
+
// allow custom `onfocus` method
|
1189
|
+
if (typeof instance.setting('onfocus') === 'function') {
|
1190
|
+
instance.setting('onfocus')();
|
1191
|
+
}
|
1192
|
+
|
1193
|
+
// unbind the event
|
1194
|
+
off(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
|
1195
|
+
|
1196
|
+
removeClass(instance.elements.root, classes.animationIn);
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
/**
|
1200
|
+
* Transition out transitionend event handler.
|
1201
|
+
*
|
1202
|
+
* @param {Event} TransitionEnd event object.
|
1203
|
+
* @param {Object} The dilog instance.
|
1204
|
+
*
|
1205
|
+
* @return {undefined}
|
1206
|
+
*/
|
1207
|
+
function handleTransitionOutEvent(event, instance) {
|
1208
|
+
// clear the timer
|
1209
|
+
clearTimeout(transitionOutTimeout);
|
1210
|
+
// unbind the event
|
1211
|
+
off(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
|
1212
|
+
|
1213
|
+
// reset move updates
|
1214
|
+
resetMove(instance);
|
1215
|
+
// reset resize updates
|
1216
|
+
resetResize(instance);
|
1217
|
+
|
1218
|
+
// restore if maximized
|
1219
|
+
if (instance.isMaximized()) {
|
1220
|
+
restore(instance);
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
// return focus to the last active element
|
1224
|
+
instance.__internal.activeElement.focus();
|
1225
|
+
}
|
1226
|
+
/* Controls moving a dialog around */
|
1227
|
+
//holde the current moving instance
|
1228
|
+
var movable = null,
|
1229
|
+
//holds the current X offset when move starts
|
1230
|
+
offsetX = 0,
|
1231
|
+
//holds the current Y offset when move starts
|
1232
|
+
offsetY = 0,
|
1233
|
+
xProp = 'pageX',
|
1234
|
+
yProp = 'pageY'
|
1235
|
+
;
|
1236
|
+
|
1237
|
+
/**
|
1238
|
+
* Helper: sets the element top/left coordinates
|
1239
|
+
*
|
1240
|
+
* @param {Event} event DOM event object.
|
1241
|
+
* @param {Node} element The element being moved.
|
1242
|
+
*
|
1243
|
+
* @return {undefined}
|
1244
|
+
*/
|
1245
|
+
function moveElement(event, element) {
|
1246
|
+
element.style.left = (event[xProp] - offsetX) + 'px';
|
1247
|
+
element.style.top = (event[yProp] - offsetY) + 'px';
|
1248
|
+
}
|
1249
|
+
|
1250
|
+
/**
|
1251
|
+
* Triggers the start of a move event, attached to the header element mouse down event.
|
1252
|
+
* Adds no-selection class to the body, disabling selection while moving.
|
1253
|
+
*
|
1254
|
+
* @param {Event} event DOM event object.
|
1255
|
+
* @param {Object} instance The dilog instance.
|
1256
|
+
*
|
1257
|
+
* @return {Boolean} false
|
1258
|
+
*/
|
1259
|
+
function beginMove(event, instance) {
|
1260
|
+
if (resizable === null && !instance.isMaximized() && instance.setting('movable')) {
|
1261
|
+
var eventSrc;
|
1262
|
+
if (event.type === 'touchstart') {
|
1263
|
+
event.preventDefault();
|
1264
|
+
eventSrc = event.targetTouches[0];
|
1265
|
+
xProp = 'clientX';
|
1266
|
+
yProp = 'clientY';
|
1267
|
+
} else if (event.button === 0) {
|
1268
|
+
eventSrc = event;
|
1269
|
+
}
|
1270
|
+
|
1271
|
+
if (eventSrc) {
|
1272
|
+
|
1273
|
+
movable = instance;
|
1274
|
+
offsetX = eventSrc[xProp];
|
1275
|
+
offsetY = eventSrc[yProp];
|
1276
|
+
|
1277
|
+
var element = instance.elements.dialog;
|
1278
|
+
|
1279
|
+
if (element.style.left) {
|
1280
|
+
offsetX -= parseInt(element.style.left, 10);
|
1281
|
+
}
|
1282
|
+
|
1283
|
+
if (element.style.top) {
|
1284
|
+
offsetY -= parseInt(element.style.top, 10);
|
1285
|
+
}
|
1286
|
+
moveElement(eventSrc, element);
|
1287
|
+
|
1288
|
+
addClass(document.body, classes.noSelection);
|
1289
|
+
return false;
|
1290
|
+
}
|
1291
|
+
}
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
/**
|
1295
|
+
* The actual move handler, attached to document.body mousemove event.
|
1296
|
+
*
|
1297
|
+
* @param {Event} event DOM event object.
|
1298
|
+
*
|
1299
|
+
* @return {undefined}
|
1300
|
+
*/
|
1301
|
+
function move(event) {
|
1302
|
+
if (movable) {
|
1303
|
+
var eventSrc;
|
1304
|
+
if (event.type === 'touchmove') {
|
1305
|
+
event.preventDefault();
|
1306
|
+
eventSrc = event.targetTouches[0];
|
1307
|
+
} else if (event.button === 0) {
|
1308
|
+
eventSrc = event;
|
1309
|
+
}
|
1310
|
+
if (eventSrc) {
|
1311
|
+
moveElement(eventSrc, movable.elements.dialog);
|
1312
|
+
}
|
1313
|
+
}
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
/**
|
1317
|
+
* Triggers the end of a move event, attached to document.body mouseup event.
|
1318
|
+
* Removes no-selection class from document.body, allowing selection.
|
1319
|
+
*
|
1320
|
+
* @return {undefined}
|
1321
|
+
*/
|
1322
|
+
function endMove() {
|
1323
|
+
if (movable) {
|
1324
|
+
movable = null;
|
1325
|
+
removeClass(document.body, classes.noSelection);
|
1326
|
+
}
|
1327
|
+
}
|
1328
|
+
|
1329
|
+
/**
|
1330
|
+
* Resets any changes made by moving the element to its original state,
|
1331
|
+
*
|
1332
|
+
* @param {Object} instance The dilog instance.
|
1333
|
+
*
|
1334
|
+
* @return {undefined}
|
1335
|
+
*/
|
1336
|
+
function resetMove(instance) {
|
1337
|
+
movable = null;
|
1338
|
+
var element = instance.elements.dialog;
|
1339
|
+
element.style.left = element.style.top = '';
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
/**
|
1343
|
+
* Updates the dialog move behavior.
|
1344
|
+
*
|
1345
|
+
* @param {Object} instance The dilog instance.
|
1346
|
+
* @param {Boolean} on True to add the behavior, removes it otherwise.
|
1347
|
+
*
|
1348
|
+
* @return {undefined}
|
1349
|
+
*/
|
1350
|
+
function updateMovable(instance) {
|
1351
|
+
if (instance.setting('movable')) {
|
1352
|
+
// add class
|
1353
|
+
addClass(instance.elements.root, classes.movable);
|
1354
|
+
if (instance.isOpen()) {
|
1355
|
+
bindMovableEvents(instance);
|
1356
|
+
}
|
1357
|
+
} else {
|
1358
|
+
|
1359
|
+
//reset
|
1360
|
+
resetMove(instance);
|
1361
|
+
// remove class
|
1362
|
+
removeClass(instance.elements.root, classes.movable);
|
1363
|
+
if (instance.isOpen()) {
|
1364
|
+
unbindMovableEvents(instance);
|
1365
|
+
}
|
1366
|
+
}
|
1367
|
+
}
|
1368
|
+
|
1369
|
+
/* Controls moving a dialog around */
|
1370
|
+
//holde the current instance being resized
|
1371
|
+
var resizable = null,
|
1372
|
+
//holds the staring left offset when resize starts.
|
1373
|
+
startingLeft = Number.Nan,
|
1374
|
+
//holds the staring width when resize starts.
|
1375
|
+
startingWidth = 0,
|
1376
|
+
//holds the initial width when resized for the first time.
|
1377
|
+
minWidth = 0,
|
1378
|
+
//holds the offset of the resize handle.
|
1379
|
+
handleOffset = 0
|
1380
|
+
;
|
1381
|
+
|
1382
|
+
/**
|
1383
|
+
* Helper: sets the element width/height and updates left coordinate if neccessary.
|
1384
|
+
*
|
1385
|
+
* @param {Event} event DOM mousemove event object.
|
1386
|
+
* @param {Node} element The element being moved.
|
1387
|
+
* @param {Boolean} pinned A flag indicating if the element being resized is pinned to the screen.
|
1388
|
+
*
|
1389
|
+
* @return {undefined}
|
1390
|
+
*/
|
1391
|
+
function resizeElement(event, element, pageRelative) {
|
1392
|
+
|
1393
|
+
//calculate offsets from 0,0
|
1394
|
+
var current = element;
|
1395
|
+
var offsetLeft = 0;
|
1396
|
+
var offsetTop = 0;
|
1397
|
+
do {
|
1398
|
+
offsetLeft += current.offsetLeft;
|
1399
|
+
offsetTop += current.offsetTop;
|
1400
|
+
} while (current = current.offsetParent);
|
1401
|
+
|
1402
|
+
// determine X,Y coordinates.
|
1403
|
+
var X, Y;
|
1404
|
+
if (pageRelative === true) {
|
1405
|
+
X = event.pageX;
|
1406
|
+
Y = event.pageY;
|
1407
|
+
} else {
|
1408
|
+
X = event.clientX;
|
1409
|
+
Y = event.clientY;
|
1410
|
+
}
|
1411
|
+
// rtl handling
|
1412
|
+
var isRTL = isRightToLeft();
|
1413
|
+
if (isRTL) {
|
1414
|
+
// reverse X
|
1415
|
+
X = document.body.offsetWidth - X;
|
1416
|
+
// if has a starting left, calculate offsetRight
|
1417
|
+
if (!isNaN(startingLeft)) {
|
1418
|
+
offsetLeft = document.body.offsetWidth - offsetLeft - element.offsetWidth;
|
1419
|
+
}
|
1420
|
+
}
|
1421
|
+
|
1422
|
+
// set width/height
|
1423
|
+
element.style.height = (Y - offsetTop + handleOffset) + 'px';
|
1424
|
+
element.style.width = (X - offsetLeft + handleOffset) + 'px';
|
1425
|
+
|
1426
|
+
// if the element being resized has a starting left, maintain it.
|
1427
|
+
// the dialog is centered, divide by half the offset to maintain the margins.
|
1428
|
+
if (!isNaN(startingLeft)) {
|
1429
|
+
var diff = Math.abs(element.offsetWidth - startingWidth) * 0.5;
|
1430
|
+
if (isRTL) {
|
1431
|
+
//negate the diff, why?
|
1432
|
+
//when growing it should decrease left
|
1433
|
+
//when shrinking it should increase left
|
1434
|
+
diff *= -1;
|
1435
|
+
}
|
1436
|
+
if (element.offsetWidth > startingWidth) {
|
1437
|
+
//growing
|
1438
|
+
element.style.left = (startingLeft + diff) + 'px';
|
1439
|
+
} else if (element.offsetWidth >= minWidth) {
|
1440
|
+
//shrinking
|
1441
|
+
element.style.left = (startingLeft - diff) + 'px';
|
1442
|
+
}
|
1443
|
+
}
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
/**
|
1447
|
+
* Triggers the start of a resize event, attached to the resize handle element mouse down event.
|
1448
|
+
* Adds no-selection class to the body, disabling selection while moving.
|
1449
|
+
*
|
1450
|
+
* @param {Event} event DOM event object.
|
1451
|
+
* @param {Object} instance The dilog instance.
|
1452
|
+
*
|
1453
|
+
* @return {Boolean} false
|
1454
|
+
*/
|
1455
|
+
function beginResize(event, instance) {
|
1456
|
+
if (!instance.isMaximized()) {
|
1457
|
+
var eventSrc;
|
1458
|
+
if (event.type === 'touchstart') {
|
1459
|
+
event.preventDefault();
|
1460
|
+
eventSrc = event.targetTouches[0];
|
1461
|
+
} else if (event.button === 0) {
|
1462
|
+
eventSrc = event;
|
1463
|
+
}
|
1464
|
+
if (eventSrc) {
|
1465
|
+
resizable = instance;
|
1466
|
+
handleOffset = instance.elements.resizeHandle.offsetHeight / 2;
|
1467
|
+
var element = instance.elements.dialog;
|
1468
|
+
startingLeft = parseInt(element.style.left, 10);
|
1469
|
+
element.style.height = element.offsetHeight + 'px';
|
1470
|
+
element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px';
|
1471
|
+
element.style.width = (startingWidth = element.offsetWidth) + 'px';
|
1472
|
+
|
1473
|
+
if (element.style.maxWidth !== 'none') {
|
1474
|
+
element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
|
1475
|
+
}
|
1476
|
+
element.style.maxWidth = 'none';
|
1477
|
+
addClass(document.body, classes.noSelection);
|
1478
|
+
return false;
|
1479
|
+
}
|
1480
|
+
}
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
/**
|
1484
|
+
* The actual resize handler, attached to document.body mousemove event.
|
1485
|
+
*
|
1486
|
+
* @param {Event} event DOM event object.
|
1487
|
+
*
|
1488
|
+
* @return {undefined}
|
1489
|
+
*/
|
1490
|
+
function resize(event) {
|
1491
|
+
if (resizable) {
|
1492
|
+
var eventSrc;
|
1493
|
+
if (event.type === 'touchmove') {
|
1494
|
+
event.preventDefault();
|
1495
|
+
eventSrc = event.targetTouches[0];
|
1496
|
+
} else if (event.button === 0) {
|
1497
|
+
eventSrc = event;
|
1498
|
+
}
|
1499
|
+
if (eventSrc) {
|
1500
|
+
resizeElement(eventSrc, resizable.elements.dialog, !resizable.setting('modal') && !resizable.setting('pinned'));
|
1501
|
+
}
|
1502
|
+
}
|
1503
|
+
}
|
1504
|
+
|
1505
|
+
/**
|
1506
|
+
* Triggers the end of a resize event, attached to document.body mouseup event.
|
1507
|
+
* Removes no-selection class from document.body, allowing selection.
|
1508
|
+
*
|
1509
|
+
* @return {undefined}
|
1510
|
+
*/
|
1511
|
+
function endResize() {
|
1512
|
+
if (resizable) {
|
1513
|
+
resizable = null;
|
1514
|
+
removeClass(document.body, classes.noSelection);
|
1515
|
+
cancelClick = true;
|
1516
|
+
}
|
1517
|
+
}
|
1518
|
+
|
1519
|
+
/**
|
1520
|
+
* Resets any changes made by resizing the element to its original state.
|
1521
|
+
*
|
1522
|
+
* @param {Object} instance The dilog instance.
|
1523
|
+
*
|
1524
|
+
* @return {undefined}
|
1525
|
+
*/
|
1526
|
+
function resetResize(instance) {
|
1527
|
+
resizable = null;
|
1528
|
+
var element = instance.elements.dialog;
|
1529
|
+
if (element.style.maxWidth === 'none') {
|
1530
|
+
//clear inline styles.
|
1531
|
+
element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = '';
|
1532
|
+
//reset variables.
|
1533
|
+
startingLeft = Number.Nan;
|
1534
|
+
startingWidth = minWidth = handleOffset = 0;
|
1535
|
+
}
|
1536
|
+
}
|
1537
|
+
|
1538
|
+
|
1539
|
+
/**
|
1540
|
+
* Updates the dialog move behavior.
|
1541
|
+
*
|
1542
|
+
* @param {Object} instance The dilog instance.
|
1543
|
+
* @param {Boolean} on True to add the behavior, removes it otherwise.
|
1544
|
+
*
|
1545
|
+
* @return {undefined}
|
1546
|
+
*/
|
1547
|
+
function updateResizable(instance) {
|
1548
|
+
if (instance.setting('resizable')) {
|
1549
|
+
// add class
|
1550
|
+
addClass(instance.elements.root, classes.resizable);
|
1551
|
+
if (instance.isOpen()) {
|
1552
|
+
bindResizableEvents(instance);
|
1553
|
+
}
|
1554
|
+
} else {
|
1555
|
+
//reset
|
1556
|
+
resetResize(instance);
|
1557
|
+
// remove class
|
1558
|
+
removeClass(instance.elements.root, classes.resizable);
|
1559
|
+
if (instance.isOpen()) {
|
1560
|
+
unbindResizableEvents(instance);
|
1561
|
+
}
|
1562
|
+
}
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
/**
|
1566
|
+
* Reset move/resize on window resize.
|
1567
|
+
*
|
1568
|
+
* @param {Event} event window resize event object.
|
1569
|
+
*
|
1570
|
+
* @return {undefined}
|
1571
|
+
*/
|
1572
|
+
function windowResize(/*event*/) {
|
1573
|
+
for (var x = 0; x < openInstances.length; x += 1) {
|
1574
|
+
var instance = openInstances[x];
|
1575
|
+
resetMove(instance);
|
1576
|
+
resetResize(instance);
|
1577
|
+
}
|
1578
|
+
}
|
1579
|
+
/**
|
1580
|
+
* Bind dialogs events
|
1581
|
+
*
|
1582
|
+
* @param {Object} instance The dilog instance.
|
1583
|
+
*
|
1584
|
+
* @return {undefined}
|
1585
|
+
*/
|
1586
|
+
function bindEvents(instance) {
|
1587
|
+
// if first dialog, hook body handlers
|
1588
|
+
if (openInstances.length === 1) {
|
1589
|
+
//global
|
1590
|
+
on(window, 'resize', windowResize);
|
1591
|
+
on(document.body, 'keyup', keyupHandler);
|
1592
|
+
on(document.body, 'keydown', keydownHandler);
|
1593
|
+
on(document.body, 'focus', onReset);
|
1594
|
+
|
1595
|
+
//move
|
1596
|
+
on(document.body, 'mousemove', move);
|
1597
|
+
on(document.body, 'touchmove', move);
|
1598
|
+
on(document.body, 'mouseup', endMove);
|
1599
|
+
on(document.body, 'touchend', endMove);
|
1600
|
+
//resize
|
1601
|
+
on(document.body, 'mousemove', resize);
|
1602
|
+
on(document.body, 'touchmove', resize);
|
1603
|
+
on(document.body, 'mouseup', endResize);
|
1604
|
+
on(document.body, 'touchend', endResize);
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
// common events
|
1608
|
+
on(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
|
1609
|
+
on(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
|
1610
|
+
on(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
|
1611
|
+
on(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
|
1612
|
+
|
1613
|
+
//prevent handling key up when dialog is being opened by a key stroke.
|
1614
|
+
cancelKeyup = true;
|
1615
|
+
// hook in transition handler
|
1616
|
+
on(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
|
1617
|
+
|
1618
|
+
// modelss only events
|
1619
|
+
if (!instance.setting('modal')) {
|
1620
|
+
bindModelessEvents(instance);
|
1621
|
+
}
|
1622
|
+
|
1623
|
+
// resizable
|
1624
|
+
if (instance.setting('resizable')) {
|
1625
|
+
bindResizableEvents(instance);
|
1626
|
+
}
|
1627
|
+
|
1628
|
+
// movable
|
1629
|
+
if (instance.setting('movable')) {
|
1630
|
+
bindMovableEvents(instance);
|
1631
|
+
}
|
1632
|
+
}
|
1633
|
+
|
1634
|
+
/**
|
1635
|
+
* Unbind dialogs events
|
1636
|
+
*
|
1637
|
+
* @param {Object} instance The dilog instance.
|
1638
|
+
*
|
1639
|
+
* @return {undefined}
|
1640
|
+
*/
|
1641
|
+
function unbindEvents(instance) {
|
1642
|
+
// if last dialog, remove body handlers
|
1643
|
+
if (openInstances.length === 1) {
|
1644
|
+
//global
|
1645
|
+
off(window, 'resize', windowResize);
|
1646
|
+
off(document.body, 'keyup', keyupHandler);
|
1647
|
+
off(document.body, 'keydown', keydownHandler);
|
1648
|
+
off(document.body, 'focus', onReset);
|
1649
|
+
//move
|
1650
|
+
off(document.body, 'mousemove', move);
|
1651
|
+
off(document.body, 'mouseup', endMove);
|
1652
|
+
//resize
|
1653
|
+
off(document.body, 'mousemove', resize);
|
1654
|
+
off(document.body, 'mouseup', endResize);
|
1655
|
+
}
|
1656
|
+
|
1657
|
+
// common events
|
1658
|
+
off(instance.elements.commands.container, 'click', instance.__internal.commandsClickHandler);
|
1659
|
+
off(instance.elements.footer, 'click', instance.__internal.buttonsClickHandler);
|
1660
|
+
off(instance.elements.reset[0], 'focus', instance.__internal.resetHandler);
|
1661
|
+
off(instance.elements.reset[1], 'focus', instance.__internal.resetHandler);
|
1662
|
+
|
1663
|
+
// hook out transition handler
|
1664
|
+
on(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
|
1665
|
+
|
1666
|
+
// modelss only events
|
1667
|
+
if (!instance.setting('modal')) {
|
1668
|
+
unbindModelessEvents(instance);
|
1669
|
+
}
|
1670
|
+
|
1671
|
+
// movable
|
1672
|
+
if (instance.setting('movable')) {
|
1673
|
+
unbindMovableEvents(instance);
|
1674
|
+
}
|
1675
|
+
|
1676
|
+
// resizable
|
1677
|
+
if (instance.setting('resizable')) {
|
1678
|
+
unbindResizableEvents(instance);
|
1679
|
+
}
|
1680
|
+
|
1681
|
+
}
|
1682
|
+
|
1683
|
+
/**
|
1684
|
+
* Bind modeless specific events
|
1685
|
+
*
|
1686
|
+
* @param {Object} instance The dilog instance.
|
1687
|
+
*
|
1688
|
+
* @return {undefined}
|
1689
|
+
*/
|
1690
|
+
function bindModelessEvents(instance) {
|
1691
|
+
on(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
/**
|
1695
|
+
* Unbind modeless specific events
|
1696
|
+
*
|
1697
|
+
* @param {Object} instance The dilog instance.
|
1698
|
+
*
|
1699
|
+
* @return {undefined}
|
1700
|
+
*/
|
1701
|
+
function unbindModelessEvents(instance) {
|
1702
|
+
off(instance.elements.dialog, 'focus', instance.__internal.bringToFrontHandler, true);
|
1703
|
+
}
|
1704
|
+
|
1705
|
+
|
1706
|
+
|
1707
|
+
/**
|
1708
|
+
* Bind movable specific events
|
1709
|
+
*
|
1710
|
+
* @param {Object} instance The dilog instance.
|
1711
|
+
*
|
1712
|
+
* @return {undefined}
|
1713
|
+
*/
|
1714
|
+
function bindMovableEvents(instance) {
|
1715
|
+
on(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
|
1716
|
+
on(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
|
1717
|
+
}
|
1718
|
+
|
1719
|
+
/**
|
1720
|
+
* Unbind movable specific events
|
1721
|
+
*
|
1722
|
+
* @param {Object} instance The dilog instance.
|
1723
|
+
*
|
1724
|
+
* @return {undefined}
|
1725
|
+
*/
|
1726
|
+
function unbindMovableEvents(instance) {
|
1727
|
+
off(instance.elements.header, 'mousedown', instance.__internal.beginMoveHandler);
|
1728
|
+
off(instance.elements.header, 'touchstart', instance.__internal.beginMoveHandler);
|
1729
|
+
}
|
1730
|
+
|
1731
|
+
|
1732
|
+
|
1733
|
+
/**
|
1734
|
+
* Bind resizable specific events
|
1735
|
+
*
|
1736
|
+
* @param {Object} instance The dilog instance.
|
1737
|
+
*
|
1738
|
+
* @return {undefined}
|
1739
|
+
*/
|
1740
|
+
function bindResizableEvents(instance) {
|
1741
|
+
on(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
|
1742
|
+
on(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
|
1743
|
+
}
|
1744
|
+
|
1745
|
+
/**
|
1746
|
+
* Unbind resizable specific events
|
1747
|
+
*
|
1748
|
+
* @param {Object} instance The dilog instance.
|
1749
|
+
*
|
1750
|
+
* @return {undefined}
|
1751
|
+
*/
|
1752
|
+
function unbindResizableEvents(instance) {
|
1753
|
+
off(instance.elements.resizeHandle, 'mousedown', instance.__internal.beginResizeHandler);
|
1754
|
+
off(instance.elements.resizeHandle, 'touchstart', instance.__internal.beginResizeHandler);
|
1755
|
+
}
|
1756
|
+
|
1757
|
+
/**
|
1758
|
+
* Bind closable events
|
1759
|
+
*
|
1760
|
+
* @param {Object} instance The dilog instance.
|
1761
|
+
*
|
1762
|
+
* @return {undefined}
|
1763
|
+
*/
|
1764
|
+
function bindClosableEvents(instance) {
|
1765
|
+
on(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
|
1766
|
+
}
|
1767
|
+
|
1768
|
+
/**
|
1769
|
+
* Unbind closable specific events
|
1770
|
+
*
|
1771
|
+
* @param {Object} instance The dilog instance.
|
1772
|
+
*
|
1773
|
+
* @return {undefined}
|
1774
|
+
*/
|
1775
|
+
function unbindClosableEvents(instance) {
|
1776
|
+
off(instance.elements.modal, 'click', instance.__internal.modalClickHandler);
|
1777
|
+
}
|
1778
|
+
// dialog API
|
1779
|
+
return {
|
1780
|
+
__init:initialize,
|
1781
|
+
/**
|
1782
|
+
* Check if dialog is currently open
|
1783
|
+
*
|
1784
|
+
* @return {Boolean}
|
1785
|
+
*/
|
1786
|
+
isOpen: function () {
|
1787
|
+
return this.__internal.isOpen;
|
1788
|
+
},
|
1789
|
+
isModal: function (){
|
1790
|
+
return this.elements.root.className.indexOf(classes.modeless) < 0;
|
1791
|
+
},
|
1792
|
+
isMaximized:function(){
|
1793
|
+
return this.elements.root.className.indexOf(classes.maximized) > -1;
|
1794
|
+
},
|
1795
|
+
isPinned:function(){
|
1796
|
+
return this.elements.root.className.indexOf(classes.unpinned) < 0;
|
1797
|
+
},
|
1798
|
+
maximize:function(){
|
1799
|
+
if(!this.isMaximized()){
|
1800
|
+
maximize(this);
|
1801
|
+
}
|
1802
|
+
return this;
|
1803
|
+
},
|
1804
|
+
restore:function(){
|
1805
|
+
if(this.isMaximized()){
|
1806
|
+
restore(this);
|
1807
|
+
}
|
1808
|
+
return this;
|
1809
|
+
},
|
1810
|
+
pin:function(){
|
1811
|
+
if(!this.isPinned()){
|
1812
|
+
pin(this);
|
1813
|
+
}
|
1814
|
+
return this;
|
1815
|
+
},
|
1816
|
+
unpin:function(){
|
1817
|
+
if(this.isPinned()){
|
1818
|
+
unpin(this);
|
1819
|
+
}
|
1820
|
+
return this;
|
1821
|
+
},
|
1822
|
+
/**
|
1823
|
+
* Gets or Sets dialog settings/options
|
1824
|
+
*
|
1825
|
+
* @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
|
1826
|
+
* @param {Object} value Optional, the value associated with the key (in case it was a string).
|
1827
|
+
*
|
1828
|
+
* @return {undefined}
|
1829
|
+
*/
|
1830
|
+
setting : function (key, value) {
|
1831
|
+
var self = this;
|
1832
|
+
var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value);
|
1833
|
+
if(result.op === 'get'){
|
1834
|
+
if(result.found){
|
1835
|
+
return result.value;
|
1836
|
+
}else if(typeof this.settings !== 'undefined'){
|
1837
|
+
return update(this, this.settings, this.settingUpdated || function(){}, key, value).value;
|
1838
|
+
}else{
|
1839
|
+
return undefined;
|
1840
|
+
}
|
1841
|
+
}else if(result.op === 'set'){
|
1842
|
+
if(result.items.length > 0){
|
1843
|
+
var callback = this.settingUpdated || function(){};
|
1844
|
+
for(var x=0;x<result.items.length;x+=1){
|
1845
|
+
var item = result.items[x];
|
1846
|
+
if(!item.found && typeof this.settings !== 'undefined'){
|
1847
|
+
update(this, this.settings, callback, item.key, item.value);
|
1848
|
+
}
|
1849
|
+
}
|
1850
|
+
}
|
1851
|
+
return this;
|
1852
|
+
}
|
1853
|
+
},
|
1854
|
+
/**
|
1855
|
+
* Sets dialog header
|
1856
|
+
* @content {string or element}
|
1857
|
+
*
|
1858
|
+
* @return {undefined}
|
1859
|
+
*/
|
1860
|
+
setHeader:function(content){
|
1861
|
+
if(typeof content === 'string'){
|
1862
|
+
this.elements.header.innerHTML = content;
|
1863
|
+
}else if (content instanceof window.HTMLElement && this.elements.header.firstChild !== content){
|
1864
|
+
this.elements.header.innerHTML = '';
|
1865
|
+
this.elements.header.appendChild(content);
|
1866
|
+
}
|
1867
|
+
return this;
|
1868
|
+
},
|
1869
|
+
/**
|
1870
|
+
* Sets dialog contents
|
1871
|
+
* @content {string or element}
|
1872
|
+
*
|
1873
|
+
* @return {undefined}
|
1874
|
+
*/
|
1875
|
+
setContent:function(content){
|
1876
|
+
if(typeof content === 'string'){
|
1877
|
+
this.elements.content.innerHTML = content;
|
1878
|
+
}else if (content instanceof window.HTMLElement && this.elements.content.firstChild !== content){
|
1879
|
+
this.elements.content.innerHTML = '';
|
1880
|
+
this.elements.content.appendChild(content);
|
1881
|
+
}
|
1882
|
+
return this;
|
1883
|
+
},
|
1884
|
+
/**
|
1885
|
+
* Show the dialog as modal
|
1886
|
+
*
|
1887
|
+
* @return {Object} the dialog instance.
|
1888
|
+
*/
|
1889
|
+
showModal: function(className){
|
1890
|
+
return this.show(true, className);
|
1891
|
+
},
|
1892
|
+
/**
|
1893
|
+
* Show the dialog
|
1894
|
+
*
|
1895
|
+
* @return {Object} the dialog instance.
|
1896
|
+
*/
|
1897
|
+
show: function (modal, className) {
|
1898
|
+
|
1899
|
+
// ensure initialization
|
1900
|
+
initialize(this);
|
1901
|
+
|
1902
|
+
if ( !this.__internal.isOpen ) {
|
1903
|
+
|
1904
|
+
// add to open dialogs
|
1905
|
+
this.__internal.isOpen = true;
|
1906
|
+
openInstances.push(this);
|
1907
|
+
|
1908
|
+
// save last focused element
|
1909
|
+
this.__internal.activeElement = document.activeElement;
|
1910
|
+
|
1911
|
+
//allow custom dom manipulation updates before showing the dialog.
|
1912
|
+
if(typeof this.prepare === 'function'){
|
1913
|
+
this.prepare();
|
1914
|
+
}
|
1915
|
+
|
1916
|
+
bindEvents(this);
|
1917
|
+
|
1918
|
+
if(modal !== undefined){
|
1919
|
+
this.setting('modal', modal);
|
1920
|
+
}
|
1921
|
+
|
1922
|
+
ensureNoOverflow();
|
1923
|
+
|
1924
|
+
// allow custom dialog class on show
|
1925
|
+
if(typeof className === 'string' && className !== ''){
|
1926
|
+
this.__internal.className = className;
|
1927
|
+
addClass(this.elements.root, className);
|
1928
|
+
}
|
1929
|
+
|
1930
|
+
updateAbsPositionFix(this);
|
1931
|
+
|
1932
|
+
removeClass(this.elements.root, classes.animationOut);
|
1933
|
+
addClass(this.elements.root, classes.animationIn);
|
1934
|
+
|
1935
|
+
// set 1s fallback in case transition event doesn't fire
|
1936
|
+
clearTimeout( transitionInTimeout );
|
1937
|
+
transitionInTimeout = setTimeout( this.__internal.transitionInHandler, transition.supported ? 1000 : 100 );
|
1938
|
+
|
1939
|
+
if(isSafari){
|
1940
|
+
// force desktop safari reflow
|
1941
|
+
var root = this.elements.root;
|
1942
|
+
root.style.display = 'none';
|
1943
|
+
setTimeout(function(){root.style.display = 'block';}, 0);
|
1944
|
+
}
|
1945
|
+
|
1946
|
+
//reflow
|
1947
|
+
reflow = this.elements.root.offsetWidth;
|
1948
|
+
|
1949
|
+
// show dialog
|
1950
|
+
removeClass(this.elements.root, classes.hidden);
|
1951
|
+
|
1952
|
+
// allow custom `onshow` method
|
1953
|
+
if ( typeof this.setting('onshow') === 'function' ) {
|
1954
|
+
this.setting('onshow')();
|
1955
|
+
}
|
1956
|
+
}else{
|
1957
|
+
// reset move updates
|
1958
|
+
resetMove(this);
|
1959
|
+
// reset resize updates
|
1960
|
+
resetResize(this);
|
1961
|
+
// shake the dialog to indicate its already open
|
1962
|
+
addClass(this.elements.dialog, classes.shake);
|
1963
|
+
var self = this;
|
1964
|
+
setTimeout(function(){
|
1965
|
+
removeClass(self.elements.dialog, classes.shake);
|
1966
|
+
},200);
|
1967
|
+
}
|
1968
|
+
return this;
|
1969
|
+
},
|
1970
|
+
/**
|
1971
|
+
* Close the dialog
|
1972
|
+
*
|
1973
|
+
* @return {undefined}
|
1974
|
+
*/
|
1975
|
+
close: function () {
|
1976
|
+
if (this.__internal.isOpen ) {
|
1977
|
+
|
1978
|
+
unbindEvents(this);
|
1979
|
+
|
1980
|
+
removeClass(this.elements.root, classes.animationIn);
|
1981
|
+
addClass(this.elements.root, classes.animationOut);
|
1982
|
+
|
1983
|
+
// set 1s fallback in case transition event doesn't fire
|
1984
|
+
clearTimeout( transitionOutTimeout );
|
1985
|
+
transitionOutTimeout = setTimeout( this.__internal.transitionOutHandler, transition.supported ? 1000 : 100 );
|
1986
|
+
|
1987
|
+
// hide dialog
|
1988
|
+
addClass(this.elements.root, classes.hidden);
|
1989
|
+
//reflow
|
1990
|
+
reflow = this.elements.modal.offsetWidth;
|
1991
|
+
|
1992
|
+
// remove custom dialog class on hide
|
1993
|
+
if (typeof this.__internal.className !== 'undefined' && this.__internal.className !== '') {
|
1994
|
+
removeClass(this.elements.root, this.__internal.className);
|
1995
|
+
}
|
1996
|
+
|
1997
|
+
// allow custom `onclose` method
|
1998
|
+
if ( typeof this.setting('onclose') === 'function' ) {
|
1999
|
+
this.setting('onclose')();
|
2000
|
+
}
|
2001
|
+
|
2002
|
+
//remove from open dialogs
|
2003
|
+
openInstances.splice(openInstances.indexOf(this),1);
|
2004
|
+
this.__internal.isOpen = false;
|
2005
|
+
|
2006
|
+
ensureNoOverflow();
|
2007
|
+
|
2008
|
+
}
|
2009
|
+
return this;
|
2010
|
+
},
|
2011
|
+
};
|
2012
|
+
} () );
|
2013
|
+
var notifier = (function () {
|
2014
|
+
var reflow,
|
2015
|
+
element,
|
2016
|
+
classes = {
|
2017
|
+
base: 'alertify-notifier',
|
2018
|
+
message: 'ajs-message',
|
2019
|
+
top: 'ajs-top',
|
2020
|
+
right: 'ajs-right',
|
2021
|
+
bottom: 'ajs-bottom',
|
2022
|
+
left: 'ajs-left',
|
2023
|
+
visible: 'ajs-visible',
|
2024
|
+
hidden: 'ajs-hidden'
|
2025
|
+
};
|
2026
|
+
/**
|
2027
|
+
* Helper: initializes the notifier instance
|
2028
|
+
*
|
2029
|
+
*/
|
2030
|
+
function initialize(instance) {
|
2031
|
+
|
2032
|
+
if (!instance.__internal) {
|
2033
|
+
instance.__internal = {
|
2034
|
+
position: alertify.defaults.notifier.position,
|
2035
|
+
delay: alertify.defaults.notifier.delay,
|
2036
|
+
};
|
2037
|
+
|
2038
|
+
element = document.createElement('DIV');
|
2039
|
+
|
2040
|
+
updatePosition(instance);
|
2041
|
+
|
2042
|
+
//add to DOM tree.
|
2043
|
+
document.body.appendChild(element);
|
2044
|
+
}
|
2045
|
+
}
|
2046
|
+
|
2047
|
+
/**
|
2048
|
+
* Helper: update the notifier instance position
|
2049
|
+
*
|
2050
|
+
*/
|
2051
|
+
function updatePosition(instance) {
|
2052
|
+
element.className = classes.base;
|
2053
|
+
switch (instance.__internal.position) {
|
2054
|
+
case 'top-right':
|
2055
|
+
addClass(element, classes.top + ' ' + classes.right);
|
2056
|
+
break;
|
2057
|
+
case 'top-left':
|
2058
|
+
addClass(element, classes.top + ' ' + classes.left);
|
2059
|
+
break;
|
2060
|
+
case 'bottom-left':
|
2061
|
+
addClass(element, classes.bottom + ' ' + classes.left);
|
2062
|
+
break;
|
2063
|
+
|
2064
|
+
default:
|
2065
|
+
case 'bottom-right':
|
2066
|
+
addClass(element, classes.bottom + ' ' + classes.right);
|
2067
|
+
break;
|
2068
|
+
}
|
2069
|
+
}
|
2070
|
+
|
2071
|
+
/**
|
2072
|
+
* creates a new notification message
|
2073
|
+
*
|
2074
|
+
* @param {DOMElement} message The notifier message element
|
2075
|
+
* @param {Number} wait Time (in ms) to wait before the message is dismissed, a value of 0 means keep open till clicked.
|
2076
|
+
* @param {Function} callback A callback function to be invoked when the message is dismissed.
|
2077
|
+
*
|
2078
|
+
* @return {undefined}
|
2079
|
+
*/
|
2080
|
+
function create(div, callback) {
|
2081
|
+
|
2082
|
+
function clickDelegate(event, instance) {
|
2083
|
+
instance.dismiss(true);
|
2084
|
+
}
|
2085
|
+
|
2086
|
+
function transitionDone(event, instance) {
|
2087
|
+
// unbind event
|
2088
|
+
off(instance.__internal.element, transition.type, transitionDone);
|
2089
|
+
// remove the message
|
2090
|
+
element.removeChild(instance.__internal.element);
|
2091
|
+
}
|
2092
|
+
|
2093
|
+
function initialize(instance) {
|
2094
|
+
if (!instance.__internal) {
|
2095
|
+
instance.__internal = {
|
2096
|
+
pushed: false,
|
2097
|
+
delay : undefined,
|
2098
|
+
timer: undefined,
|
2099
|
+
element: div,
|
2100
|
+
clickHandler: undefined,
|
2101
|
+
transitionEndHandler: undefined,
|
2102
|
+
transitionTimeout: undefined
|
2103
|
+
};
|
2104
|
+
instance.__internal.clickHandler = delegate(instance, clickDelegate);
|
2105
|
+
instance.__internal.transitionEndHandler = delegate(instance, transitionDone);
|
2106
|
+
}
|
2107
|
+
return instance;
|
2108
|
+
}
|
2109
|
+
function clearTimers(instance) {
|
2110
|
+
clearTimeout(instance.__internal.timer);
|
2111
|
+
clearTimeout(instance.__internal.transitionTimeout);
|
2112
|
+
}
|
2113
|
+
return initialize({
|
2114
|
+
/*
|
2115
|
+
* Pushes a notification message
|
2116
|
+
* @param {string or DOMElement} content The notification message content
|
2117
|
+
* @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
|
2118
|
+
*
|
2119
|
+
*/
|
2120
|
+
push: function (_content, _wait) {
|
2121
|
+
if (!this.__internal.pushed) {
|
2122
|
+
|
2123
|
+
this.__internal.pushed = true;
|
2124
|
+
clearTimers(this);
|
2125
|
+
|
2126
|
+
var content, wait;
|
2127
|
+
switch (arguments.length) {
|
2128
|
+
case 0:
|
2129
|
+
wait = this.__internal.delay;
|
2130
|
+
break;
|
2131
|
+
case 1:
|
2132
|
+
if (typeof (_content) === 'number') {
|
2133
|
+
wait = _content;
|
2134
|
+
} else {
|
2135
|
+
content = _content;
|
2136
|
+
}
|
2137
|
+
break;
|
2138
|
+
case 2:
|
2139
|
+
content = _content;
|
2140
|
+
wait = _wait;
|
2141
|
+
break;
|
2142
|
+
}
|
2143
|
+
// set contents
|
2144
|
+
if (typeof content !== 'undefined') {
|
2145
|
+
this.setContent(content);
|
2146
|
+
}
|
2147
|
+
// append or insert
|
2148
|
+
if (notifier.__internal.position.indexOf('top') < 0) {
|
2149
|
+
element.appendChild(this.__internal.element);
|
2150
|
+
} else {
|
2151
|
+
element.insertBefore(this.__internal.element, element.firstChild);
|
2152
|
+
}
|
2153
|
+
reflow = this.__internal.element.offsetWidth;
|
2154
|
+
addClass(this.__internal.element, classes.visible);
|
2155
|
+
// attach click event
|
2156
|
+
on(this.__internal.element, 'click', this.__internal.clickHandler);
|
2157
|
+
return this.delay(wait);
|
2158
|
+
}
|
2159
|
+
return this;
|
2160
|
+
},
|
2161
|
+
/*
|
2162
|
+
* {Function} callback function to be invoked before dismissing the notification message.
|
2163
|
+
* Remarks: A return value === 'false' will cancel the dismissal
|
2164
|
+
*
|
2165
|
+
*/
|
2166
|
+
ondismiss: function () { },
|
2167
|
+
/*
|
2168
|
+
* {Function} callback function to be invoked when the message is dismissed.
|
2169
|
+
*
|
2170
|
+
*/
|
2171
|
+
callback: callback,
|
2172
|
+
/*
|
2173
|
+
* Dismisses the notification message
|
2174
|
+
* @param {Boolean} clicked A flag indicating if the dismissal was caused by a click.
|
2175
|
+
*
|
2176
|
+
*/
|
2177
|
+
dismiss: function (clicked) {
|
2178
|
+
if (this.__internal.pushed) {
|
2179
|
+
clearTimers(this);
|
2180
|
+
if (!(typeof this.ondismiss === 'function' && this.ondismiss.call(this) === false)) {
|
2181
|
+
//detach click event
|
2182
|
+
off(this.__internal.element, 'click', this.__internal.clickHandler);
|
2183
|
+
// ensure element exists
|
2184
|
+
if (typeof this.__internal.element !== 'undefined' && this.__internal.element.parentNode === element) {
|
2185
|
+
//transition end or fallback
|
2186
|
+
this.__internal.transitionTimeout = setTimeout(this.__internal.transitionEndHandler, transition.supported ? 1000 : 100);
|
2187
|
+
removeClass(this.__internal.element, classes.visible);
|
2188
|
+
|
2189
|
+
// custom callback on dismiss
|
2190
|
+
if (typeof this.callback === 'function') {
|
2191
|
+
this.callback.call(this, clicked);
|
2192
|
+
}
|
2193
|
+
}
|
2194
|
+
this.__internal.pushed = false;
|
2195
|
+
}
|
2196
|
+
}
|
2197
|
+
return this;
|
2198
|
+
},
|
2199
|
+
/*
|
2200
|
+
* Delays the notification message dismissal
|
2201
|
+
* @param {Number} wait The time (in seconds) to wait before the message is dismissed, a value of 0 means keep open till clicked.
|
2202
|
+
*
|
2203
|
+
*/
|
2204
|
+
delay: function (wait) {
|
2205
|
+
clearTimers(this);
|
2206
|
+
this.__internal.delay = typeof wait !== 'undefined' && !isNaN(+wait) ? +wait : notifier.__internal.delay;
|
2207
|
+
if (this.__internal.delay > 0) {
|
2208
|
+
var self = this;
|
2209
|
+
this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000);
|
2210
|
+
}
|
2211
|
+
return this;
|
2212
|
+
},
|
2213
|
+
/*
|
2214
|
+
* Sets the notification message contents
|
2215
|
+
* @param {string or DOMElement} content The notification message content
|
2216
|
+
*
|
2217
|
+
*/
|
2218
|
+
setContent: function (content) {
|
2219
|
+
if (typeof content === 'string') {
|
2220
|
+
this.__internal.element.innerHTML = content;
|
2221
|
+
} else {
|
2222
|
+
this.__internal.element.appendChild(content);
|
2223
|
+
}
|
2224
|
+
return this;
|
2225
|
+
}
|
2226
|
+
});
|
2227
|
+
}
|
2228
|
+
|
2229
|
+
//notifier api
|
2230
|
+
return {
|
2231
|
+
/**
|
2232
|
+
* Gets or Sets notifier settings.
|
2233
|
+
*
|
2234
|
+
* @param {string} key The setting name
|
2235
|
+
* @param {Variant} value The setting value.
|
2236
|
+
*
|
2237
|
+
* @return {Object} if the called as a setter, return the notifier instance.
|
2238
|
+
*/
|
2239
|
+
setting: function (key, value) {
|
2240
|
+
//ensure init
|
2241
|
+
initialize(this);
|
2242
|
+
|
2243
|
+
if (typeof value === 'undefined') {
|
2244
|
+
//get
|
2245
|
+
return this.__internal[key];
|
2246
|
+
} else {
|
2247
|
+
//set
|
2248
|
+
switch (key) {
|
2249
|
+
case 'position':
|
2250
|
+
this.__internal.position = value;
|
2251
|
+
updatePosition(this);
|
2252
|
+
break;
|
2253
|
+
case 'delay':
|
2254
|
+
this.__internal.delay = value;
|
2255
|
+
break;
|
2256
|
+
}
|
2257
|
+
}
|
2258
|
+
return this;
|
2259
|
+
},
|
2260
|
+
/**
|
2261
|
+
* Creates a new notification message
|
2262
|
+
*
|
2263
|
+
* @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
|
2264
|
+
* @param {Function} callback A callback function to be invoked when the message is dismissed.
|
2265
|
+
*
|
2266
|
+
* @return {undefined}
|
2267
|
+
*/
|
2268
|
+
create: function (type, callback) {
|
2269
|
+
//ensure notifier init
|
2270
|
+
initialize(this);
|
2271
|
+
//create new notification message
|
2272
|
+
var div = document.createElement('div');
|
2273
|
+
div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ajs-' + type : '');
|
2274
|
+
return create(div, callback);
|
2275
|
+
}
|
2276
|
+
};
|
2277
|
+
})();
|
2278
|
+
/**
|
2279
|
+
* Alertify public API
|
2280
|
+
* This contains everything that is exposed through the alertify object.
|
2281
|
+
*
|
2282
|
+
* @return {Object}
|
2283
|
+
*/
|
2284
|
+
function Alertify() {
|
2285
|
+
|
2286
|
+
// holds a references of created dialogs
|
2287
|
+
var dialogs = {};
|
2288
|
+
|
2289
|
+
/**
|
2290
|
+
* Extends a given prototype by merging properties from base into sub.
|
2291
|
+
*
|
2292
|
+
* @sub {Object} sub The prototype being overwritten.
|
2293
|
+
* @base {Object} base The prototype being written.
|
2294
|
+
*
|
2295
|
+
* @return {Object} The extended prototype.
|
2296
|
+
*/
|
2297
|
+
function extend(sub, base) {
|
2298
|
+
// copy dialog pototype over definition.
|
2299
|
+
for (var prop in base) {
|
2300
|
+
if (base.hasOwnProperty(prop)) {
|
2301
|
+
sub[prop] = base[prop];
|
2302
|
+
}
|
2303
|
+
}
|
2304
|
+
return sub;
|
2305
|
+
}
|
2306
|
+
|
2307
|
+
|
2308
|
+
/**
|
2309
|
+
* Helper: returns a dialog instance from saved dialogs.
|
2310
|
+
* and initializes the dialog if its not already initialized.
|
2311
|
+
*
|
2312
|
+
* @name {String} name The dialog name.
|
2313
|
+
*
|
2314
|
+
* @return {Object} The dialog instance.
|
2315
|
+
*/
|
2316
|
+
function get_dialog(name) {
|
2317
|
+
var dialog = dialogs[name].dialog;
|
2318
|
+
//initialize the dialog if its not already initialized.
|
2319
|
+
if (dialog && typeof dialog.__init === 'function') {
|
2320
|
+
dialog.__init(dialog);
|
2321
|
+
}
|
2322
|
+
return dialog;
|
2323
|
+
}
|
2324
|
+
|
2325
|
+
/**
|
2326
|
+
* Helper: registers a new dialog definition.
|
2327
|
+
*
|
2328
|
+
* @name {String} name The dialog name.
|
2329
|
+
* @Factory {Function} Factory a function resposible for creating dialog prototype.
|
2330
|
+
* @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
|
2331
|
+
* @base {String} base the name of another dialog to inherit from.
|
2332
|
+
*
|
2333
|
+
* @return {Object} The dialog definition.
|
2334
|
+
*/
|
2335
|
+
function register(name, Factory, transient, base) {
|
2336
|
+
var definition = {
|
2337
|
+
dialog: null,
|
2338
|
+
factory: Factory
|
2339
|
+
};
|
2340
|
+
|
2341
|
+
//if this is based on an existing dialog, create a new definition
|
2342
|
+
//by applying the new protoype over the existing one.
|
2343
|
+
if (base !== undefined) {
|
2344
|
+
definition.factory = function () {
|
2345
|
+
return extend(new dialogs[base].factory(), new Factory());
|
2346
|
+
};
|
2347
|
+
}
|
2348
|
+
|
2349
|
+
if (!transient) {
|
2350
|
+
//create a new definition based on dialog
|
2351
|
+
definition.dialog = extend(new definition.factory(), dialog);
|
2352
|
+
}
|
2353
|
+
return dialogs[name] = definition;
|
2354
|
+
}
|
2355
|
+
|
2356
|
+
return {
|
2357
|
+
/**
|
2358
|
+
* Alertify defaults
|
2359
|
+
*
|
2360
|
+
* @type {Object}
|
2361
|
+
*/
|
2362
|
+
defaults: defaults,
|
2363
|
+
/**
|
2364
|
+
* Dialogs factory
|
2365
|
+
*
|
2366
|
+
* @param {string} Dialog name.
|
2367
|
+
* @param {Function} A Dialog factory function.
|
2368
|
+
* @param {Boolean} indicates whether to create a singleton or transient dialog.
|
2369
|
+
* @type {Object}
|
2370
|
+
*/
|
2371
|
+
dialog: function (name, Factory, transient, base) {
|
2372
|
+
|
2373
|
+
// get request, create a new instance and return it.
|
2374
|
+
if (typeof Factory !== 'function') {
|
2375
|
+
return get_dialog(name);
|
2376
|
+
}
|
2377
|
+
|
2378
|
+
if (this.hasOwnProperty(name)) {
|
2379
|
+
throw new Error('alertify.dialog: name already exists');
|
2380
|
+
}
|
2381
|
+
|
2382
|
+
// register the dialog
|
2383
|
+
var definition = register(name, Factory, transient, base);
|
2384
|
+
|
2385
|
+
if (transient) {
|
2386
|
+
|
2387
|
+
// make it public
|
2388
|
+
this[name] = function () {
|
2389
|
+
//if passed with no params, consider it a get request
|
2390
|
+
if (arguments.length === 0) {
|
2391
|
+
return definition.dialog;
|
2392
|
+
} else {
|
2393
|
+
var instance = extend(new definition.factory(), dialog);
|
2394
|
+
//ensure init
|
2395
|
+
if (instance && typeof instance.__init === 'function') {
|
2396
|
+
instance.__init(instance);
|
2397
|
+
}
|
2398
|
+
instance['main'].apply(instance, arguments);
|
2399
|
+
return instance['show'].apply(instance);
|
2400
|
+
}
|
2401
|
+
};
|
2402
|
+
} else {
|
2403
|
+
// make it public
|
2404
|
+
this[name] = function () {
|
2405
|
+
//ensure init
|
2406
|
+
if (definition.dialog && typeof definition.dialog.__init === 'function') {
|
2407
|
+
definition.dialog.__init(definition.dialog);
|
2408
|
+
}
|
2409
|
+
//if passed with no params, consider it a get request
|
2410
|
+
if (arguments.length === 0) {
|
2411
|
+
return definition.dialog;
|
2412
|
+
} else {
|
2413
|
+
var dialog = definition.dialog;
|
2414
|
+
dialog['main'].apply(definition.dialog, arguments);
|
2415
|
+
return dialog['show'].apply(definition.dialog);
|
2416
|
+
}
|
2417
|
+
};
|
2418
|
+
}
|
2419
|
+
},
|
2420
|
+
/**
|
2421
|
+
* Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
|
2422
|
+
*
|
2423
|
+
* @param {string} name The dialog name.
|
2424
|
+
* @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
|
2425
|
+
* @param {Variant} value Optional, the value associated with the key (in case it was a string).
|
2426
|
+
*
|
2427
|
+
* @return {undefined}
|
2428
|
+
*/
|
2429
|
+
setting: function (name, key, value) {
|
2430
|
+
|
2431
|
+
if (name === 'notifier') {
|
2432
|
+
return notifier.setting(key, value);
|
2433
|
+
}
|
2434
|
+
|
2435
|
+
var dialog = get_dialog(name);
|
2436
|
+
if (dialog) {
|
2437
|
+
return dialog.setting(key, value);
|
2438
|
+
}
|
2439
|
+
},
|
2440
|
+
/**
|
2441
|
+
* Creates a new notification message.
|
2442
|
+
* If a type is passed, a class name "ajs-{type}" will be added.
|
2443
|
+
* This allows for custom look and feel for various types of notifications.
|
2444
|
+
*
|
2445
|
+
* @param {String} [message=undefined] Message text
|
2446
|
+
* @param {String} [type=''] Type of log message
|
2447
|
+
* @param {String} [value=''] Time (in ms) to wait before auto-close
|
2448
|
+
* @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
|
2449
|
+
*
|
2450
|
+
* @return {undefined}
|
2451
|
+
*/
|
2452
|
+
notify: function (message, type, wait, callback) {
|
2453
|
+
return notifier.create(type, callback).push(message, wait);
|
2454
|
+
},
|
2455
|
+
/**
|
2456
|
+
* Creates a new notification message.
|
2457
|
+
*
|
2458
|
+
* @param {String} [message=undefined] Message text
|
2459
|
+
* @param {String} [type=''] Type of log message
|
2460
|
+
* @param {String} [value=''] Time (in ms) to wait before auto-close
|
2461
|
+
* @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
|
2462
|
+
*
|
2463
|
+
* @return {undefined}
|
2464
|
+
*/
|
2465
|
+
message: function (message, wait, callback) {
|
2466
|
+
return notifier.create(null, callback).push(message, wait);
|
2467
|
+
},
|
2468
|
+
/**
|
2469
|
+
* Creates a new notification message of type 'success'.
|
2470
|
+
*
|
2471
|
+
* @param {String} [message=undefined] Message text
|
2472
|
+
* @param {String} [type=''] Type of log message
|
2473
|
+
* @param {String} [value=''] Time (in ms) to wait before auto-close
|
2474
|
+
* @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
|
2475
|
+
*
|
2476
|
+
* @return {undefined}
|
2477
|
+
*/
|
2478
|
+
success: function (message, wait, callback) {
|
2479
|
+
return notifier.create('success', callback).push(message, wait);
|
2480
|
+
},
|
2481
|
+
/**
|
2482
|
+
* Creates a new notification message of type 'error'.
|
2483
|
+
*
|
2484
|
+
* @param {String} [message=undefined] Message text
|
2485
|
+
* @param {String} [type=''] Type of log message
|
2486
|
+
* @param {String} [value=''] Time (in ms) to wait before auto-close
|
2487
|
+
* @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
|
2488
|
+
*
|
2489
|
+
* @return {undefined}
|
2490
|
+
*/
|
2491
|
+
error: function (message, wait, callback) {
|
2492
|
+
return notifier.create('error', callback).push(message, wait);
|
2493
|
+
},
|
2494
|
+
/**
|
2495
|
+
* Creates a new notification message of type 'warning'.
|
2496
|
+
*
|
2497
|
+
* @param {String} [message=undefined] Message text
|
2498
|
+
* @param {String} [type=''] Type of log message
|
2499
|
+
* @param {String} [value=''] Time (in ms) to wait before auto-close
|
2500
|
+
* @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
|
2501
|
+
*
|
2502
|
+
* @return {undefined}
|
2503
|
+
*/
|
2504
|
+
warning: function (message, wait, callback) {
|
2505
|
+
return notifier.create('warning', callback).push(message, wait);
|
2506
|
+
}
|
2507
|
+
|
2508
|
+
};
|
2509
|
+
}
|
2510
|
+
var alertify = new Alertify();
|
2511
|
+
|
2512
|
+
/**
|
2513
|
+
* Alert dialog definition
|
2514
|
+
*
|
2515
|
+
* invoked by:
|
2516
|
+
* alertify.alert(message);
|
2517
|
+
* alertify.alert(title, message);
|
2518
|
+
* alertify.alert(message, onok);
|
2519
|
+
* alertify.alert(title, message, onok);
|
2520
|
+
*/
|
2521
|
+
alertify.dialog('alert', function () {
|
2522
|
+
return {
|
2523
|
+
main: function (_title, _message, _onok) {
|
2524
|
+
var title, message, onok;
|
2525
|
+
switch (arguments.length) {
|
2526
|
+
case 1:
|
2527
|
+
message = _title;
|
2528
|
+
break;
|
2529
|
+
case 2:
|
2530
|
+
if (typeof _message === 'function') {
|
2531
|
+
message = _title;
|
2532
|
+
onok = _message;
|
2533
|
+
} else {
|
2534
|
+
title = _title;
|
2535
|
+
message = _message;
|
2536
|
+
}
|
2537
|
+
break;
|
2538
|
+
case 3:
|
2539
|
+
title = _title;
|
2540
|
+
message = _message;
|
2541
|
+
onok = _onok;
|
2542
|
+
break;
|
2543
|
+
}
|
2544
|
+
this.setting('title', title);
|
2545
|
+
this.setting('message', message);
|
2546
|
+
this.setting('onok', onok);
|
2547
|
+
return this;
|
2548
|
+
},
|
2549
|
+
setup: function () {
|
2550
|
+
return {
|
2551
|
+
buttons: [
|
2552
|
+
{
|
2553
|
+
text: alertify.defaults.glossary.ok,
|
2554
|
+
key: keys.ESC,
|
2555
|
+
invokeOnClose: true,
|
2556
|
+
className: alertify.defaults.theme.ok,
|
2557
|
+
}
|
2558
|
+
],
|
2559
|
+
focus: {
|
2560
|
+
element: 0,
|
2561
|
+
select: false
|
2562
|
+
},
|
2563
|
+
options: {
|
2564
|
+
maximizable: false,
|
2565
|
+
resizable: false
|
2566
|
+
}
|
2567
|
+
};
|
2568
|
+
},
|
2569
|
+
build: function () {
|
2570
|
+
// nothing
|
2571
|
+
},
|
2572
|
+
prepare: function () {
|
2573
|
+
//nothing
|
2574
|
+
},
|
2575
|
+
setMessage: function (message) {
|
2576
|
+
this.setContent(message);
|
2577
|
+
},
|
2578
|
+
settings: {
|
2579
|
+
message: undefined,
|
2580
|
+
onok: undefined,
|
2581
|
+
label: undefined,
|
2582
|
+
},
|
2583
|
+
settingUpdated: function (key, oldValue, newValue) {
|
2584
|
+
switch (key) {
|
2585
|
+
case 'message':
|
2586
|
+
this.setMessage(newValue);
|
2587
|
+
break;
|
2588
|
+
case 'label':
|
2589
|
+
if (this.__internal.buttons[0].element) {
|
2590
|
+
this.__internal.buttons[0].element.innerHTML = newValue;
|
2591
|
+
}
|
2592
|
+
break;
|
2593
|
+
}
|
2594
|
+
},
|
2595
|
+
callback: function (closeEvent) {
|
2596
|
+
if (typeof this.settings.onok === 'function') {
|
2597
|
+
var returnValue = this.settings.onok.call(undefined, closeEvent);
|
2598
|
+
if (typeof returnValue !== 'undefined') {
|
2599
|
+
closeEvent.cancel = !returnValue;
|
2600
|
+
}
|
2601
|
+
}
|
2602
|
+
}
|
2603
|
+
};
|
2604
|
+
});
|
2605
|
+
/**
|
2606
|
+
* Confirm dialog object
|
2607
|
+
*
|
2608
|
+
* alertify.confirm(message);
|
2609
|
+
* alertify.confirm(message, onok);
|
2610
|
+
* alertify.confirm(message, onok, oncancel);
|
2611
|
+
* alertify.confirm(title, message, onok, oncancel);
|
2612
|
+
*/
|
2613
|
+
alertify.dialog('confirm', function () {
|
2614
|
+
|
2615
|
+
var autoConfirm = {
|
2616
|
+
timer: null,
|
2617
|
+
index: null,
|
2618
|
+
text: null,
|
2619
|
+
duratuin: null,
|
2620
|
+
task: function (event, self) {
|
2621
|
+
if (self.isOpen()) {
|
2622
|
+
self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (‏' + autoConfirm.duration + '‏) ';
|
2623
|
+
autoConfirm.duration -= 1;
|
2624
|
+
if (autoConfirm.duration === -1) {
|
2625
|
+
clearAutoConfirm(self);
|
2626
|
+
var button = self.__internal.buttons[autoConfirm.index];
|
2627
|
+
var closeEvent = createCloseEvent(autoConfirm.index, button);
|
2628
|
+
|
2629
|
+
if (typeof self.callback === 'function') {
|
2630
|
+
self.callback.apply(self, [closeEvent]);
|
2631
|
+
}
|
2632
|
+
//close the dialog.
|
2633
|
+
if (closeEvent.close !== false) {
|
2634
|
+
self.close();
|
2635
|
+
}
|
2636
|
+
}
|
2637
|
+
} else {
|
2638
|
+
clearAutoConfirm(self);
|
2639
|
+
}
|
2640
|
+
}
|
2641
|
+
};
|
2642
|
+
|
2643
|
+
function clearAutoConfirm(self) {
|
2644
|
+
if (autoConfirm.timer !== null) {
|
2645
|
+
clearInterval(autoConfirm.timer);
|
2646
|
+
autoConfirm.timer = null;
|
2647
|
+
self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
|
2648
|
+
}
|
2649
|
+
}
|
2650
|
+
|
2651
|
+
function startAutoConfirm(self, index, duration) {
|
2652
|
+
clearAutoConfirm(self);
|
2653
|
+
autoConfirm.duration = duration;
|
2654
|
+
autoConfirm.index = index;
|
2655
|
+
autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
|
2656
|
+
autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
|
2657
|
+
autoConfirm.task(null, self);
|
2658
|
+
}
|
2659
|
+
|
2660
|
+
|
2661
|
+
return {
|
2662
|
+
main: function (_title, _message, _onok, _oncancel) {
|
2663
|
+
var title, message, onok, oncancel;
|
2664
|
+
switch (arguments.length) {
|
2665
|
+
case 1:
|
2666
|
+
message = _title;
|
2667
|
+
break;
|
2668
|
+
case 2:
|
2669
|
+
message = _title;
|
2670
|
+
onok = _message;
|
2671
|
+
break;
|
2672
|
+
case 3:
|
2673
|
+
message = _title;
|
2674
|
+
onok = _message;
|
2675
|
+
oncancel = _onok;
|
2676
|
+
break;
|
2677
|
+
case 4:
|
2678
|
+
title = _title;
|
2679
|
+
message = _message;
|
2680
|
+
onok = _onok;
|
2681
|
+
oncancel = _oncancel;
|
2682
|
+
break;
|
2683
|
+
}
|
2684
|
+
this.setting('title', title);
|
2685
|
+
this.setting('message', message);
|
2686
|
+
this.setting('onok', onok);
|
2687
|
+
this.setting('oncancel', oncancel);
|
2688
|
+
return this;
|
2689
|
+
},
|
2690
|
+
setup: function () {
|
2691
|
+
return {
|
2692
|
+
buttons: [
|
2693
|
+
{
|
2694
|
+
text: alertify.defaults.glossary.ok,
|
2695
|
+
key: keys.ENTER,
|
2696
|
+
className: alertify.defaults.theme.ok,
|
2697
|
+
},
|
2698
|
+
{
|
2699
|
+
text: alertify.defaults.glossary.cancel,
|
2700
|
+
key: keys.ESC,
|
2701
|
+
invokeOnClose: true,
|
2702
|
+
className: alertify.defaults.theme.cancel,
|
2703
|
+
}
|
2704
|
+
],
|
2705
|
+
focus: {
|
2706
|
+
element: 0,
|
2707
|
+
select: false
|
2708
|
+
},
|
2709
|
+
options: {
|
2710
|
+
maximizable: false,
|
2711
|
+
resizable: false
|
2712
|
+
}
|
2713
|
+
};
|
2714
|
+
},
|
2715
|
+
build: function () {
|
2716
|
+
//nothing
|
2717
|
+
},
|
2718
|
+
prepare: function () {
|
2719
|
+
//nothing
|
2720
|
+
},
|
2721
|
+
setMessage: function (message) {
|
2722
|
+
this.setContent(message);
|
2723
|
+
},
|
2724
|
+
settings: {
|
2725
|
+
message: null,
|
2726
|
+
labels: null,
|
2727
|
+
onok: null,
|
2728
|
+
oncancel: null,
|
2729
|
+
defaultFocus: null,
|
2730
|
+
reverseButtons: null,
|
2731
|
+
},
|
2732
|
+
settingUpdated: function (key, oldValue, newValue) {
|
2733
|
+
switch (key) {
|
2734
|
+
case 'message':
|
2735
|
+
this.setMessage(newValue);
|
2736
|
+
break;
|
2737
|
+
case 'labels':
|
2738
|
+
if ('ok' in newValue && this.__internal.buttons[0].element) {
|
2739
|
+
this.__internal.buttons[0].text = newValue.ok;
|
2740
|
+
this.__internal.buttons[0].element.innerHTML = newValue.ok;
|
2741
|
+
}
|
2742
|
+
if ('cancel' in newValue && this.__internal.buttons[1].element) {
|
2743
|
+
this.__internal.buttons[1].text = newValue.cancel;
|
2744
|
+
this.__internal.buttons[1].element.innerHTML = newValue.cancel;
|
2745
|
+
}
|
2746
|
+
break;
|
2747
|
+
case 'reverseButtons':
|
2748
|
+
if (newValue === true) {
|
2749
|
+
this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
|
2750
|
+
} else {
|
2751
|
+
this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
|
2752
|
+
}
|
2753
|
+
break;
|
2754
|
+
case 'defaultFocus':
|
2755
|
+
this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
|
2756
|
+
break;
|
2757
|
+
}
|
2758
|
+
},
|
2759
|
+
callback: function (closeEvent) {
|
2760
|
+
clearAutoConfirm(this);
|
2761
|
+
var returnValue;
|
2762
|
+
switch (closeEvent.index) {
|
2763
|
+
case 0:
|
2764
|
+
if (typeof this.settings.onok === 'function') {
|
2765
|
+
returnValue = this.settings.onok.call(undefined, closeEvent);
|
2766
|
+
if (typeof returnValue !== 'undefined') {
|
2767
|
+
closeEvent.cancel = !returnValue;
|
2768
|
+
}
|
2769
|
+
}
|
2770
|
+
break;
|
2771
|
+
case 1:
|
2772
|
+
if (typeof this.settings.oncancel === 'function') {
|
2773
|
+
returnValue = this.settings.oncancel.call(undefined, closeEvent);
|
2774
|
+
if (typeof returnValue !== 'undefined') {
|
2775
|
+
closeEvent.cancel = !returnValue;
|
2776
|
+
}
|
2777
|
+
}
|
2778
|
+
break;
|
2779
|
+
}
|
2780
|
+
},
|
2781
|
+
autoOk: function (duration) {
|
2782
|
+
startAutoConfirm(this, 0, duration);
|
2783
|
+
return this;
|
2784
|
+
},
|
2785
|
+
autoCancel: function (duration) {
|
2786
|
+
startAutoConfirm(this, 1, duration);
|
2787
|
+
return this;
|
2788
|
+
}
|
2789
|
+
};
|
2790
|
+
});
|
2791
|
+
/**
|
2792
|
+
* Prompt dialog object
|
2793
|
+
*
|
2794
|
+
* invoked by:
|
2795
|
+
* alertify.prompt(message);
|
2796
|
+
* alertify.prompt(message, value);
|
2797
|
+
* alertify.prompt(message, value, onok);
|
2798
|
+
* alertify.prompt(message, value, onok, oncancel);
|
2799
|
+
* alertify.prompt(title, message, value, onok, oncancel);
|
2800
|
+
*/
|
2801
|
+
alertify.dialog('prompt', function () {
|
2802
|
+
var input = document.createElement('INPUT');
|
2803
|
+
var p = document.createElement('P');
|
2804
|
+
return {
|
2805
|
+
main: function (_title, _message, _value, _onok, _oncancel) {
|
2806
|
+
var title, message, value, onok, oncancel;
|
2807
|
+
switch (arguments.length) {
|
2808
|
+
case 1:
|
2809
|
+
message = _title;
|
2810
|
+
break;
|
2811
|
+
case 2:
|
2812
|
+
message = _title;
|
2813
|
+
value = _message;
|
2814
|
+
break;
|
2815
|
+
case 3:
|
2816
|
+
message = _title;
|
2817
|
+
value = _message;
|
2818
|
+
onok = _value;
|
2819
|
+
break;
|
2820
|
+
case 4:
|
2821
|
+
message = _title;
|
2822
|
+
value = _message;
|
2823
|
+
onok = _value;
|
2824
|
+
oncancel = _onok;
|
2825
|
+
break;
|
2826
|
+
case 4:
|
2827
|
+
title = _title;
|
2828
|
+
message = _message;
|
2829
|
+
value = _value;
|
2830
|
+
onok = _onok;
|
2831
|
+
oncancel = _oncancel;
|
2832
|
+
break;
|
2833
|
+
}
|
2834
|
+
this.setting('title', title);
|
2835
|
+
this.setting('message', message);
|
2836
|
+
this.setting('value', value);
|
2837
|
+
this.setting('onok', onok);
|
2838
|
+
this.setting('oncancel', oncancel);
|
2839
|
+
return this;
|
2840
|
+
},
|
2841
|
+
setup: function () {
|
2842
|
+
return {
|
2843
|
+
buttons: [
|
2844
|
+
{
|
2845
|
+
text: alertify.defaults.glossary.ok,
|
2846
|
+
key: keys.ENTER,
|
2847
|
+
className: alertify.defaults.theme.ok,
|
2848
|
+
},
|
2849
|
+
{
|
2850
|
+
text: alertify.defaults.glossary.cancel,
|
2851
|
+
key: keys.ESC,
|
2852
|
+
invokeOnClose: true,
|
2853
|
+
className: alertify.defaults.theme.cancel,
|
2854
|
+
}
|
2855
|
+
],
|
2856
|
+
focus: {
|
2857
|
+
element: input,
|
2858
|
+
select: true
|
2859
|
+
},
|
2860
|
+
options: {
|
2861
|
+
maximizable: false,
|
2862
|
+
resizable: false
|
2863
|
+
}
|
2864
|
+
};
|
2865
|
+
},
|
2866
|
+
build: function () {
|
2867
|
+
input.className = alertify.defaults.theme.input;
|
2868
|
+
input.setAttribute('type', 'text');
|
2869
|
+
input.value = this.settings.value;
|
2870
|
+
this.elements.content.appendChild(p);
|
2871
|
+
this.elements.content.appendChild(input);
|
2872
|
+
},
|
2873
|
+
prepare: function () {
|
2874
|
+
//nothing
|
2875
|
+
},
|
2876
|
+
setMessage: function (message) {
|
2877
|
+
if (typeof message === 'string') {
|
2878
|
+
p.innerHTML = message;
|
2879
|
+
} else if (message instanceof window.HTMLElement && p.firstChild !== message) {
|
2880
|
+
p.innerHTML = '';
|
2881
|
+
p.appendChild(message);
|
2882
|
+
}
|
2883
|
+
},
|
2884
|
+
settings: {
|
2885
|
+
message: undefined,
|
2886
|
+
labels: undefined,
|
2887
|
+
onok: undefined,
|
2888
|
+
oncancel: undefined,
|
2889
|
+
value: '',
|
2890
|
+
reverseButtons: undefined,
|
2891
|
+
},
|
2892
|
+
settingUpdated: function (key, oldValue, newValue) {
|
2893
|
+
switch (key) {
|
2894
|
+
case 'message':
|
2895
|
+
this.setMessage(newValue);
|
2896
|
+
break;
|
2897
|
+
case 'value':
|
2898
|
+
input.value = newValue;
|
2899
|
+
break;
|
2900
|
+
case 'labels':
|
2901
|
+
if (newValue.ok && this.__internal.buttons[0].element) {
|
2902
|
+
this.__internal.buttons[0].element.innerHTML = newValue.ok;
|
2903
|
+
}
|
2904
|
+
if (newValue.cancel && this.__internal.buttons[1].element) {
|
2905
|
+
this.__internal.buttons[1].element.innerHTML = newValue.cancel;
|
2906
|
+
}
|
2907
|
+
break;
|
2908
|
+
case 'reverseButtons':
|
2909
|
+
if (newValue === true) {
|
2910
|
+
this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
|
2911
|
+
} else {
|
2912
|
+
this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
|
2913
|
+
}
|
2914
|
+
break;
|
2915
|
+
}
|
2916
|
+
},
|
2917
|
+
callback: function (closeEvent) {
|
2918
|
+
var returnValue;
|
2919
|
+
switch (closeEvent.index) {
|
2920
|
+
case 0:
|
2921
|
+
this.value = input.value;
|
2922
|
+
if (typeof this.settings.onok === 'function') {
|
2923
|
+
returnValue = this.settings.onok.call(undefined, closeEvent, this.value);
|
2924
|
+
if (typeof returnValue !== 'undefined') {
|
2925
|
+
closeEvent.cancel = !returnValue;
|
2926
|
+
}
|
2927
|
+
}
|
2928
|
+
break;
|
2929
|
+
case 1:
|
2930
|
+
if (typeof this.settings.oncancel === 'function') {
|
2931
|
+
returnValue = this.settings.oncancel.call(undefined, closeEvent);
|
2932
|
+
if (typeof returnValue !== 'undefined') {
|
2933
|
+
closeEvent.cancel = !returnValue;
|
2934
|
+
}
|
2935
|
+
}
|
2936
|
+
break;
|
2937
|
+
}
|
2938
|
+
}
|
2939
|
+
};
|
2940
|
+
});
|
2941
|
+
// AMD and window support
|
2942
|
+
if ( typeof define === 'function' ) {
|
2943
|
+
define( [], function () {
|
2944
|
+
return alertify;
|
2945
|
+
} );
|
2946
|
+
} else if ( !window.alertify ) {
|
2947
|
+
window.alertify = alertify;
|
2948
|
+
}
|
2949
|
+
|
2950
|
+
} ( this ) );
|