UNPKG

fuelux

Version:

Base Fuel UX styles and controls

296 lines (236 loc) 7.67 kB
/* 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 --