mirror of
https://github.com/ExactTarget/fuelux.git
synced 2026-04-26 03:00:10 -04:00
296 lines
7.5 KiB
JavaScript
296 lines
7.5 KiB
JavaScript
/* global jQuery:true */
|
|
|
|
/*
|
|
* Fuel UX Picker
|
|
* https://github.com/ExactTarget/fuelux
|
|
*
|
|
* Copyright (c) 2014 ExactTarget
|
|
* Licensed under the BSD New license.
|
|
*/
|
|
|
|
// -- BEGIN UMD WRAPPER PREFACE --
|
|
|
|
// For more information on UMD visit:
|
|
// https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
|
|
|
|
(function umdFactory (factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
// if AMD loader is available, register as an anonymous module.
|
|
define(['jquery'], factory);
|
|
} else if (typeof exports === 'object') {
|
|
// Node/CommonJS
|
|
module.exports = factory(require('jquery'));
|
|
} else {
|
|
// OR use browser globals if AMD is not present
|
|
factory(jQuery);
|
|
}
|
|
}(function PickerWrapper ($) {
|
|
// -- END UMD WRAPPER PREFACE --
|
|
|
|
// -- BEGIN MODULE CODE HERE --
|
|
|
|
var old = $.fn.picker;
|
|
|
|
// PLACARD CONSTRUCTOR AND PROTOTYPE
|
|
|
|
var Picker = function Picker(element, options) {
|
|
var self = this;
|
|
this.$element = $(element);
|
|
this.options = $.extend({}, $.fn.picker.defaults, options);
|
|
|
|
this.$accept = this.$element.find('.picker-accept');
|
|
this.$cancel = this.$element.find('.picker-cancel');
|
|
this.$trigger = this.$element.find('.picker-trigger');
|
|
this.$footer = this.$element.find('.picker-footer');
|
|
this.$header = this.$element.find('.picker-header');
|
|
this.$popup = this.$element.find('.picker-popup');
|
|
this.$body = this.$element.find('.picker-body');
|
|
|
|
this.clickStamp = '_';
|
|
|
|
this.isInput = this.$trigger.is('input');
|
|
|
|
this.$trigger.on('keydown.fu.picker', $.proxy(this.keyComplete, this));
|
|
this.$trigger.on('focus.fu.picker', $.proxy(function inputFocus(e){
|
|
if(typeof e === "undefined" || $(e.target).is('input[type=text]')){
|
|
$.proxy(this.show(), this);
|
|
}
|
|
}, this));
|
|
this.$trigger.on('click.fu.picker', $.proxy(function triggerClick(e){
|
|
if(!$(e.target).is('input[type=text]')){
|
|
$.proxy(this.toggle(), this);
|
|
}else{
|
|
$.proxy(this.show(), this);
|
|
}
|
|
}, this));
|
|
this.$accept.on('click.fu.picker', $.proxy(this.complete, this, 'accepted'));
|
|
this.$cancel.on('click.fu.picker', function (e) {
|
|
e.preventDefault(); self.complete('cancelled');
|
|
});
|
|
|
|
|
|
};
|
|
|
|
var _isOffscreen = function _isOffscreen(picker) {
|
|
var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
|
var scrollTop = $(document).scrollTop();
|
|
var popupTop = picker.$popup.offset();
|
|
var popupBottom = popupTop.top + picker.$popup.outerHeight(true);
|
|
|
|
//if the bottom of the popup goes off the page, but the top does not, dropup.
|
|
if (popupBottom > windowHeight + scrollTop || popupTop.top < scrollTop){
|
|
return true;
|
|
}else{//otherwise, prefer showing the top of the popup only vs the bottom
|
|
return false;
|
|
}
|
|
};
|
|
|
|
var _display = function _display(picker) {
|
|
picker.$popup.css('visibility', 'hidden');
|
|
|
|
_showBelow(picker);
|
|
|
|
//if part of the popup is offscreen try to show it above
|
|
if(_isOffscreen(picker)){
|
|
_showAbove(picker);
|
|
|
|
//if part of the popup is still offscreen, prefer cutting off the bottom
|
|
if(_isOffscreen(picker)){
|
|
_showBelow(picker);
|
|
}
|
|
}
|
|
|
|
picker.$popup.css('visibility', 'visible');
|
|
};
|
|
|
|
var _showAbove = function _showAbove(picker) {
|
|
picker.$popup.css('top', - picker.$popup.outerHeight(true) + 'px');
|
|
};
|
|
|
|
var _showBelow = function _showBelow(picker) {
|
|
picker.$popup.css('top', picker.$trigger.outerHeight(true) + 'px');
|
|
};
|
|
|
|
Picker.prototype = {
|
|
constructor: Picker,
|
|
|
|
complete: function complete(action) {
|
|
var EVENT_CALLBACK_MAP = {
|
|
'accepted': 'onAccept',
|
|
'cancelled': 'onCancel',
|
|
'exited': 'onExit'
|
|
};
|
|
var func = this.options[ EVENT_CALLBACK_MAP[action] ];
|
|
|
|
var obj = {
|
|
contents: this.$body
|
|
};
|
|
|
|
if (func) {
|
|
func(obj);
|
|
this.$element.trigger(action + '.fu.picker', obj);
|
|
} else {
|
|
this.$element.trigger(action + '.fu.picker', obj);
|
|
this.hide();
|
|
}
|
|
},
|
|
|
|
keyComplete: function keyComplete(e) {
|
|
if (this.isInput && e.keyCode === 13) {
|
|
this.complete('accepted');
|
|
this.$trigger.blur();
|
|
} else if (e.keyCode === 27) {
|
|
this.complete('exited');
|
|
this.$trigger.blur();
|
|
}
|
|
},
|
|
|
|
destroy: function destroy() {
|
|
this.$element.remove();
|
|
// remove any external bindings
|
|
$(document).off('click.fu.picker.externalClick.' + this.clickStamp);
|
|
// empty elements to return to original markup
|
|
// [none]
|
|
// return string of markup
|
|
return this.$element[0].outerHTML;
|
|
},
|
|
|
|
disable: function disable() {
|
|
this.$element.addClass('disabled');
|
|
this.$trigger.attr('disabled', 'disabled');
|
|
},
|
|
|
|
enable: function enable() {
|
|
this.$element.removeClass('disabled');
|
|
this.$trigger.removeAttr('disabled');
|
|
},
|
|
|
|
toggle: function toggle() {
|
|
if (this.$element.hasClass('showing')) {
|
|
this.hide();
|
|
}else{
|
|
this.show();
|
|
}
|
|
},
|
|
|
|
hide: function hide() {
|
|
if (!this.$element.hasClass('showing')) {
|
|
return;
|
|
}
|
|
|
|
this.$element.removeClass('showing');
|
|
$(document).off('click.fu.picker.externalClick.' + this.clickStamp);
|
|
this.$element.trigger('hidden.fu.picker');
|
|
},
|
|
|
|
externalClickListener: function externalClickListener(e, force) {
|
|
if (force === true || this.isExternalClick(e)) {
|
|
this.complete('exited');
|
|
}
|
|
},
|
|
|
|
isExternalClick: function isExternalClick(e) {
|
|
var el = this.$element.get(0);
|
|
var exceptions = this.options.externalClickExceptions || [];
|
|
var $originEl = $(e.target);
|
|
var i, l;
|
|
|
|
if (e.target === el || $originEl.parents('.picker:first').get(0) === el) {
|
|
return false;
|
|
} else {
|
|
for (i = 0, l = exceptions.length; i < l; i++) {
|
|
if ($originEl.is(exceptions[i]) || $originEl.parents(exceptions[i]).length > 0) {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
show: function show() {
|
|
var other;
|
|
|
|
other = $(document).find('.picker.showing');
|
|
if (other.length > 0) {
|
|
if (other.data('fu.picker') && other.data('fu.picker').options.explicit) {
|
|
return;
|
|
}
|
|
|
|
other.picker('externalClickListener', {}, true);
|
|
}
|
|
|
|
this.$element.addClass('showing');
|
|
|
|
_display(this);
|
|
|
|
this.$element.trigger('shown.fu.picker');
|
|
|
|
this.clickStamp = new Date().getTime() + (Math.floor(Math.random() * 100) + 1);
|
|
if (!this.options.explicit) {
|
|
$(document).on('click.fu.picker.externalClick.' + this.clickStamp, $.proxy(this.externalClickListener, this));
|
|
}
|
|
}
|
|
};
|
|
|
|
// PLACARD PLUGIN DEFINITION
|
|
|
|
$.fn.picker = function picker(option) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
var methodReturn;
|
|
|
|
var $set = this.each(function () {
|
|
var $this = $(this);
|
|
var data = $this.data('fu.picker');
|
|
var options = typeof option === 'object' && option;
|
|
|
|
if (!data) {
|
|
$this.data('fu.picker', (data = new Picker(this, options)));
|
|
}
|
|
|
|
if (typeof option === 'string') {
|
|
methodReturn = data[option].apply(data, args);
|
|
}
|
|
});
|
|
|
|
return (methodReturn === undefined) ? $set : methodReturn;
|
|
};
|
|
|
|
$.fn.picker.defaults = {
|
|
onAccept: undefined,
|
|
onCancel: undefined,
|
|
onExit: undefined,
|
|
externalClickExceptions: [],
|
|
explicit: false
|
|
};
|
|
|
|
$.fn.picker.Constructor = Picker;
|
|
|
|
$.fn.picker.noConflict = function noConflict() {
|
|
$.fn.picker = old;
|
|
return this;
|
|
};
|
|
|
|
// DATA-API
|
|
|
|
$(document).on('focus.fu.picker.data-api', '[data-initialize=picker]', function (e) {
|
|
var $control = $(e.target).closest('.picker');
|
|
if (!$control.data('fu.picker')) {
|
|
$control.picker($control.data());
|
|
}
|
|
});
|
|
|
|
// Must be domReady for AMD compatibility
|
|
$(function () {
|
|
$('[data-initialize=picker]').each(function () {
|
|
var $this = $(this);
|
|
if ($this.data('fu.picker')) return;
|
|
$this.picker($this.data());
|
|
});
|
|
});
|
|
|
|
// -- BEGIN UMD WRAPPER AFTERWORD --
|
|
}));
|
|
// -- END UMD WRAPPER AFTERWORD --
|