alertifyjs-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ # Specify your gem's dependencies in alertifyjs-rails.gemspec
3
+ gemspec
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,2 @@
1
+ require 'alertifyjs/rails'
2
+
@@ -0,0 +1,9 @@
1
+ require 'alertifyjs/rails/engine' if ::Rails.version >= '3.1'
2
+ require 'alertifyjs/rails/railtie'
3
+ require 'alertifyjs/rails/version'
4
+
5
+ module Alertifyjs #:nodoc:
6
+ module Rails #:nodoc:
7
+ end
8
+ end
9
+
@@ -0,0 +1,6 @@
1
+ module Alertifyjs #:nodoc:
2
+ module Rails #:nodoc:
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ 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,6 @@
1
+ module Alertifyjs #:nodoc:
2
+ module Rails #:nodoc:
3
+ VERSION = "0.1.0"
4
+ ALERTIFY_VERSION = "0.4.0"
5
+ end
6
+ 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 + ' (&#8207;' + autoConfirm.duration + '&#8207;) ';
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 ) );