placard.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. * Fuel UX Placard
  3. * https://github.com/ExactTarget/fuelux
  4. *
  5. * Copyright (c) 2014 ExactTarget
  6. * Licensed under the BSD New license.
  7. */
  8. // -- BEGIN UMD WRAPPER PREFACE --
  9. // For more information on UMD visit:
  10. // https://github.com/umdjs/umd/blob/master/jqueryPlugin.js
  11. (function (factory) {
  12. if (typeof define === 'function' && define.amd) {
  13. // if AMD loader is available, register as an anonymous module.
  14. define(['jquery'], factory);
  15. } else {
  16. // OR use browser globals if AMD is not present
  17. factory(jQuery);
  18. }
  19. }(function ($) {
  20. // -- END UMD WRAPPER PREFACE --
  21. // -- BEGIN MODULE CODE HERE --
  22. var old = $.fn.placard;
  23. // PLACARD CONSTRUCTOR AND PROTOTYPE
  24. var Placard = function (element, options) {
  25. var self = this;
  26. this.$element = $(element);
  27. this.options = $.extend({}, $.fn.placard.defaults, options);
  28. this.$accept = this.$element.find('.placard-accept');
  29. this.$cancel = this.$element.find('.placard-cancel');
  30. this.$field = this.$element.find('.placard-field');
  31. this.$footer = this.$element.find('.placard-footer');
  32. this.$header = this.$element.find('.placard-header');
  33. this.$popup = this.$element.find('.placard-popup');
  34. this.actualValue = null;
  35. this.clickStamp = '_';
  36. this.previousValue = '';
  37. if (this.options.revertOnCancel === -1) {
  38. this.options.revertOnCancel = (this.$accept.length > 0) ? true : false;
  39. }
  40. this.isInput = this.$field.is('input');
  41. this.$field.on('focus.fu.placard', $.proxy(this.show, this));
  42. this.$field.on('keydown.fu.placard', $.proxy(this.keyComplete, this));
  43. this.$accept.on('click.fu.placard', $.proxy(this.complete, this, 'accept'));
  44. this.$cancel.on('click.fu.placard', function (e) {
  45. e.preventDefault(); self.complete('cancel');
  46. });
  47. this.ellipsis();
  48. };
  49. Placard.prototype = {
  50. constructor: Placard,
  51. complete: function (action) {
  52. var func = this.options['on' + action[0].toUpperCase() + action.substring(1)];
  53. var obj = {
  54. previousValue: this.previousValue,
  55. value: this.$field.val()
  56. };
  57. if (func) {
  58. func(obj);
  59. this.$element.trigger(action, obj);
  60. } else {
  61. if (action === 'cancel' && this.options.revertOnCancel) {
  62. this.$field.val(this.previousValue);
  63. }
  64. this.$element.trigger(action, obj);
  65. this.hide();
  66. }
  67. },
  68. keyComplete: function (e) {
  69. if (this.isInput && e.keyCode === 13) {
  70. this.complete('accept');
  71. this.$field.blur();
  72. } else if (e.keyCode === 27) {
  73. this.complete('cancel');
  74. this.$field.blur();
  75. }
  76. },
  77. destroy: function () {
  78. this.$element.remove();
  79. // remove any external bindings
  80. $(document).off('click.fu.placard.externalClick.' + this.clickStamp);
  81. // set input value attrbute
  82. this.$element.find('input').each(function () {
  83. $(this).attr('value', $(this).val());
  84. });
  85. // empty elements to return to original markup
  86. // [none]
  87. // return string of markup
  88. return this.$element[0].outerHTML;
  89. },
  90. disable: function () {
  91. this.$element.addClass('disabled');
  92. this.$field.attr('disabled', 'disabled');
  93. this.hide();
  94. },
  95. ellipsis: function () {
  96. var field, i, str;
  97. if (this.$element.attr('data-ellipsis') === 'true') {
  98. field = this.$field.get(0);
  99. if (this.$field.is('input')) {
  100. field.scrollLeft = 0;
  101. } else {
  102. field.scrollTop = 0;
  103. if (field.clientHeight < field.scrollHeight) {
  104. this.actualValue = this.$field.val();
  105. this.$field.val('');
  106. str = '';
  107. i = 0;
  108. while (field.clientHeight >= field.scrollHeight) {
  109. str += this.actualValue[i];
  110. this.$field.val(str + '...');
  111. i++;
  112. }
  113. str = (str.length > 0) ? str.substring(0, str.length - 1) : '';
  114. this.$field.val(str + '...');
  115. }
  116. }
  117. }
  118. },
  119. enable: function () {
  120. this.$element.removeClass('disabled');
  121. this.$field.removeAttr('disabled');
  122. },
  123. externalClickListener: function (e, force) {
  124. if (force === true || this.isExternalClick(e)) {
  125. this.complete(this.options.externalClickAction);
  126. }
  127. },
  128. getValue: function () {
  129. if (this.actualValue !== null) {
  130. return this.actualValue;
  131. } else {
  132. return this.$field.val();
  133. }
  134. },
  135. hide: function () {
  136. if (!this.$element.hasClass('showing')) {
  137. return;
  138. }
  139. this.$element.removeClass('showing');
  140. this.ellipsis();
  141. $(document).off('click.fu.placard.externalClick.' + this.clickStamp);
  142. this.$element.trigger('hidden.fu.placard');
  143. },
  144. isExternalClick: function (e) {
  145. var el = this.$element.get(0);
  146. var exceptions = this.options.externalClickExceptions || [];
  147. var $originEl = $(e.target);
  148. var i, l;
  149. if (e.target === el || $originEl.parents('.placard:first').get(0) === el) {
  150. return false;
  151. } else {
  152. for (i = 0, l = exceptions.length; i < l; i++) {
  153. if ($originEl.is(exceptions[i]) || $originEl.parents(exceptions[i]).length > 0) {
  154. return false;
  155. }
  156. }
  157. }
  158. return true;
  159. },
  160. setValue: function (val) {
  161. this.$field.val(val);
  162. if (!this.$element.hasClass('showing')) {
  163. this.ellipsis();
  164. }
  165. },
  166. show: function () {
  167. var other;
  168. if (this.$element.hasClass('showing')) {
  169. return;
  170. }
  171. other = $(document).find('.placard.showing');
  172. if (other.length > 0) {
  173. if (other.data('fu.placard') && other.data('fu.placard').options.explicit) {
  174. return;
  175. }
  176. other.placard('externalClickListener', {}, true);
  177. }
  178. this.previousValue = this.$field.val();
  179. this.$element.addClass('showing');
  180. if (this.actualValue !== null) {
  181. this.$field.val(this.actualValue);
  182. this.actualValue = null;
  183. }
  184. if (this.$header.length > 0) {
  185. this.$popup.css('top', '-' + this.$header.outerHeight(true) + 'px');
  186. }
  187. if (this.$footer.length > 0) {
  188. this.$popup.css('bottom', '-' + this.$footer.outerHeight(true) + 'px');
  189. }
  190. this.$element.trigger('shown.fu.placard');
  191. this.clickStamp = new Date().getTime() + (Math.floor(Math.random() * 100) + 1);
  192. if (!this.options.explicit) {
  193. $(document).on('click.fu.placard.externalClick.' + this.clickStamp, $.proxy(this.externalClickListener, this));
  194. }
  195. }
  196. };
  197. // PLACARD PLUGIN DEFINITION
  198. $.fn.placard = function (option) {
  199. var args = Array.prototype.slice.call(arguments, 1);
  200. var methodReturn;
  201. var $set = this.each(function () {
  202. var $this = $(this);
  203. var data = $this.data('fu.placard');
  204. var options = typeof option === 'object' && option;
  205. if (!data) {
  206. $this.data('fu.placard', (data = new Placard(this, options)));
  207. }
  208. if (typeof option === 'string') {
  209. methodReturn = data[option].apply(data, args);
  210. }
  211. });
  212. return (methodReturn === undefined) ? $set : methodReturn;
  213. };
  214. $.fn.placard.defaults = {
  215. onAccept: undefined,
  216. onCancel: undefined,
  217. externalClickAction: 'cancel',
  218. externalClickExceptions: [],
  219. explicit: false,
  220. revertOnCancel: -1//negative 1 will check for an '.placard-accept' button. Also can be set to true or false
  221. };
  222. $.fn.placard.Constructor = Placard;
  223. $.fn.placard.noConflict = function () {
  224. $.fn.placard = old;
  225. return this;
  226. };
  227. // DATA-API
  228. $(document).on('focus.fu.placard.data-api', '[data-initialize=placard]', function (e) {
  229. var $control = $(e.target).closest('.placard');
  230. if (!$control.data('fu.placard')) {
  231. $control.placard($control.data());
  232. }
  233. });
  234. // Must be domReady for AMD compatibility
  235. $(function () {
  236. $('[data-initialize=placard]').each(function () {
  237. var $this = $(this);
  238. if ($this.data('fu.placard')) return;
  239. $this.placard($this.data());
  240. });
  241. });
  242. // -- BEGIN UMD WRAPPER AFTERWORD --
  243. }));
  244. // -- END UMD WRAPPER AFTERWORD --