PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/files/fuelux/3.9.0/js/fuelux.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 1840 lines | 1326 code | 368 blank | 146 comment | 248 complexity | 76a4d6e8280a33854e54304fcfc5b904 MD5 | raw file
  1. /*!
  2. * Fuel UX v3.9.0
  3. * Copyright 2012-2015 ExactTarget
  4. * Licensed under the BSD-3-Clause license (https://github.com/ExactTarget/fuelux/blob/master/LICENSE)
  5. */
  6. // For more information on UMD visit: https://github.com/umdjs/umd/
  7. ( function( factory ) {
  8. if ( typeof define === 'function' && define.amd ) {
  9. define( [ 'jquery', 'bootstrap' ], factory );
  10. } else {
  11. factory( jQuery );
  12. }
  13. }( function( jQuery ) {
  14. if ( typeof jQuery === 'undefined' ) {
  15. throw new Error( 'Fuel UX\'s JavaScript requires jQuery' )
  16. }
  17. if ( typeof jQuery.fn.dropdown === 'undefined' || typeof jQuery.fn.collapse === 'undefined' ) {
  18. throw new Error( 'Fuel UX\'s JavaScript requires Bootstrap' )
  19. }
  20. ( function( $ ) {
  21. /*
  22. * Fuel UX Checkbox
  23. * https://github.com/ExactTarget/fuelux
  24. *
  25. * Copyright (c) 2014 ExactTarget
  26. * Licensed under the BSD New license.
  27. */
  28. // -- BEGIN MODULE CODE HERE --
  29. var old = $.fn.checkbox;
  30. // CHECKBOX CONSTRUCTOR AND PROTOTYPE
  31. var Checkbox = function( element, options ) {
  32. this.options = $.extend( {}, $.fn.checkbox.defaults, options );
  33. if ( element.tagName.toLowerCase() !== 'label' ) {
  34. //console.log('initialize checkbox on the label that wraps the checkbox');
  35. return;
  36. }
  37. // cache elements
  38. this.$label = $( element );
  39. this.$chk = this.$label.find( 'input[type="checkbox"]' );
  40. this.$container = $( element ).parent( '.checkbox' ); // the container div
  41. // determine if a toggle container is specified
  42. var containerSelector = this.$chk.attr( 'data-toggle' );
  43. this.$toggleContainer = $( containerSelector );
  44. // handle internal events
  45. this.$chk.on( 'change', $.proxy( this.itemchecked, this ) );
  46. // set default state
  47. this.setInitialState();
  48. };
  49. Checkbox.prototype = {
  50. constructor: Checkbox,
  51. setInitialState: function() {
  52. var $chk = this.$chk;
  53. var $lbl = this.$label;
  54. // get current state of input
  55. var checked = $chk.prop( 'checked' );
  56. var disabled = $chk.prop( 'disabled' );
  57. // sync label class with input state
  58. this.setCheckedState( $chk, checked );
  59. this.setDisabledState( $chk, disabled );
  60. },
  61. setCheckedState: function( element, checked ) {
  62. var $chk = element;
  63. var $lbl = this.$label;
  64. var $container = this.$container;
  65. var $containerToggle = this.$toggleContainer;
  66. // set class on outer container too...to support highlighting
  67. // TODO: verify inline checkboxes, also test with MCTheme
  68. if ( checked ) {
  69. $chk.prop( 'checked', true );
  70. $lbl.addClass( 'checked' );
  71. //$container.addClass('checked');
  72. $containerToggle.removeClass( 'hide hidden' );
  73. $lbl.trigger( 'checked.fu.checkbox' );
  74. } else {
  75. $chk.prop( 'checked', false );
  76. $lbl.removeClass( 'checked' );
  77. //$container.removeClass('checked');
  78. $containerToggle.addClass( 'hidden' );
  79. $lbl.trigger( 'unchecked.fu.checkbox' );
  80. }
  81. $lbl.trigger( 'changed.fu.checkbox', checked );
  82. },
  83. setDisabledState: function( element, disabled ) {
  84. var $chk = element;
  85. var $lbl = this.$label;
  86. if ( disabled ) {
  87. this.$chk.prop( 'disabled', true );
  88. $lbl.addClass( 'disabled' );
  89. $lbl.trigger( 'disabled.fu.checkbox' );
  90. } else {
  91. this.$chk.prop( 'disabled', false );
  92. $lbl.removeClass( 'disabled' );
  93. $lbl.trigger( 'enabled.fu.checkbox' );
  94. }
  95. },
  96. itemchecked: function( evt ) {
  97. var $chk = $( evt.target );
  98. var checked = $chk.prop( 'checked' );
  99. this.setCheckedState( $chk, checked );
  100. },
  101. toggle: function() {
  102. var checked = this.isChecked();
  103. if ( checked ) {
  104. this.uncheck();
  105. } else {
  106. this.check();
  107. }
  108. },
  109. check: function() {
  110. this.setCheckedState( this.$chk, true );
  111. },
  112. uncheck: function() {
  113. this.setCheckedState( this.$chk, false );
  114. },
  115. isChecked: function() {
  116. var checked = this.$chk.prop( 'checked' );
  117. return checked;
  118. },
  119. enable: function() {
  120. this.setDisabledState( this.$chk, false );
  121. },
  122. disable: function() {
  123. this.setDisabledState( this.$chk, true );
  124. },
  125. destroy: function() {
  126. this.$label.remove();
  127. // remove any external bindings
  128. // [none]
  129. // empty elements to return to original markup
  130. // [none]
  131. return this.$label[ 0 ].outerHTML;
  132. }
  133. };
  134. // CHECKBOX PLUGIN DEFINITION
  135. $.fn.checkbox = function( option ) {
  136. var args = Array.prototype.slice.call( arguments, 1 );
  137. var methodReturn;
  138. var $set = this.each( function() {
  139. var $this = $( this );
  140. var data = $this.data( 'fu.checkbox' );
  141. var options = typeof option === 'object' && option;
  142. if ( !data ) {
  143. $this.data( 'fu.checkbox', ( data = new Checkbox( this, options ) ) );
  144. }
  145. if ( typeof option === 'string' ) {
  146. methodReturn = data[ option ].apply( data, args );
  147. }
  148. } );
  149. return ( methodReturn === undefined ) ? $set : methodReturn;
  150. };
  151. $.fn.checkbox.defaults = {};
  152. $.fn.checkbox.Constructor = Checkbox;
  153. $.fn.checkbox.noConflict = function() {
  154. $.fn.checkbox = old;
  155. return this;
  156. };
  157. // DATA-API
  158. $( document ).on( 'mouseover.fu.checkbox.data-api', '[data-initialize=checkbox]', function( e ) {
  159. var $control = $( e.target );
  160. if ( !$control.data( 'fu.checkbox' ) ) {
  161. $control.checkbox( $control.data() );
  162. }
  163. } );
  164. // Must be domReady for AMD compatibility
  165. $( function() {
  166. $( '[data-initialize=checkbox]' ).each( function() {
  167. var $this = $( this );
  168. if ( !$this.data( 'fu.checkbox' ) ) {
  169. $this.checkbox( $this.data() );
  170. }
  171. } );
  172. } );
  173. } )( jQuery );
  174. ( function( $ ) {
  175. /*
  176. * Fuel UX Combobox
  177. * https://github.com/ExactTarget/fuelux
  178. *
  179. * Copyright (c) 2014 ExactTarget
  180. * Licensed under the BSD New license.
  181. */
  182. // -- BEGIN MODULE CODE HERE --
  183. var old = $.fn.combobox;
  184. // COMBOBOX CONSTRUCTOR AND PROTOTYPE
  185. var Combobox = function( element, options ) {
  186. this.$element = $( element );
  187. this.options = $.extend( {}, $.fn.combobox.defaults, options );
  188. this.$dropMenu = this.$element.find( '.dropdown-menu' );
  189. this.$input = this.$element.find( 'input' );
  190. this.$button = this.$element.find( '.btn' );
  191. this.$element.on( 'click.fu.combobox', 'a', $.proxy( this.itemclicked, this ) );
  192. this.$element.on( 'change.fu.combobox', 'input', $.proxy( this.inputchanged, this ) );
  193. this.$element.on( 'shown.bs.dropdown', $.proxy( this.menuShown, this ) );
  194. // set default selection
  195. this.setDefaultSelection();
  196. };
  197. Combobox.prototype = {
  198. constructor: Combobox,
  199. destroy: function() {
  200. this.$element.remove();
  201. // remove any external bindings
  202. // [none]
  203. // set input value attrbute in markup
  204. this.$element.find( 'input' ).each( function() {
  205. $( this ).attr( 'value', $( this ).val() );
  206. } );
  207. // empty elements to return to original markup
  208. // [none]
  209. return this.$element[ 0 ].outerHTML;
  210. },
  211. doSelect: function( $item ) {
  212. if ( typeof $item[ 0 ] !== 'undefined' ) {
  213. this.$selectedItem = $item;
  214. this.$input.val( this.$selectedItem.text().trim() );
  215. } else {
  216. this.$selectedItem = null;
  217. }
  218. },
  219. menuShown: function() {
  220. if ( this.options.autoResizeMenu ) {
  221. this.resizeMenu();
  222. }
  223. },
  224. resizeMenu: function() {
  225. var width = this.$element.outerWidth();
  226. this.$dropMenu.outerWidth( width );
  227. },
  228. selectedItem: function() {
  229. var item = this.$selectedItem;
  230. var data = {};
  231. if ( item ) {
  232. var txt = this.$selectedItem.text().trim();
  233. data = $.extend( {
  234. text: txt
  235. }, this.$selectedItem.data() );
  236. } else {
  237. data = {
  238. text: this.$input.val()
  239. };
  240. }
  241. return data;
  242. },
  243. selectByText: function( text ) {
  244. var $item = $( [] );
  245. this.$element.find( 'li' ).each( function() {
  246. if ( ( this.textContent || this.innerText || $( this ).text() || '' ).toLowerCase() === ( text || '' ).toLowerCase() ) {
  247. $item = $( this );
  248. return false;
  249. }
  250. } );
  251. this.doSelect( $item );
  252. },
  253. selectByValue: function( value ) {
  254. var selector = 'li[data-value="' + value + '"]';
  255. this.selectBySelector( selector );
  256. },
  257. selectByIndex: function( index ) {
  258. // zero-based index
  259. var selector = 'li:eq(' + index + ')';
  260. this.selectBySelector( selector );
  261. },
  262. selectBySelector: function( selector ) {
  263. var $item = this.$element.find( selector );
  264. this.doSelect( $item );
  265. },
  266. setDefaultSelection: function() {
  267. var selector = 'li[data-selected=true]:first';
  268. var item = this.$element.find( selector );
  269. if ( item.length > 0 ) {
  270. // select by data-attribute
  271. this.selectBySelector( selector );
  272. item.removeData( 'selected' );
  273. item.removeAttr( 'data-selected' );
  274. }
  275. },
  276. enable: function() {
  277. this.$element.removeClass( 'disabled' );
  278. this.$input.removeAttr( 'disabled' );
  279. this.$button.removeClass( 'disabled' );
  280. },
  281. disable: function() {
  282. this.$element.addClass( 'disabled' );
  283. this.$input.attr( 'disabled', true );
  284. this.$button.addClass( 'disabled' );
  285. },
  286. itemclicked: function( e ) {
  287. this.$selectedItem = $( e.target ).parent();
  288. // set input text and trigger input change event marked as synthetic
  289. this.$input.val( this.$selectedItem.text().trim() ).trigger( 'change', {
  290. synthetic: true
  291. } );
  292. // pass object including text and any data-attributes
  293. // to onchange event
  294. var data = this.selectedItem();
  295. // trigger changed event
  296. this.$element.trigger( 'changed.fu.combobox', data );
  297. e.preventDefault();
  298. // return focus to control after selecting an option
  299. this.$element.find( '.dropdown-toggle' ).focus();
  300. },
  301. inputchanged: function( e, extra ) {
  302. // skip processing for internally-generated synthetic event
  303. // to avoid double processing
  304. if ( extra && extra.synthetic ) return;
  305. var val = $( e.target ).val();
  306. this.selectByText( val );
  307. // find match based on input
  308. // if no match, pass the input value
  309. var data = this.selectedItem();
  310. if ( data.text.length === 0 ) {
  311. data = {
  312. text: val
  313. };
  314. }
  315. // trigger changed event
  316. this.$element.trigger( 'changed.fu.combobox', data );
  317. }
  318. };
  319. // COMBOBOX PLUGIN DEFINITION
  320. $.fn.combobox = function( option ) {
  321. var args = Array.prototype.slice.call( arguments, 1 );
  322. var methodReturn;
  323. var $set = this.each( function() {
  324. var $this = $( this );
  325. var data = $this.data( 'fu.combobox' );
  326. var options = typeof option === 'object' && option;
  327. if ( !data ) {
  328. $this.data( 'fu.combobox', ( data = new Combobox( this, options ) ) );
  329. }
  330. if ( typeof option === 'string' ) {
  331. methodReturn = data[ option ].apply( data, args );
  332. }
  333. } );
  334. return ( methodReturn === undefined ) ? $set : methodReturn;
  335. };
  336. $.fn.combobox.defaults = {
  337. autoResizeMenu: true
  338. };
  339. $.fn.combobox.Constructor = Combobox;
  340. $.fn.combobox.noConflict = function() {
  341. $.fn.combobox = old;
  342. return this;
  343. };
  344. // DATA-API
  345. $( document ).on( 'mousedown.fu.combobox.data-api', '[data-initialize=combobox]', function( e ) {
  346. var $control = $( e.target ).closest( '.combobox' );
  347. if ( !$control.data( 'fu.combobox' ) ) {
  348. $control.combobox( $control.data() );
  349. }
  350. } );
  351. // Must be domReady for AMD compatibility
  352. $( function() {
  353. $( '[data-initialize=combobox]' ).each( function() {
  354. var $this = $( this );
  355. if ( !$this.data( 'fu.combobox' ) ) {
  356. $this.combobox( $this.data() );
  357. }
  358. } );
  359. } );
  360. } )( jQuery );
  361. ( function( $ ) {
  362. /*
  363. * Fuel UX Datepicker
  364. * https://github.com/ExactTarget/fuelux
  365. *
  366. * Copyright (c) 2014 ExactTarget
  367. * Licensed under the BSD New license.
  368. */
  369. // -- BEGIN MODULE CODE HERE --
  370. var INVALID_DATE = 'Invalid Date';
  371. var MOMENT_NOT_AVAILABLE = 'moment.js is not available so you cannot use this function';
  372. var datepickerStack = [];
  373. var moment = false;
  374. var old = $.fn.datepicker;
  375. var requestedMoment = false;
  376. var runStack = function() {
  377. var i, l;
  378. requestedMoment = true;
  379. for ( i = 0, l = datepickerStack.length; i < l; i++ ) {
  380. datepickerStack[ i ].init.call( datepickerStack[ i ].scope );
  381. }
  382. datepickerStack = [];
  383. };
  384. //only load moment if it's there. otherwise we'll look for it in window.moment
  385. if ( typeof define === 'function' && define.amd ) { //check if AMD is available
  386. require( [ 'moment' ], function( amdMoment ) {
  387. moment = amdMoment;
  388. runStack();
  389. }, function( err ) {
  390. var failedId = err.requireModules && err.requireModules[ 0 ];
  391. if ( failedId === 'moment' ) {
  392. runStack();
  393. }
  394. } );
  395. } else {
  396. runStack();
  397. }
  398. // DATEPICKER CONSTRUCTOR AND PROTOTYPE
  399. var Datepicker = function( element, options ) {
  400. this.$element = $( element );
  401. this.options = $.extend( true, {}, $.fn.datepicker.defaults, options );
  402. this.$calendar = this.$element.find( '.datepicker-calendar' );
  403. this.$days = this.$calendar.find( '.datepicker-calendar-days' );
  404. this.$header = this.$calendar.find( '.datepicker-calendar-header' );
  405. this.$headerTitle = this.$header.find( '.title' );
  406. this.$input = this.$element.find( 'input' );
  407. this.$wheels = this.$element.find( '.datepicker-wheels' );
  408. this.$wheelsMonth = this.$element.find( '.datepicker-wheels-month' );
  409. this.$wheelsYear = this.$element.find( '.datepicker-wheels-year' );
  410. this.artificialScrolling = false;
  411. this.formatDate = this.options.formatDate || this.formatDate;
  412. this.inputValue = null;
  413. this.moment = false;
  414. this.momentFormat = null;
  415. this.parseDate = this.options.parseDate || this.parseDate;
  416. this.preventBlurHide = false;
  417. this.restricted = this.options.restricted || [];
  418. this.restrictedParsed = [];
  419. this.restrictedText = this.options.restrictedText;
  420. this.sameYearOnly = this.options.sameYearOnly;
  421. this.selectedDate = null;
  422. this.yearRestriction = null;
  423. this.$calendar.find( '.datepicker-today' ).on( 'click.fu.datepicker', $.proxy( this.todayClicked, this ) );
  424. this.$days.on( 'click.fu.datepicker', 'tr td button', $.proxy( this.dateClicked, this ) );
  425. this.$element.find( '.dropdown-menu' ).on( 'mousedown.fu.datepicker', $.proxy( this.dropdownMousedown, this ) );
  426. this.$header.find( '.next' ).on( 'click.fu.datepicker', $.proxy( this.next, this ) );
  427. this.$header.find( '.prev' ).on( 'click.fu.datepicker', $.proxy( this.prev, this ) );
  428. this.$headerTitle.on( 'click.fu.datepicker', $.proxy( this.titleClicked, this ) );
  429. this.$input.on( 'blur.fu.datepicker', $.proxy( this.inputBlurred, this ) );
  430. this.$input.on( 'focus.fu.datepicker', $.proxy( this.inputFocused, this ) );
  431. this.$wheels.find( '.datepicker-wheels-back' ).on( 'click.fu.datepicker', $.proxy( this.backClicked, this ) );
  432. this.$wheels.find( '.datepicker-wheels-select' ).on( 'click.fu.datepicker', $.proxy( this.selectClicked, this ) );
  433. this.$wheelsMonth.on( 'click.fu.datepicker', 'ul button', $.proxy( this.monthClicked, this ) );
  434. this.$wheelsYear.on( 'click.fu.datepicker', 'ul button', $.proxy( this.yearClicked, this ) );
  435. this.$wheelsYear.find( 'ul' ).on( 'scroll.fu.datepicker', $.proxy( this.onYearScroll, this ) );
  436. var init = function() {
  437. if ( this.checkForMomentJS() ) {
  438. moment = moment || window.moment; // need to pull in the global moment if they didn't do it via require
  439. this.moment = true;
  440. this.momentFormat = this.options.momentConfig.format;
  441. this.setCulture( this.options.momentConfig.culture );
  442. // support moment with lang (< v2.8) or locale
  443. moment.locale = moment.locale || moment.lang;
  444. }
  445. this.setRestrictedDates( this.restricted );
  446. if ( !this.setDate( this.options.date ) ) {
  447. this.$input.val( '' );
  448. this.inputValue = this.$input.val();
  449. }
  450. if ( this.sameYearOnly ) {
  451. this.yearRestriction = ( this.selectedDate ) ? this.selectedDate.getFullYear() : new Date().getFullYear();
  452. }
  453. };
  454. if ( requestedMoment ) {
  455. init.call( this );
  456. } else {
  457. datepickerStack.push( {
  458. init: init,
  459. scope: this
  460. } );
  461. }
  462. };
  463. Datepicker.prototype = {
  464. constructor: Datepicker,
  465. backClicked: function() {
  466. this.changeView( 'calendar' );
  467. },
  468. changeView: function( view, date ) {
  469. if ( view === 'wheels' ) {
  470. this.$calendar.hide().attr( 'aria-hidden', 'true' );
  471. this.$wheels.show().removeAttr( 'aria-hidden', '' );
  472. if ( date ) {
  473. this.renderWheel( date );
  474. }
  475. } else {
  476. this.$wheels.hide().attr( 'aria-hidden', 'true' );
  477. this.$calendar.show().removeAttr( 'aria-hidden', '' );
  478. if ( date ) {
  479. this.renderMonth( date );
  480. }
  481. }
  482. },
  483. checkForMomentJS: function() {
  484. if (
  485. ( $.isFunction( window.moment ) || ( typeof moment !== 'undefined' && $.isFunction( moment ) ) ) &&
  486. $.isPlainObject( this.options.momentConfig ) &&
  487. this.options.momentConfig.culture && this.options.momentConfig.format
  488. ) {
  489. return true;
  490. } else {
  491. return false;
  492. }
  493. },
  494. dateClicked: function( e ) {
  495. var $td = $( e.currentTarget ).parents( 'td:first' );
  496. var date;
  497. if ( $td.hasClass( 'restricted' ) ) {
  498. return;
  499. }
  500. this.$days.find( 'td.selected' ).removeClass( 'selected' );
  501. $td.addClass( 'selected' );
  502. date = new Date( $td.attr( 'data-year' ), $td.attr( 'data-month' ), $td.attr( 'data-date' ) );
  503. this.selectedDate = date;
  504. this.$input.val( this.formatDate( date ) );
  505. this.inputValue = this.$input.val();
  506. this.$input.focus();
  507. this.$element.trigger( 'dateClicked.fu.datepicker', date );
  508. },
  509. destroy: function() {
  510. this.$element.remove();
  511. // any external bindings
  512. // [none]
  513. // empty elements to return to original markup
  514. this.$days.find( 'tbody' ).empty();
  515. this.$wheelsYear.find( 'ul' ).empty();
  516. return this.$element[ 0 ].outerHTML;
  517. },
  518. disable: function() {
  519. this.$element.addClass( 'disabled' );
  520. this.$element.find( 'input, button' ).attr( 'disabled', 'disabled' );
  521. this.$element.find( '.input-group-btn' ).removeClass( 'open' );
  522. },
  523. dropdownMousedown: function() {
  524. var self = this;
  525. this.preventBlurHide = true;
  526. setTimeout( function() {
  527. self.preventBlurHide = false;
  528. }, 0 );
  529. },
  530. enable: function() {
  531. this.$element.removeClass( 'disabled' );
  532. this.$element.find( 'input, button' ).removeAttr( 'disabled' );
  533. },
  534. formatDate: function( date ) {
  535. var padTwo = function( value ) {
  536. var s = '0' + value;
  537. return s.substr( s.length - 2 );
  538. };
  539. if ( this.moment ) {
  540. return moment( date ).format( this.momentFormat );
  541. } else {
  542. return padTwo( date.getMonth() + 1 ) + '/' + padTwo( date.getDate() ) + '/' + date.getFullYear();
  543. }
  544. },
  545. getCulture: function() {
  546. if ( this.moment ) {
  547. return moment.locale();
  548. } else {
  549. throw MOMENT_NOT_AVAILABLE;
  550. }
  551. },
  552. getDate: function() {
  553. return ( !this.selectedDate ) ? new Date( NaN ) : this.selectedDate;
  554. },
  555. getFormat: function() {
  556. if ( this.moment ) {
  557. return this.momentFormat;
  558. } else {
  559. throw MOMENT_NOT_AVAILABLE;
  560. }
  561. },
  562. getFormattedDate: function() {
  563. return ( !this.selectedDate ) ? INVALID_DATE : this.formatDate( this.selectedDate );
  564. },
  565. getRestrictedDates: function() {
  566. return this.restricted;
  567. },
  568. inputBlurred: function( e ) {
  569. var inputVal = this.$input.val();
  570. var date;
  571. if ( inputVal !== this.inputValue ) {
  572. date = this.setDate( inputVal );
  573. if ( date === null ) {
  574. this.$element.trigger( 'inputParsingFailed.fu.datepicker', inputVal );
  575. } else if ( date === false ) {
  576. this.$element.trigger( 'inputRestrictedDate.fu.datepicker', date );
  577. } else {
  578. this.$element.trigger( 'changed.fu.datepicker', date );
  579. }
  580. }
  581. if ( !this.preventBlurHide ) {
  582. this.$element.find( '.input-group-btn' ).removeClass( 'open' );
  583. }
  584. },
  585. inputFocused: function( e ) {
  586. this.$element.find( '.input-group-btn' ).addClass( 'open' );
  587. },
  588. isInvalidDate: function( date ) {
  589. var dateString = date.toString();
  590. if ( dateString === INVALID_DATE || dateString === 'NaN' ) {
  591. return true;
  592. }
  593. return false;
  594. },
  595. isRestricted: function( date, month, year ) {
  596. var restricted = this.restrictedParsed;
  597. var i, from, l, to;
  598. if ( this.sameYearOnly && this.yearRestriction !== null && year !== this.yearRestriction ) {
  599. return true;
  600. }
  601. for ( i = 0, l = restricted.length; i < l; i++ ) {
  602. from = restricted[ i ].from;
  603. to = restricted[ i ].to;
  604. if (
  605. ( year > from.year || ( year === from.year && month > from.month ) || ( year === from.year && month === from.month && date >= from.date ) ) &&
  606. ( year < to.year || ( year === to.year && month < to.month ) || ( year === to.year && month === to.month && date <= to.date ) )
  607. ) {
  608. return true;
  609. }
  610. }
  611. return false;
  612. },
  613. monthClicked: function( e ) {
  614. this.$wheelsMonth.find( '.selected' ).removeClass( 'selected' );
  615. $( e.currentTarget ).parent().addClass( 'selected' );
  616. },
  617. next: function() {
  618. var month = this.$headerTitle.attr( 'data-month' );
  619. var year = this.$headerTitle.attr( 'data-year' );
  620. month++;
  621. if ( month > 11 ) {
  622. if ( this.sameYearOnly ) {
  623. return;
  624. }
  625. month = 0;
  626. year++;
  627. }
  628. this.renderMonth( new Date( year, month, 1 ) );
  629. },
  630. onYearScroll: function( e ) {
  631. if ( this.artificialScrolling ) {
  632. return;
  633. }
  634. var $yearUl = $( e.currentTarget );
  635. var height = ( $yearUl.css( 'box-sizing' ) === 'border-box' ) ? $yearUl.outerHeight() : $yearUl.height();
  636. var scrollHeight = $yearUl.get( 0 ).scrollHeight;
  637. var scrollTop = $yearUl.scrollTop();
  638. var bottomPercentage = ( height / ( scrollHeight - scrollTop ) ) * 100;
  639. var topPercentage = ( scrollTop / scrollHeight ) * 100;
  640. var i, start;
  641. if ( topPercentage < 5 ) {
  642. start = parseInt( $yearUl.find( 'li:first' ).attr( 'data-year' ), 10 );
  643. for ( i = ( start - 1 ); i > ( start - 11 ); i-- ) {
  644. $yearUl.prepend( '<li data-year="' + i + '"><button type="button">' + i + '</button></li>' );
  645. }
  646. this.artificialScrolling = true;
  647. $yearUl.scrollTop( ( $yearUl.get( 0 ).scrollHeight - scrollHeight ) + scrollTop );
  648. this.artificialScrolling = false;
  649. } else if ( bottomPercentage > 90 ) {
  650. start = parseInt( $yearUl.find( 'li:last' ).attr( 'data-year' ), 10 );
  651. for ( i = ( start + 1 ); i < ( start + 11 ); i++ ) {
  652. $yearUl.append( '<li data-year="' + i + '"><button type="button">' + i + '</button></li>' );
  653. }
  654. }
  655. },
  656. //some code ripped from http://stackoverflow.com/questions/2182246/javascript-dates-in-ie-nan-firefox-chrome-ok
  657. parseDate: function( date ) {
  658. var self = this;
  659. var BAD_DATE = new Date( NaN );
  660. var dt, isoExp, momentParse, momentParseWithFormat, tryMomentParseAll, month, parts, use;
  661. if ( date ) {
  662. if ( this.moment ) { //if we have moment, use that to parse the dates
  663. momentParseWithFormat = function( d ) {
  664. var md = moment( d, self.momentFormat );
  665. return ( true === md.isValid() ) ? md.toDate() : BAD_DATE;
  666. };
  667. momentParse = function( d ) {
  668. var md = moment( new Date( d ) );
  669. return ( true === md.isValid() ) ? md.toDate() : BAD_DATE;
  670. };
  671. tryMomentParseAll = function( d, parseFunc1, parseFunc2 ) {
  672. var pd = parseFunc1( d );
  673. if ( !self.isInvalidDate( pd ) ) {
  674. return pd;
  675. }
  676. pd = parseFunc2( pd );
  677. if ( !self.isInvalidDate( pd ) ) {
  678. return pd;
  679. }
  680. return BAD_DATE;
  681. };
  682. if ( 'string' === typeof( date ) ) {
  683. // Attempts to parse date strings using this.momentFormat, falling back on newing a date
  684. return tryMomentParseAll( date, momentParseWithFormat, momentParse );
  685. } else {
  686. // Attempts to parse date by newing a date object directly, falling back on parsing using this.momentFormat
  687. return tryMomentParseAll( date, momentParse, momentParseWithFormat );
  688. }
  689. } else { //if moment isn't present, use previous date parsing strategy
  690. if ( typeof( date ) === 'string' ) {
  691. dt = new Date( Date.parse( date ) );
  692. if ( !this.isInvalidDate( dt ) ) {
  693. return dt;
  694. } else {
  695. date = date.split( 'T' )[ 0 ];
  696. isoExp = /^\s*(\d{4})-(\d\d)-(\d\d)\s*$/;
  697. parts = isoExp.exec( date );
  698. if ( parts ) {
  699. month = parseInt( parts[ 2 ], 10 );
  700. dt = new Date( parts[ 1 ], month - 1, parts[ 3 ] );
  701. if ( month === ( dt.getMonth() + 1 ) ) {
  702. return dt;
  703. }
  704. }
  705. }
  706. } else {
  707. dt = new Date( date );
  708. if ( !this.isInvalidDate( dt ) ) {
  709. return dt;
  710. }
  711. }
  712. }
  713. }
  714. return new Date( NaN );
  715. },
  716. prev: function() {
  717. var month = this.$headerTitle.attr( 'data-month' );
  718. var year = this.$headerTitle.attr( 'data-year' );
  719. month--;
  720. if ( month < 0 ) {
  721. if ( this.sameYearOnly ) {
  722. return;
  723. }
  724. month = 11;
  725. year--;
  726. }
  727. this.renderMonth( new Date( year, month, 1 ) );
  728. },
  729. renderMonth: function( date ) {
  730. date = date || new Date();
  731. var firstDay = new Date( date.getFullYear(), date.getMonth(), 1 ).getDay();
  732. var lastDate = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate();
  733. var lastMonthDate = new Date( date.getFullYear(), date.getMonth(), 0 ).getDate();
  734. var $month = this.$headerTitle.find( '.month' );
  735. var month = date.getMonth();
  736. var now = new Date();
  737. var nowDate = now.getDate();
  738. var nowMonth = now.getMonth();
  739. var nowYear = now.getFullYear();
  740. var selected = this.selectedDate;
  741. var $tbody = this.$days.find( 'tbody' );
  742. var year = date.getFullYear();
  743. var curDate, curMonth, curYear, i, j, rows, stage, previousStage, lastStage, $td, $tr;
  744. if ( selected ) {
  745. selected = {
  746. date: selected.getDate(),
  747. month: selected.getMonth(),
  748. year: selected.getFullYear()
  749. };
  750. }
  751. $month.find( '.current' ).removeClass( 'current' );
  752. $month.find( 'span[data-month="' + month + '"]' ).addClass( 'current' );
  753. this.$headerTitle.find( '.year' ).text( year );
  754. this.$headerTitle.attr( {
  755. 'data-month': month,
  756. 'data-year': year
  757. } );
  758. $tbody.empty();
  759. if ( firstDay !== 0 ) {
  760. curDate = lastMonthDate - firstDay + 1;
  761. stage = -1;
  762. } else {
  763. curDate = 1;
  764. stage = 0;
  765. }
  766. rows = ( lastDate <= ( 35 - firstDay ) ) ? 5 : 6;
  767. for ( i = 0; i < rows; i++ ) {
  768. $tr = $( '<tr></tr>' );
  769. for ( j = 0; j < 7; j++ ) {
  770. $td = $( '<td></td>' );
  771. if ( stage === -1 ) {
  772. $td.addClass( 'last-month' );
  773. if ( previousStage !== stage ) {
  774. $td.addClass( 'first' );
  775. }
  776. } else if ( stage === 1 ) {
  777. $td.addClass( 'next-month' );
  778. if ( previousStage !== stage ) {
  779. $td.addClass( 'first' );
  780. }
  781. }
  782. curMonth = month + stage;
  783. curYear = year;
  784. if ( curMonth < 0 ) {
  785. curMonth = 11;
  786. curYear--;
  787. } else if ( curMonth > 11 ) {
  788. curMonth = 0;
  789. curYear++;
  790. }
  791. $td.attr( {
  792. 'data-date': curDate,
  793. 'data-month': curMonth,
  794. 'data-year': curYear
  795. } );
  796. if ( curYear === nowYear && curMonth === nowMonth && curDate === nowDate ) {
  797. $td.addClass( 'current-day' );
  798. } else if ( curYear < nowYear || ( curYear === nowYear && curMonth < nowMonth ) ||
  799. ( curYear === nowYear && curMonth === nowMonth && curDate < nowDate ) ) {
  800. $td.addClass( 'past' );
  801. if ( !this.options.allowPastDates ) {
  802. $td.addClass( 'restricted' ).attr( 'title', this.restrictedText );
  803. }
  804. }
  805. if ( this.isRestricted( curDate, curMonth, curYear ) ) {
  806. $td.addClass( 'restricted' ).attr( 'title', this.restrictedText );
  807. }
  808. if ( selected && curYear === selected.year && curMonth === selected.month && curDate === selected.date ) {
  809. $td.addClass( 'selected' );
  810. }
  811. if ( $td.hasClass( 'restricted' ) ) {
  812. $td.html( '<span><b class="datepicker-date">' + curDate + '</b></span>' );
  813. } else {
  814. $td.html( '<span><button type="button" class="datepicker-date">' + curDate + '</button></span>' );
  815. }
  816. curDate++;
  817. lastStage = previousStage;
  818. previousStage = stage;
  819. if ( stage === -1 && curDate > lastMonthDate ) {
  820. curDate = 1;
  821. stage = 0;
  822. if ( lastStage !== stage ) {
  823. $td.addClass( 'last' );
  824. }
  825. } else if ( stage === 0 && curDate > lastDate ) {
  826. curDate = 1;
  827. stage = 1;
  828. if ( lastStage !== stage ) {
  829. $td.addClass( 'last' );
  830. }
  831. }
  832. if ( i === ( rows - 1 ) && j === 6 ) {
  833. $td.addClass( 'last' );
  834. }
  835. $tr.append( $td );
  836. }
  837. $tbody.append( $tr );
  838. }
  839. },
  840. renderWheel: function( date ) {
  841. var month = date.getMonth();
  842. var $monthUl = this.$wheelsMonth.find( 'ul' );
  843. var year = date.getFullYear();
  844. var $yearUl = this.$wheelsYear.find( 'ul' );
  845. var i, $monthSelected, $yearSelected;
  846. if ( this.sameYearOnly ) {
  847. this.$wheelsMonth.addClass( 'full' );
  848. this.$wheelsYear.addClass( 'hidden' );
  849. } else {
  850. this.$wheelsMonth.removeClass( 'full' );
  851. this.$wheelsYear.removeClass( 'hide hidden' ); // .hide is deprecated
  852. }
  853. $monthUl.find( '.selected' ).removeClass( 'selected' );
  854. $monthSelected = $monthUl.find( 'li[data-month="' + month + '"]' );
  855. $monthSelected.addClass( 'selected' );
  856. $monthUl.scrollTop( $monthUl.scrollTop() + ( $monthSelected.position().top - $monthUl.outerHeight() / 2 - $monthSelected.outerHeight( true ) / 2 ) );
  857. $yearUl.empty();
  858. for ( i = ( year - 10 ); i < ( year + 11 ); i++ ) {
  859. $yearUl.append( '<li data-year="' + i + '"><button type="button">' + i + '</button></li>' );
  860. }
  861. $yearSelected = $yearUl.find( 'li[data-year="' + year + '"]' );
  862. $yearSelected.addClass( 'selected' );
  863. this.artificialScrolling = true;
  864. $yearUl.scrollTop( $yearUl.scrollTop() + ( $yearSelected.position().top - $yearUl.outerHeight() / 2 - $yearSelected.outerHeight( true ) / 2 ) );
  865. this.artificialScrolling = false;
  866. $monthSelected.find( 'button' ).focus();
  867. },
  868. selectClicked: function() {
  869. var month = this.$wheelsMonth.find( '.selected' ).attr( 'data-month' );
  870. var year = this.$wheelsYear.find( '.selected' ).attr( 'data-year' );
  871. this.changeView( 'calendar', new Date( year, month, 1 ) );
  872. },
  873. setCulture: function( cultureCode ) {
  874. if ( !cultureCode ) {
  875. return false;
  876. }
  877. if ( this.moment ) {
  878. moment.locale( cultureCode );
  879. } else {
  880. throw MOMENT_NOT_AVAILABLE;
  881. }
  882. },
  883. setDate: function( date ) {
  884. var parsed = this.parseDate( date );
  885. if ( !this.isInvalidDate( parsed ) ) {
  886. if ( !this.isRestricted( parsed.getDate(), parsed.getMonth(), parsed.getFullYear() ) ) {
  887. this.selectedDate = parsed;
  888. this.renderMonth( parsed );
  889. this.$input.val( this.formatDate( parsed ) );
  890. } else {
  891. this.selectedDate = false;
  892. this.renderMonth();
  893. }
  894. } else {
  895. this.selectedDate = null;
  896. this.renderMonth();
  897. }
  898. this.inputValue = this.$input.val();
  899. return this.selectedDate;
  900. },
  901. setFormat: function( format ) {
  902. if ( !format ) {
  903. return false;
  904. }
  905. if ( this.moment ) {
  906. this.momentFormat = format;
  907. } else {
  908. throw MOMENT_NOT_AVAILABLE;
  909. }
  910. },
  911. setRestrictedDates: function( restricted ) {
  912. var parsed = [];
  913. var self = this;
  914. var i, l;
  915. var parseItem = function( val ) {
  916. if ( val === -Infinity ) {
  917. return {
  918. date: -Infinity,
  919. month: -Infinity,
  920. year: -Infinity
  921. };
  922. } else if ( val === Infinity ) {
  923. return {
  924. date: Infinity,
  925. month: Infinity,
  926. year: Infinity
  927. };
  928. } else {
  929. val = self.parseDate( val );
  930. return {
  931. date: val.getDate(),
  932. month: val.getMonth(),
  933. year: val.getFullYear()
  934. };
  935. }
  936. };
  937. this.restricted = restricted;
  938. for ( i = 0, l = restricted.length; i < l; i++ ) {
  939. parsed.push( {
  940. from: parseItem( restricted[ i ].from ),
  941. to: parseItem( restricted[ i ].to )
  942. } );
  943. }
  944. this.restrictedParsed = parsed;
  945. },
  946. titleClicked: function( e ) {
  947. this.changeView( 'wheels', new Date( this.$headerTitle.attr( 'data-year' ), this.$headerTitle.attr( 'data-month' ), 1 ) );
  948. },
  949. todayClicked: function( e ) {
  950. var date = new Date();
  951. if ( ( date.getMonth() + '' ) !== this.$headerTitle.attr( 'data-month' ) || ( date.getFullYear() + '' ) !== this.$headerTitle.attr( 'data-year' ) ) {
  952. this.renderMonth( date );
  953. }
  954. },
  955. yearClicked: function( e ) {
  956. this.$wheelsYear.find( '.selected' ).removeClass( 'selected' );
  957. $( e.currentTarget ).parent().addClass( 'selected' );
  958. }
  959. };
  960. // DATEPICKER PLUGIN DEFINITION
  961. $.fn.datepicker = function( option ) {
  962. var args = Array.prototype.slice.call( arguments, 1 );
  963. var methodReturn;
  964. var $set = this.each( function() {
  965. var $this = $( this );
  966. var data = $this.data( 'fu.datepicker' );
  967. var options = typeof option === 'object' && option;
  968. if ( !data ) {
  969. $this.data( 'fu.datepicker', ( data = new Datepicker( this, options ) ) );
  970. }
  971. if ( typeof option === 'string' ) {
  972. methodReturn = data[ option ].apply( data, args );
  973. }
  974. } );
  975. return ( methodReturn === undefined ) ? $set : methodReturn;
  976. };
  977. $.fn.datepicker.defaults = {
  978. allowPastDates: false,
  979. date: new Date(),
  980. formatDate: null,
  981. momentConfig: {
  982. culture: 'en',
  983. format: 'L' // more formats can be found here http://momentjs.com/docs/#/customization/long-date-formats/.
  984. },
  985. parseDate: null,
  986. restricted: [], //accepts an array of objects formatted as so: { from: {{date}}, to: {{date}} } (ex: [ { from: new Date('12/11/2014'), to: new Date('03/31/2015') } ])
  987. restrictedText: 'Restricted',
  988. sameYearOnly: false
  989. };
  990. $.fn.datepicker.Constructor = Datepicker;
  991. $.fn.datepicker.noConflict = function() {
  992. $.fn.datepicker = old;
  993. return this;
  994. };
  995. // DATA-API
  996. $( document ).on( 'mousedown.fu.datepicker.data-api', '[data-initialize=datepicker]', function( e ) {
  997. var $control = $( e.target ).closest( '.datepicker' );
  998. if ( !$control.data( 'datepicker' ) ) {
  999. $control.datepicker( $control.data() );
  1000. }
  1001. } );
  1002. //used to prevent the dropdown from closing when clicking within it's bounds
  1003. $( document ).on( 'click.fu.datepicker.data-api', '.datepicker .dropdown-menu', function( e ) {
  1004. var $target = $( e.target );
  1005. if ( !$target.is( '.datepicker-date' ) || $target.closest( '.restricted' ).length ) {
  1006. e.stopPropagation();
  1007. }
  1008. } );
  1009. //used to prevent the dropdown from closing when clicking on the input
  1010. $( document ).on( 'click.fu.datepicker.data-api', '.datepicker input', function( e ) {
  1011. e.stopPropagation();
  1012. } );
  1013. $( function() {
  1014. $( '[data-initialize=datepicker]' ).each( function() {
  1015. var $this = $( this );
  1016. if ( $this.data( 'datepicker' ) ) {
  1017. return;
  1018. }
  1019. $this.datepicker( $this.data() );
  1020. } );
  1021. } );
  1022. } )( jQuery );
  1023. ( function( $ ) {
  1024. /*
  1025. * Fuel UX Dropdown Auto Flip
  1026. * https://github.com/ExactTarget/fuelux
  1027. *
  1028. * Copyright (c) 2014 ExactTarget
  1029. * Licensed under the BSD New license.
  1030. */
  1031. // -- BEGIN MODULE CODE HERE --
  1032. $( document.body ).on( 'click.fu.dropdown-autoflip', '[data-toggle=dropdown][data-flip]', function( event ) {
  1033. if ( $( this ).data().flip === "auto" ) {
  1034. // have the drop down decide where to place itself
  1035. _autoFlip( $( this ).next( '.dropdown-menu' ) );
  1036. }
  1037. } );
  1038. // For pillbox suggestions dropdown
  1039. $( document.body ).on( 'suggested.fu.pillbox', function( event, element ) {
  1040. _autoFlip( $( element ) );
  1041. $( element ).parent().addClass( 'open' );
  1042. } );
  1043. function _autoFlip( menu ) {
  1044. // hide while the browser thinks
  1045. $( menu ).css( {
  1046. visibility: "hidden"
  1047. } );
  1048. // decide where to put menu
  1049. if ( dropUpCheck( menu ) ) {
  1050. menu.parent().addClass( "dropup" );
  1051. } else {
  1052. menu.parent().removeClass( "dropup" );
  1053. }
  1054. // show again
  1055. $( menu ).css( {
  1056. visibility: "visible"
  1057. } );
  1058. }
  1059. function dropUpCheck( element ) {
  1060. // caching container
  1061. var $container = _getContainer( element );
  1062. // building object with measurementsances for later use
  1063. var measurements = {};
  1064. measurements.parentHeight = element.parent().outerHeight();
  1065. measurements.parentOffsetTop = element.parent().offset().top;
  1066. measurements.dropdownHeight = element.outerHeight();
  1067. measurements.containerHeight = $container.overflowElement.outerHeight();
  1068. // this needs to be different if the window is the container or another element is
  1069. measurements.containerOffsetTop = ( !!$container.isWindow ) ? $container.overflowElement.scrollTop() : $container.overflowElement.offset().top;
  1070. // doing the calculations
  1071. measurements.fromTop = measurements.parentOffsetTop - measurements.containerOffsetTop;
  1072. measurements.fromBottom = measurements.containerHeight - measurements.parentHeight - ( measurements.parentOffsetTop - measurements.containerOffsetTop );
  1073. // actual determination of where to put menu
  1074. // false = drop down
  1075. // true = drop up
  1076. if ( measurements.dropdownHeight < measurements.fromBottom ) {
  1077. return false;
  1078. } else if ( measurements.dropdownHeight < measurements.fromTop ) {
  1079. return true;
  1080. } else if ( measurements.dropdownHeight >= measurements.fromTop && measurements.dropdownHeight >= measurements.fromBottom ) {
  1081. // decide which one is bigger and put it there
  1082. if ( measurements.fromTop >= measurements.fromBottom ) {
  1083. return true;
  1084. } else {
  1085. return false;
  1086. }
  1087. }
  1088. }
  1089. function _getContainer( element ) {
  1090. var containerElement, isWindow;
  1091. if ( element.attr( 'data-target' ) ) {
  1092. containerElement = element.attr( 'data-target' );
  1093. isWindow = false;
  1094. } else {
  1095. containerElement = window;
  1096. isWindow = true;
  1097. }
  1098. $.each( element.parents(), function( index, value ) {
  1099. if ( $( value ).css( 'overflow' ) !== 'visible' ) {
  1100. containerElement = value;
  1101. isWindow = false;
  1102. return false;
  1103. }
  1104. } );
  1105. return {
  1106. overflowElement: $( containerElement ),
  1107. isWindow: isWindow
  1108. };
  1109. }
  1110. // register empty plugin
  1111. $.fn.dropdownautoflip = function() {
  1112. /* empty */
  1113. };
  1114. } )( jQuery );
  1115. ( function( $ ) {
  1116. /*
  1117. * Fuel UX Loader
  1118. * https://github.com/ExactTarget/fuelux
  1119. *
  1120. * Copyright (c) 2014 ExactTarget
  1121. * Licensed under the BSD New license.
  1122. */
  1123. // -- BEGIN MODULE CODE HERE --
  1124. var old = $.fn.loader;
  1125. // LOADER CONSTRUCTOR AND PROTOTYPE
  1126. var Loader = function( element, options ) {
  1127. this.$element = $( element );
  1128. this.options = $.extend( {}, $.fn.loader.defaults, options );
  1129. this.begin = ( this.$element.is( '[data-begin]' ) ) ? parseInt( this.$element.attr( 'data-begin' ), 10 ) : 1;
  1130. this.delay = ( this.$element.is( '[data-delay]' ) ) ? parseFloat( this.$element.attr( 'data-delay' ) ) : 150;
  1131. this.end = ( this.$element.is( '[data-end]' ) ) ? parseInt( this.$element.attr( 'data-end' ), 10 ) : 8;
  1132. this.frame = ( this.$element.is( '[data-frame]' ) ) ? parseInt( this.$element.attr( 'data-frame' ), 10 ) : this.begin;
  1133. this.isIElt9 = false;
  1134. this.timeout = {};
  1135. var ieVer = this.msieVersion();
  1136. if ( ieVer !== false && ieVer < 9 ) {
  1137. this.$element.addClass( 'iefix' );
  1138. this.isIElt9 = true;
  1139. }
  1140. this.$element.attr( 'data-frame', this.frame + '' );
  1141. this.play();
  1142. };
  1143. Loader.prototype = {
  1144. constructor: Loader,
  1145. destroy: function() {
  1146. this.pause();
  1147. this.$element.remove();
  1148. // any external bindings
  1149. // [none]
  1150. // empty elements to return to original markup
  1151. // [none]
  1152. // returns string of markup
  1153. return this.$element[ 0 ].outerHTML;
  1154. },
  1155. ieRepaint: function() {
  1156. if ( this.isIElt9 ) {
  1157. this.$element.addClass( 'iefix_repaint' ).removeClass( 'iefix_repaint' );
  1158. }
  1159. },
  1160. msieVersion: function() {
  1161. var ua = window.navigator.userAgent;
  1162. var msie = ua.indexOf( 'MSIE ' );
  1163. if ( msie > 0 ) {
  1164. return parseInt( ua.substring( msie + 5, ua.indexOf( ".", msie ) ), 10 );
  1165. } else {
  1166. return false;
  1167. }
  1168. },
  1169. next: function() {
  1170. this.frame++;
  1171. if ( this.frame > this.end ) {
  1172. this.frame = this.begin;
  1173. }
  1174. this.$element.attr( 'data-frame', this.frame + '' );
  1175. this.ieRepaint();
  1176. },
  1177. pause: function() {
  1178. clearTimeout( this.timeout );
  1179. },
  1180. play: function() {
  1181. var self = this;
  1182. clearTimeout( this.timeout );
  1183. this.timeout = setTimeout( function() {
  1184. self.next();
  1185. self.play();
  1186. }, this.delay );
  1187. },
  1188. previous: function() {
  1189. this.frame--;
  1190. if ( this.frame < this.begin ) {
  1191. this.frame = this.end;
  1192. }
  1193. this.$element.attr( 'data-frame', this.frame + '' );
  1194. this.ieRepaint();
  1195. },
  1196. reset: function() {
  1197. this.frame = this.begin;
  1198. this.$element.attr( 'data-frame', this.frame + '' );
  1199. this.ieRepaint();
  1200. }
  1201. };
  1202. // LOADER PLUGIN DEFINITION
  1203. $.fn.loader = function( option ) {
  1204. var args = Array.prototype.slice.call( arguments, 1 );
  1205. var methodReturn;
  1206. var $set = this.each( function() {
  1207. var $this = $( this );
  1208. var data = $this.data( 'fu.loader' );
  1209. var options = typeof option === 'object' && option;
  1210. if ( !data ) {
  1211. $this.data( 'fu.loader', ( data = new Loader( this, options ) ) );
  1212. }
  1213. if ( typeof option === 'string' ) {
  1214. methodReturn = data[ option ].apply( data, args );
  1215. }
  1216. } );
  1217. return ( methodReturn === undefined ) ? $set : methodReturn;
  1218. };
  1219. $.fn.loader.defaults = {};
  1220. $.fn.loader.Constructor = Loader;
  1221. $.fn.loader.noConflict = function() {
  1222. $.fn.loader = old;
  1223. return this;
  1224. };
  1225. // INIT LOADER ON DOMCONTENTLOADED
  1226. $( function() {
  1227. $( '[data-initialize=loader]' ).each( function() {
  1228. var $this = $( this );
  1229. if ( !$this.data( 'fu.loader' ) ) {
  1230. $this.loader( $this.data() );
  1231. }
  1232. } );
  1233. } );
  1234. } )( jQuery );
  1235. ( function( $ ) {
  1236. /*
  1237. * Fuel UX Placard
  1238. * https://github.com/ExactTarget/fuelux
  1239. *
  1240. * Copyright (c) 2014 ExactTarget
  1241. * Licensed under the BSD New license.
  1242. */
  1243. // -- BEGIN MODULE CODE HERE --
  1244. var old = $.fn.placard;
  1245. // PLACARD CONSTRUCTOR AND PROTOTYPE
  1246. var Placard = function( element, options ) {
  1247. var self = this;
  1248. this.$element = $( element );
  1249. this.options = $.extend( {}, $.fn.placard.defaults, options );
  1250. this.$accept = this.$element.find( '.placard-accept' );
  1251. this.$cancel = this.$element.find( '.placard-cancel' );
  1252. this.$field = this.$element.find( '.placard-field' );
  1253. this.$footer = this.$element.find( '.placard-footer' );
  1254. this.$header = this.$element.find( '.placard-header' );
  1255. this.$popup = this.$element.find( '.placard-popup' );
  1256. this.actualValue = null;
  1257. this.clickStamp = '_';
  1258. this.previousValue = '';
  1259. if ( this.options.revertOnCancel === -1 ) {
  1260. this.options.revertOnCancel = ( this.$accept.length > 0 ) ? true : false;
  1261. }
  1262. this.isInput = this.$field.is( 'input' );
  1263. this.$field.on( 'focus.fu.placard', $.proxy( this.show, this ) );
  1264. this.$field.on( 'keydown.fu.placard', $.proxy( this.keyComplete, this ) );
  1265. this.$accept.on( 'click.fu.placard', $.proxy( this.complete, this, 'accept' ) );
  1266. this.$cancel.on( 'click.fu.placard', function( e ) {
  1267. e.preventDefault();
  1268. self.complete( 'cancel' );
  1269. } );
  1270. this.ellipsis();
  1271. };
  1272. Placard.prototype = {
  1273. constructor: Placard,
  1274. complete: function( action ) {
  1275. var func = this.options[ 'on' + action[ 0 ].toUpperCase() + action.substring( 1 ) ];
  1276. var obj = {
  1277. previousValue: this.previousValue,
  1278. value: this.$field.val()
  1279. };
  1280. if ( func ) {
  1281. func( obj );
  1282. this.$element.trigger( action, obj );
  1283. } else {
  1284. if ( action === 'cancel' && this.options.revertOnCancel ) {
  1285. this.$field.val( this.previousValue );
  1286. }
  1287. this.$element.trigger( action, obj );
  1288. this.hide();
  1289. }
  1290. },
  1291. keyComplete: function( e ) {
  1292. if ( this.isInput && e.keyCode === 13 ) {
  1293. this.complete( 'accept' );
  1294. this.$field.blur();
  1295. } else if ( e.keyCode === 27 ) {
  1296. this.complete( 'cancel' );
  1297. this.$field.blur();
  1298. }
  1299. },
  1300. destroy: function() {
  1301. this.$element.remove();
  1302. // remove any external bindings
  1303. $( document ).off( 'click.fu.placard.externalClick.' + this.clickStamp );
  1304. // set input value attrbute
  1305. this.$element.find( 'input' ).each( function() {
  1306. $( this ).attr( 'value', $( this ).val() );
  1307. } );
  1308. // empty elements to return to original markup
  1309. // [none]
  1310. // return string of markup
  1311. return this.$element[ 0 ].outerHTML;
  1312. },
  1313. disable: function() {
  1314. this.$element.addClass( 'disabled' );
  1315. this.$field.attr( 'disabled', 'disabled' );
  1316. this.hide();
  1317. },
  1318. ellipsis: function() {
  1319. var field, i, str;
  1320. if ( this.$element.attr( 'data-ellipsis' ) === 'true' ) {
  1321. field = this.$field.get( 0 );
  1322. if ( this.$field.is( 'input' ) ) {
  1323. field.scrollLeft = 0;
  1324. } else {
  1325. field.scrollTop = 0;
  1326. if ( field.clientHeight < field.scrollHeight ) {
  1327. this.actualValue = this.$field.val();
  1328. this.$field.val( '' );
  1329. str = '';
  1330. i = 0;
  1331. while ( field.clientHeight >= field.scrollHeight ) {
  1332. str += this.actualValue[ i ];
  1333. this.$field.val( str + '...' );
  1334. i++;
  1335. }
  1336. str = ( str.length > 0 ) ? str.substring( 0, str.length - 1 ) : '';
  1337. this.$field.val( str + '...' );
  1338. }
  1339. }
  1340. }
  1341. },
  1342. enable: function() {
  1343. this.$element.removeClass( 'disabled' );
  1344. this.$field.removeAttr( 'disabled' );
  1345. },
  1346. externalClickListener: function( e, force ) {
  1347. if ( force === true || this.isExternalClick( e ) ) {
  1348. this.complete( this.options.externalClickAction );
  1349. }
  1350. },
  1351. getValue: function() {
  1352. if ( this.actualValue !== null ) {
  1353. return this.actualValue;
  1354. } else {
  1355. return this.$field.val();
  1356. }
  1357. },
  1358. hide: function() {
  1359. if ( !this.$element.hasClass( 'showing' ) ) {
  1360. return;
  1361. }
  1362. this.$element.removeClass( 'showing' );
  1363. this.ellipsis();
  1364. $( document ).off( 'click.fu.placard.externalClick.' + this.clickStamp );
  1365. this.$element.trigger( 'hidden.fu.placard' );
  1366. },
  1367. isExternalClick: function( e ) {
  1368. var el = this.$element.get( 0 );
  1369. var exceptions = this.options.externalClickExceptions || [];
  1370. var $originEl = $( e.target );
  1371. var i, l;
  1372. if ( e.target === el || $originEl.parents( '.placard:first' ).get( 0 ) === el ) {
  1373. return false;
  1374. } else {
  1375. for ( i = 0, l = exceptions.length; i < l; i++ ) {
  1376. if ( $originEl.is( exceptions[ i ] ) || $originEl.parents( exceptions[ i ] ).length > 0 ) {
  1377. return false;
  1378. }
  1379. }
  1380. }
  1381. return true;
  1382. },
  1383. setValue: function( val ) {
  1384. this.$field.val( val );
  1385. if ( !this.$element.hasClass( 'showing' ) ) {
  1386. this.ellipsis();
  1387. }
  1388. },
  1389. show: function() {
  1390. var other;
  1391. if ( this.$element.hasClass( 'showing' ) ) {
  1392. return;
  1393. }
  1394. other = $( document ).find( '.placard.showing' );
  1395. if ( other.length > 0 ) {
  1396. if ( other.data( 'fu.placard' ) && other.data( 'fu.placard' ).options.explicit ) {
  1397. return;
  1398. }
  1399. other.placard( 'externalClickListener', {}, true );
  1400. }
  1401. this.previousValue = this.$field.val();
  1402. this.$element.addClass( 'showing' );
  1403. if ( this.actualValue !== null ) {
  1404. this.$field.val( this.actualValue );
  1405. this.actualValue = null;
  1406. }
  1407. if ( this.$header.length > 0 ) {
  1408. this.$popup.css( 'top', '-' + this.$header.outerHeight( true ) + 'px' );
  1409. }
  1410. if ( this.$footer.length > 0 ) {
  1411. this.$popup.css( 'bottom', '-' + this.$footer.outerHeight( true ) + 'px' );
  1412. }
  1413. this.$element.trigger( 'shown.fu.placard' );
  1414. this.clickStamp = new Date().getTime() + ( Math.floor( Math.random() * 100 ) + 1 );
  1415. if ( !this.options.explicit ) {
  1416. $( document ).on( 'click.fu.placard.externalClick.' + this.clickStamp, $.proxy( this.externalClickListener, this ) );
  1417. }
  1418. }
  1419. };
  1420. // PLACARD PLUGIN DEFINITION
  1421. $.fn.placard = function( option ) {
  1422. var args = Array.prototype.slice.call( arguments, 1 );
  1423. var methodReturn;
  1424. var $set = this.each( function() {
  1425. var $this = $( this );
  1426. var data = $this.data( 'fu.placard' );
  1427. var options = typeof option === 'object' && option;
  1428. if ( !data ) {
  1429. $this.data( 'fu.placard', ( data = new Placard( this, options ) ) );
  1430. }
  1431. if ( typeof option === 'string' ) {
  1432. methodReturn = data[ option ].apply( data, args );
  1433. }
  1434. } );
  1435. return ( methodReturn === undefined ) ? $set : methodReturn;
  1436. };
  1437. $.fn.placard.defaults = {
  1438. onAccept: undefined,
  1439. onCancel: undefined,
  1440. externalClickAction: 'cancel',
  1441. externalClickExceptions: [],
  1442. explicit: false,
  1443. revertOnCancel: -1 //negative 1 will check for an '.placard-accept' button. Also can be set to true or false
  1444. };
  1445. $.fn.placard.Constructor = Placard;
  1446. $.fn.placard.noConflict = function() {
  1447. $.fn.placard = old;
  1448. return this;
  1449. };
  1450. // DATA-API
  1451. $( document ).on( 'focus.fu.placard.data-api', '[data-initialize=placard]', function( e ) {
  1452. var $control = $( e.target ).closest( '.placard' );
  1453. if ( !$control.data( 'fu.placard' ) ) {
  1454. $control.placard( $control.data() );
  1455. }
  1456. } );
  1457. // Must be domReady for AMD compatibility
  1458. $( function() {
  1459. $( '[data-initialize=placard]' ).each( function() {
  1460. var $this = $( this );
  1461. if ( $this.data( 'fu.placard' ) ) return;
  1462. $this.placard( $this.data() );
  1463. } );
  1464. } );
  1465. } )( jQuery );
  1466. ( function( $ ) {
  1467. /*
  1468. * Fuel UX Radio
  1469. * https://github.com/ExactTarget/fuelux
  1470. *
  1471. * Copyright (c) 2014 ExactTarget
  1472. * Licensed under the BSD New