alertifyjs-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore 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 ) );