<commit_before>import declare from 'dojo/_base/declare';
import array from 'dojo/_base/array';
import lang from 'dojo/_base/lang';
import Memory from 'dojo/store/Memory';
import SpeedSearchWidget from '../SpeedSearchWidget';
import string from 'dojo/string';
import GroupedList from 'argos/GroupedList';

const resource = window.localeContext.getEntitySync('leftDrawer').attributes;

/**
 * @class crm.Views.LeftDrawer
 *
 *
 * @extends argos.GroupedList
 *
 */
const __class = declare('crm.Views.LeftDrawer', [GroupedList], {
  // Templates
  cls: ' contextualContent',
  enablePullToRefresh: false,
  rowTemplate: new Simplate([
    '
',
    '{% if ($$._hasIcon($)) { %}',
    '
',
    '{% if ($.iconTemplate) { %}',
    '{%! $.iconTemplate %}',
    '{% } else if ($.cls) { %}',
    '

',
    '{% } else if ($.icon) { %}',
    '',
    '{% } %}',
    '
',
    '{% } %}',
    '
{%! $$.itemTemplate %}
',
    '
',
  ]),
  _hasIcon: function _hasIcon(entry) {
    return entry.iconTemplate || entry.cls || entry.icon;
  },
  itemTemplate: new Simplate([
    '
{%: $.title %}
',
  ]),

  // Localization
  configureText: resource.configureText,
  addAccountContactText: resource.addAccountContactText,
  titleText: resource.titleText,
  actionsText: resource.actionsText,
  viewsText: resource.viewsText,
  footerText: resource.footerText,
  settingsText: resource.settingsText,
  helpText: resource.helpText,
  logOutText: resource.logOutText,
  logOutConfirmText: resource.logOutConfirmText,
  onlineText: resource.onlineText,
  offlineText: resource.offlineText,
  connectionText: resource.connectionText,

  // View Properties
  id: 'left_drawer',
  expose: false,
  enableSearch: true,
  searchWidgetClass: SpeedSearchWidget,
  customizationSet: 'left_drawer',

  settingsView: 'settings',
  helpView: 'help',
  configurationView: 'configure',
  addAccountContactView: 'add_account_contact',
  searchView: 'speedsearch_list',

  logOut: function logOut() {
    const sure = window.confirm(this.logOutConfirmText); // eslint-disable-line
    if (sure) {
      App.logOut();
    }
  },
  loadAndNavigateToView: function loadAndNavigateToView(params) {
    const view = App.getView(params && params.view);
    this.navigateToView(view);
  },
  navigateToView: function navigateToView(view) {
    App.snapper.close();
    if (view) {
      view.show();
    }
  },
  addAccountContact: function addAccountContact() {
    App.snapper.close();
    const view = App.getView('add_account_contact');
    if (view) {
      view.show({
        insert: true,
      });
    }
  },
  navigateToConfigurationView: function navigateToConfigurationView() {
    const view = App.getView(this.configurationView);
    this.navigateToView(view);
  },
  navigateToSettingsView: function navigateToSettingsView() {
    const view = App.getView(this.settingsView);
    this.navigateToView(view);
  },
  navigateToHelpView: function navigateToHelpView() {
    const view = App.getView(this.helpView);
    this.navigateToView(view);
  },
  formatSearchQuery: function formatSearchQuery(searchQuery) {
    const expression = new RegExp(searchQuery, 'i');

    return function testExpression(entry) {
      return expression.test(entry.title);
    };
  },
  hasMoreData: function hasMoreData() {
    return false;
  },
  getGroupForEntry: function getGroupForEntry(entry) {
    const footerItems = [
      'ConfigureMenu',
      'SettingsAction',
      'HelpAction',
      'Logout',
      'ConnectionIndicator',
    ];

    if (entry.view) {
      return {
        tag: 'view',
        title: this.viewsText,
      };
    }

    if (array.indexOf(footerItems, entry.name) >= 0) {
      return {
        tag: 'footer',
        title: this.footerText,
      };
    }

    return {
      tag: 'action',
      title: this.actionsText,
    };
  },
  init: function init() {
    this.inherited(arguments);
    this.connect(App, 'onRegistered', this._onRegistered);
  },
  createLayout: function createLayout() {
    if (this.layout) {
      return this.layout;
    }

    const layout = [];

    const quickActions = {
      id: 'actions',
      children: [{
        'name': 'AddAccountContactAction',
        'action': 'addAccountContact',
        'title': this.addAccountContactText,
      }],
    };

    layout.push(quickActions);

    const goTo = {
      id: 'views',
      children: [],
    };

    const configured = lang.getObject('preferences.home.visible', false, window.App);
    for (let i = 0; i < configured.length; i++) {
      const view = App.getView(configured[i]);
      if (view) {
        goTo.children.push({
          'action': 'loadAndNavigateToView',
          'view': view.id,
          'title': view.titleText,
          'security': view.getSecurity(),
          'enableOfflineSupport': view.enableOfflineSupport,
        });
      }
    }

    layout.push(goTo);

    const footer = {
      id: 'footer',
      children: [{
        'name': 'ConfigureMenu',
        'action': 'navigateToConfigurationView',
        'title': this.configureText,
        'enableOfflineSupport': false,
      }, {
        'name': 'SettingsAction',
        'action': 'navigateToSettingsView',
        'title': this.settingsText,
        'enableOfflineSupport': true,
      }, {
        'name': 'HelpAction',
        'action': 'navigateToHelpView',
        'title': this.helpText,
        'enableOfflineSupport': true,
      }, {
        'name': 'Logout',
        'action': 'logOut',
        'title': this.logOutText,
        'enableOfflineSupport': false,
      }, {
        'name': 'ConnectionIndicator',
        'title': string.substitute(this.connectionText, {connectionStatus: App.onLine ? this.onlineText : this.offlineText}),
        'enableOfflineSupport': true,
      }],
    };

    layout.push(footer);

    return layout;
  },
  createStore: function createStore() {
    const layout = this._createCustomizedLayout(this.createLayout());
    const list = [];
    let total = 0;
    let section;

    for (let i = 0; i < layout.length; i++) {
      section = layout[i].children;

      for (let j = 0; j < section.length; j++) {
        total = total + 1;
        const row = section[j];
        row.$key = total;

        if (row.security && !App.hasAccessTo(row.security)) {
          continue;
        }

        if (!App.isOnline() && !row.enableOfflineSupport) {
          continue;
        }

        if (typeof this.query !== 'function' || this.query(row)) {
          list.push(row);
        }
      }
    }

    const store = new Memory({
      data: list,
    });
    store.idProperty = '$key';
    return store;
  },
  /**
   * Override the List refresh to also clear the view (something the beforeTransitionTo handles, but we are not using)
   */
  refresh: function refresh() {
    this.clear();
    this.requestData();
    if (this.searchWidget) {
      if (App.onLine) {
        this.searchWidget.enable();
      } else {
        this.searchWidget.disable();
      }
    }
  },
  clear: function clear() {
    this.inherited(arguments);
    this.layout = null;
    this.store = null;
  },
  show: function show() {
    if (this.onShow(this) === false) {
      return;
    }

    this.refresh();
  },
  refreshRequiredFor: function refreshRequiredFor() {
    const visible = lang.getObject('preferences.home.visible', false, App) || [];
    const shown = this.feed && this.feed.$resources;

    if (!visible || !shown || (visible.length !== shown.length)) {
      return true;
    }

    for (let i = 0; i < visible.length; i++) {
      if (visible[i] !== shown[i].$key) {
        return true;
      }
    }

    return this.inherited(arguments);
  },
  _onRegistered: function _onRegistered() {
    this.refreshRequired = true;
  },
  _onSearchExpression: function _onSearchExpression(expression) {
    const view = App.getView(this.searchView);
    const current = App.getPrimaryActiveView();

    if (view) {
      // If the speedsearch list is not our current view, show it first
      if (view.id !== current.id) {
        view.show({
          query: expression,
        });
      }

      // Set the search term on the list and call search.
      // This will keep the search terms on each widget in sync.
      setTimeout(() => {
        view.setSearchTerm(expression);
        if (current && current.id === view.id) {
          view.search();
        }
      }, 10);
    }

    App.snapper.close();
  },
});

lang.setObject('Mobile.SalesLogix.Views.LeftDrawer', __class);
export default __class;
<commit_msg>INFORCRM-5735: Fix speedsearch widget in left drawer.<commit_after>import declare from 'dojo/_base/declare';
import array from 'dojo/_base/array';
import lang from 'dojo/_base/lang';
import Memory from 'dojo/store/Memory';
import SpeedSearchWidget from '../SpeedSearchWidget';
import string from 'dojo/string';
import GroupedList from 'argos/GroupedList';

const resource = window.localeContext.getEntitySync('leftDrawer').attributes;

/**
 * @class crm.Views.LeftDrawer
 *
 *
 * @extends argos.GroupedList
 *
 */
const __class = declare('crm.Views.LeftDrawer', [GroupedList], {
  // Templates
  cls: ' contextualContent',
  enablePullToRefresh: false,
  rowTemplate: new Simplate([
    '
',
    '{% if ($$._hasIcon($)) { %}',
    '
',
    '{% if ($.iconTemplate) { %}',
    '{%! $.iconTemplate %}',
    '{% } else if ($.cls) { %}',
    '

',
    '{% } else if ($.icon) { %}',
    '',
    '{% } %}',
    '
',
    '{% } %}',
    '
{%! $$.itemTemplate %}
',
    '
',
  ]),
  _hasIcon: function _hasIcon(entry) {
    return entry.iconTemplate || entry.cls || entry.icon;
  },
  itemTemplate: new Simplate([
    '
{%: $.title %}
',
  ]),

  // Localization
  configureText: resource.configureText,
  addAccountContactText: resource.addAccountContactText,
  titleText: resource.titleText,
  actionsText: resource.actionsText,
  viewsText: resource.viewsText,
  footerText: resource.footerText,
  settingsText: resource.settingsText,
  helpText: resource.helpText,
  logOutText: resource.logOutText,
  logOutConfirmText: resource.logOutConfirmText,
  onlineText: resource.onlineText,
  offlineText: resource.offlineText,
  connectionText: resource.connectionText,

  // View Properties
  id: 'left_drawer',
  expose: false,
  enableSearch: true,
  searchWidgetClass: SpeedSearchWidget,
  customizationSet: 'left_drawer',

  settingsView: 'settings',
  helpView: 'help',
  configurationView: 'configure',
  addAccountContactView: 'add_account_contact',
  searchView: 'speedsearch_list',

  logOut: function logOut() {
    const sure = window.confirm(this.logOutConfirmText); // eslint-disable-line
    if (sure) {
      App.logOut();
    }
  },
  loadAndNavigateToView: function loadAndNavigateToView(params) {
    const view = App.getView(params && params.view);
    this.navigateToView(view);
  },
  navigateToView: function navigateToView(view) {
    App.snapper.close();
    if (view) {
      view.show();
    }
  },
  addAccountContact: function addAccountContact() {
    App.snapper.close();
    const view = App.getView('add_account_contact');
    if (view) {
      view.show({
        insert: true,
      });
    }
  },
  navigateToConfigurationView: function navigateToConfigurationView() {
    const view = App.getView(this.configurationView);
    this.navigateToView(view);
  },
  navigateToSettingsView: function navigateToSettingsView() {
    const view = App.getView(this.settingsView);
    this.navigateToView(view);
  },
  navigateToHelpView: function navigateToHelpView() {
    const view = App.getView(this.helpView);
    this.navigateToView(view);
  },
  hasMoreData: function hasMoreData() {
    return false;
  },
  getGroupForEntry: function getGroupForEntry(entry) {
    const footerItems = [
      'ConfigureMenu',
      'SettingsAction',
      'HelpAction',
      'Logout',
      'ConnectionIndicator',
    ];

    if (entry.view) {
      return {
        tag: 'view',
        title: this.viewsText,
      };
    }

    if (array.indexOf(footerItems, entry.name) >= 0) {
      return {
        tag: 'footer',
        title: this.footerText,
      };
    }

    return {
      tag: 'action',
      title: this.actionsText,
    };
  },
  init: function init() {
    this.inherited(arguments);
    this.connect(App, 'onRegistered', this._onRegistered);
  },
  createLayout: function createLayout() {
    if (this.layout) {
      return this.layout;
    }

    const layout = [];

    const quickActions = {
      id: 'actions',
      children: [{
        'name': 'AddAccountContactAction',
        'action': 'addAccountContact',
        'title': this.addAccountContactText,
      }],
    };

    layout.push(quickActions);

    const goTo = {
      id: 'views',
      children: [],
    };

    const configured = lang.getObject('preferences.home.visible', false, window.App);
    for (let i = 0; i < configured.length; i++) {
      const view = App.getView(configured[i]);
      if (view) {
        goTo.children.push({
          'action': 'loadAndNavigateToView',
          'view': view.id,
          'title': view.titleText,
          'security': view.getSecurity(),
          'enableOfflineSupport': view.enableOfflineSupport,
        });
      }
    }

    layout.push(goTo);

    const footer = {
      id: 'footer',
      children: [{
        'name': 'ConfigureMenu',
        'action': 'navigateToConfigurationView',
        'title': this.configureText,
        'enableOfflineSupport': false,
      }, {
        'name': 'SettingsAction',
        'action': 'navigateToSettingsView',
        'title': this.settingsText,
        'enableOfflineSupport': true,
      }, {
        'name': 'HelpAction',
        'action': 'navigateToHelpView',
        'title': this.helpText,
        'enableOfflineSupport': true,
      }, {
        'name': 'Logout',
        'action': 'logOut',
        'title': this.logOutText,
        'enableOfflineSupport': false,
      }, {
        'name': 'ConnectionIndicator',
        'title': string.substitute(this.connectionText, {connectionStatus: App.onLine ? this.onlineText : this.offlineText}),
        'enableOfflineSupport': true,
      }],
    };

    layout.push(footer);

    return layout;
  },
  createStore: function createStore() {
    const layout = this._createCustomizedLayout(this.createLayout());
    const list = [];
    let total = 0;
    let section;

    for (let i = 0; i < layout.length; i++) {
      section = layout[i].children;

      for (let j = 0; j < section.length; j++) {
        total = total + 1;
        const row = section[j];
        row.$key = total;

        if (row.security && !App.hasAccessTo(row.security)) {
          continue;
        }

        if (!App.isOnline() && !row.enableOfflineSupport) {
          continue;
        }

        if (typeof this.query !== 'function' || this.query(row)) {
          list.push(row);
        }
      }
    }

    const store = new Memory({
      data: list,
    });
    store.idProperty = '$key';
    return store;
  },
  /**
   * Override the List refresh to also clear the view (something the beforeTransitionTo handles, but we are not using)
   */
  refresh: function refresh() {
    this.clear();
    this.requestData();
    if (this.searchWidget) {
      if (App.onLine) {
        this.searchWidget.enable();
      } else {
        this.searchWidget.disable();
      }
    }
  },
  clear: function clear() {
    this.inherited(arguments);
    this.layout = null;
    this.store = null;
  },
  show: function show() {
    if (this.onShow(this) === false) {
      return;
    }

    this.refresh();
  },
  refreshRequiredFor: function refreshRequiredFor() {
    const visible = lang.getObject('preferences.home.visible', false, App) || [];
    const shown = this.feed && this.feed.$resources;

    if (!visible || !shown || (visible.length !== shown.length)) {
      return true;
    }

    for (let i = 0; i < visible.length; i++) {
      if (visible[i] !== shown[i].$key) {
        return true;
      }
    }

    return this.inherited(arguments);
  },
  _onRegistered: function _onRegistered() {
    this.refreshRequired = true;
  },
  _onSearchExpression: function _onSearchExpression(expression) {
    const view = App.getView(this.searchView);
    const current = App.getPrimaryActiveView();

    if (view) {
      // If the speedsearch list is not our current view, show it first
      if (view.id !== current.id) {
        view.show({
          query: expression,
        });
      }

      // Set the search term on the list and call search.
      // This will keep the search terms on each widget in sync.
      setTimeout(() => {
        view.setSearchTerm(expression);
        if (current && current.id === view.id) {
          view.search();
        }
      }, 10);
    }

    App.snapper.close();
  },
});

lang.setObject('Mobile.SalesLogix.Views.LeftDrawer', __class);
export default __class;
<|endoftext|>


<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Responsive presentation and behavior for HTML data panels
//>>label: Panel
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.panel.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../jquery.mobile.widget", "./page", "./page.sections" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$.widget( "mobile.panel", $.mobile.widget, {
	options: {
		classes: {
			panel: "ui-panel",
			panelOpen: "ui-panel-open",
			panelClosed: "ui-panel-closed",
			panelFixed: "ui-panel-fixed",
			panelInner: "ui-panel-inner",
			modal: "ui-panel-dismiss",
			modalOpen: "ui-panel-dismiss-open",
			pagePanel: "ui-page-panel",
			pagePanelOpen: "ui-page-panel-open",
			contentWrap: "ui-panel-content-wrap",
			contentWrapOpen: "ui-panel-content-wrap-open",
			contentWrapClosed: "ui-panel-content-wrap-closed",
			contentFixedToolbar: "ui-panel-content-fixed-toolbar",
			contentFixedToolbarOpen: "ui-panel-content-fixed-toolbar-open",
			contentFixedToolbarClosed: "ui-panel-content-fixed-toolbar-closed",
			animate: "ui-panel-animate"
		},
		animate: true,
		theme: null,
		position: "left",
		dismissible: true,
		display: "reveal", //accepts reveal, push, overlay
		initSelector: ":jqmData(role='panel')",
		swipeClose: true,
		positionFixed: false
	},

	_panelID: null,
	_closeLink: null,
	_page: null,
	_modal: null,
	_wrapper: null,
	_fixedToolbar: null,

	_create: function() {
		var self = this,
			$el = self.element,
			page = $el.closest( ":jqmData(role='page')" ),
			_getPageTheme = function() {
				var $theme = $.data( page[0], "mobilePage" ).options.theme,
				$pageThemeClass = "ui-body-" + $theme;
				return $pageThemeClass;
			},
			_getPanelInner = function() {
				var $pannelInner = $el.find( "." + self.options.classes.panelInner );
				if ( $pannelInner.length === 0 ) {
					$pannelInner = $el.children().wrapAll( '
' ).parent();
				}
				return $pannelInner;
			},
			_getWrapper = function() {
				var $wrapper = page.find( "." + self.options.classes.contentWrap );
				if ( $wrapper.length === 0 ) {
					$wrapper = page.children( ".ui-header:not(:jqmData(position='fixed')), .ui-content:not(.ui-popup), .ui-footer:not(:jqmData(position='fixed'))" ).wrapAll( '
' ).parent();
					if ( $.support.cssTransform3d && !!self.options.animate ) {
						$wrapper.addClass( self.options.classes.animate );
					}
				}
				return $wrapper;
			},
			_getFixedToolbar = function() {
				var $fixedToolbar = page.find( "." + self.options.classes.contentFixedToolbar );
				if ( $fixedToolbar.length === 0 ) {
					$fixedToolbar = page.find( ".ui-header:jqmData(position='fixed'), .ui-footer:jqmData(position='fixed')" ).addClass( self.options.classes.contentFixedToolbar );
					if ( $.support.cssTransform3d && !!self.options.animate ) {
						$fixedToolbar.addClass( self.options.classes.animate );
					}
				}
				return $fixedToolbar;
			};

		// expose some private props to other methods
		$.extend( this, {
			_panelID: $el.attr( "id" ),
			_closeLink: $el.find( ":jqmData(rel='close')" ),
			_page: $el.closest( ":jqmData(role='page')" ),
			_pageTheme: _getPageTheme(),
			_pannelInner: _getPanelInner(),
			_wrapper: _getWrapper(),
			_fixedToolbar: _getFixedToolbar()
		});

		self._addPanelClasses();
		self._wrapper.addClass( this.options.classes.contentWrapClosed );
		self._fixedToolbar.addClass( this.options.classes.contentFixedToolbarClosed );
		// add class to page so we can set "overflow-x: hidden;" for it to fix Android zoom issue
		self._page.addClass( self.options.classes.pagePanel );

		// if animating, add the class to do so
		if ( $.support.cssTransform3d && !!self.options.animate ) {
			this.element.addClass( self.options.classes.animate );
		}

		self._bindCloseEvents();
		self._bindLinkListeners();
		self._bindPageEvents();

		if ( self.options.dismissible ) {
			self._createModal();
		}
	},

	_createModal: function( options ) {
		var self = this;
		self._modal = $( "

" )
			.on( "mousedown" , function() {
				self.close();
			})
			.appendTo( this._page );
	},

	_getPosDisplayClasses: function( prefix ) {
		return prefix + "-position-" + this.options.position + " " + prefix + "-display-" + this.options.display;
	},

	_getPanelClasses: function() {
		var panelClasses = this.options.classes.panel +
			" " + this._getPosDisplayClasses( this.options.classes.panel ) +
			" " + this.options.classes.panelClosed;

		if ( this.options.theme ) {
			panelClasses += " ui-body-" + this.options.theme;
		}
		if ( !!this.options.positionFixed ) {
			panelClasses += " " + this.options.classes.panelFixed;
		}
		return panelClasses;
	},

	_addPanelClasses: function() {
		this.element.addClass( this._getPanelClasses() );
	},

	_bindCloseEvents: function() {
		var self = this;
		self._closeLink.on( "click.panel" , function( e ) {
			e.preventDefault();
			self.close();
			return false;
		});
	},

	_positionPanel: function() {
		var self = this,
			pannelInnerHeight = self._pannelInner.outerHeight(),
			expand = pannelInnerHeight > $.mobile.getScreenHeight();

		if ( expand || !self.options.positionFixed ) {
			if ( expand ) {
				self._unfixPanel();
				$.mobile.resetActivePageHeight( pannelInnerHeight );
			}
			self._scrollIntoView( pannelInnerHeight );
		} else {
			self._fixPanel();
		}
	},

	_scrollIntoView: function( pannelInnerHeight ) {
		if ( pannelInnerHeight < $( window ).scrollTop() ) {
			window.scrollTo( 0, 0 );
		}
	},

	_bindFixListener: function() {
		this._on( $( window ), { "throttledresize": "_positionPanel" });
	},

	_unbindFixListener: function() {
		this._off( $( window ), "throttledresize" );
	},

	_unfixPanel: function() {
		if ( !!this.options.positionFixed && $.support.fixedPosition ) {
			this.element.removeClass( this.options.classes.panelFixed );
		}
	},

	_fixPanel: function() {
		if ( !!this.options.positionFixed && $.support.fixedPosition ) {
			this.element.addClass( this.options.classes.panelFixed );
		}
	},

	_bindLinkListeners: function() {
		var self = this;

		this._page.on( "click.panel" , "a", function( e ) {
			if ( this.href.split( "#" )[ 1 ] === self._panelID && self._panelID !== undefined ) {
				e.preventDefault();
				var $link = $( this );
				$link.addClass( $.mobile.activeBtnClass );
				self.element.one( "panelopen panelclose", function() {
					$link.removeClass( $.mobile.activeBtnClass );
				});
				self.toggle();
				return false;
			}
		});
	},

	_bindPageEvents: function() {
		var self = this;
		if( !!this.options.swipeClose ){
			self.element
				// on swipe, close the panel (should swipe open too?)
				.on( "swipe.panel" , function( e ){
					self.close( );
				});
		}
		self._page
			// Close immediately if another panel on the page opens
			.on( "panelbeforeopen", function( e ) {
				if ( self._open && e.target !== self.element[ 0 ] ) {
					self.close( true );
				}
			})
			// clean up open panels after page hide
			.on(  "pagebeforehide", function( e ) {
				if ( self._open ) {
					self.close( true );
				}
			})
			// on escape, close? might need to have a target check too...
			.on( "keyup.panel", function( e ) {
				if( e.keyCode === 27 && self._open ){
					self.close( );
				}
			});
	},

	// state storage of open or closed
	_open: false,

	_contentWrapOpenClasses: null,
	_fixedToolbarOpenClasses: null,
	_modalOpenClasses: null,

	open: function( immediate ) {
		if ( !this._open ) {
			var self = this,
				o = self.options,
				complete = function() {
					self.element.add( self._wrapper ).add( self._fixedToolbar ).off( self._transitionEndEvents, complete );
					self._page.addClass( o.classes.pagePanelOpen );
					self._positionPanel();
					self._bindFixListener();
					self._trigger( "open" );
				};

			if ( this.element.closest( ".ui-page-active" ).length < 0 ) {
				immediate = true;
			}
			self._trigger( "beforeopen" );

			if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
				self.element.add( self._wrapper ).add( self._fixedToolbar ).on( self._transitionEndEvents, complete );
			} else {
				setTimeout( complete, 0 );
			}
			if ( self.options.theme ) {
				self._page.removeClass( self._pageTheme ).addClass( "ui-body-" + self.options.theme );
			}
			self.element.removeClass( o.classes.panelClosed );
			self.element.addClass( o.classes.panelOpen );
			self._contentWrapOpenClasses = self._getPosDisplayClasses( o.classes.contentWrap );
			self._wrapper.removeClass( o.classes.contentWrapClosed );
			self._wrapper.addClass( self._contentWrapOpenClasses + " " + o.classes.contentWrapOpen );
			self._fixedToolbarOpenClasses = self._getPosDisplayClasses( o.classes.contentFixedToolbar );
			self._fixedToolbar.removeClass( o.classes.contentFixedToolbarClosed );
			self._fixedToolbar.addClass( self._fixedToolbarOpenClasses + " " + o.classes.contentFixedToolbarOpen );
			self._modalOpenClasses = self._getPosDisplayClasses( o.classes.modal ) + " " + o.classes.modalOpen;
			if ( this._modal ) {
				self._modal.addClass( self._modalOpenClasses );
			}
			self._open = true;
		}
	},

	close: function( immediate ) {
		if ( this._open ) {
			var o = this.options,
				self = this,
				complete = function() {
					if ( self.options.theme ) {
						self._page.removeClass( "ui-body-" + self.options.theme ).addClass( self._pageTheme );
					}
					self.element.add( self._wrapper ).add( self._fixedToolbar ).off( self._transitionEndEvents, complete );
					self.element.addClass( o.classes.panelClosed );
					self._wrapper.removeClass( self._contentWrapOpenClasses );
					self._wrapper.addClass( o.classes.contentWrapClosed );
					self._fixedToolbar.removeClass( self._fixedToolbarOpenClasses );
					self._fixedToolbar.addClass( o.classes.contentFixedToolbarClosed );
					self._page.removeClass( o.classes.pagePanelOpen );
					self._fixPanel();
					self._unbindFixListener();
					$.mobile.resetActivePageHeight();
					self._trigger( "close" );
				};
			if ( this.element.closest( ".ui-page-active" ).length < 0 ) {
				immediate = true;
			}
			self._trigger( "beforeclose" );

			if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
				self.element.add( self._wrapper ).add( self._fixedToolbar ).on( self._transitionEndEvents, complete );
			} else{
				setTimeout( complete, 0 );
			}

			self.element.removeClass( o.classes.panelOpen );
			if ( this._modal ) {
				self._modal.removeClass( self._modalOpenClasses );
			}
			self._wrapper.removeClass( o.classes.contentWrapOpen );
			self._fixedToolbar.removeClass( o.classes.contentFixedToolbarOpen );

			self._open = false;
		}
	},

	toggle: function( options ) {
		this[ this._open ? "close" : "open" ]();
	},

	_transitionEndEvents: "webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",

	_destroy: function() {
		var classes = this.options.classes,
			theme = this.options.theme,
			hasOtherSiblingPanels = this.element.siblings( "." + classes.panel ).length;

		// create
		if ( !hasOtherSiblingPanels ) {
			this._wrapper.children().unwrap();
			this._page.find( "a" ).unbind( "panelopen panelclose" );
			this._page.removeClass( classes.pagePanel );
			if ( this._open ) {
				this._page.removeClass( classes.pagePanelOpen );
				if ( theme ) {
					this._page.removeClass( "ui-body-" + theme ).addClass( this._pageTheme );
				}
				$.mobile.resetActivePageHeight();
			}
		} else if ( this._open ) {
			this._wrapper.removeClass( classes.contentWrapOpen );
			this._fixedToolbar.removeClass( classes.contentFixedToolbarOpen );
			this._page.removeClass( classes.pagePanelOpen );
			if ( theme ) {
				this._page.removeClass( "ui-body-" + theme ).addClass( this._pageTheme );
			}
		}

		this._pannelInner.children().unwrap();

		this.element.removeClass( [ this._getPanelClasses(), classes.panelAnimate ].join( " " ) )
			.off( "swipe.panel" )
			.off( "panelbeforeopen" )
			.off( "panelbeforehide" )
			.off( "keyup.panel" );

		this._closeLink.off( "click.panel" );

		if ( this._modal ) {
			this._modal.remove();
		}

		// open and close
		this.element.off( this._transitionEndEvents )
			.removeClass( [ classes.panelUnfixed, classes.panelClosed, classes.panelOpen ].join( " " ) );
	}
});

//auto self-init widgets
$( document ).bind( "pagecreate create", function( e ) {
	$.mobile.panel.prototype.enhanceWithin( e.target );
});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>Panel: changed event listener. Fixed #5480 - Panel should close after, and not during, page animation<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Responsive presentation and behavior for HTML data panels
//>>label: Panel
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.panel.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../jquery.mobile.widget", "./page", "./page.sections" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$.widget( "mobile.panel", $.mobile.widget, {
	options: {
		classes: {
			panel: "ui-panel",
			panelOpen: "ui-panel-open",
			panelClosed: "ui-panel-closed",
			panelFixed: "ui-panel-fixed",
			panelInner: "ui-panel-inner",
			modal: "ui-panel-dismiss",
			modalOpen: "ui-panel-dismiss-open",
			pagePanel: "ui-page-panel",
			pagePanelOpen: "ui-page-panel-open",
			contentWrap: "ui-panel-content-wrap",
			contentWrapOpen: "ui-panel-content-wrap-open",
			contentWrapClosed: "ui-panel-content-wrap-closed",
			contentFixedToolbar: "ui-panel-content-fixed-toolbar",
			contentFixedToolbarOpen: "ui-panel-content-fixed-toolbar-open",
			contentFixedToolbarClosed: "ui-panel-content-fixed-toolbar-closed",
			animate: "ui-panel-animate"
		},
		animate: true,
		theme: null,
		position: "left",
		dismissible: true,
		display: "reveal", //accepts reveal, push, overlay
		initSelector: ":jqmData(role='panel')",
		swipeClose: true,
		positionFixed: false
	},

	_panelID: null,
	_closeLink: null,
	_page: null,
	_modal: null,
	_wrapper: null,
	_fixedToolbar: null,

	_create: function() {
		var self = this,
			$el = self.element,
			page = $el.closest( ":jqmData(role='page')" ),
			_getPageTheme = function() {
				var $theme = $.data( page[0], "mobilePage" ).options.theme,
				$pageThemeClass = "ui-body-" + $theme;
				return $pageThemeClass;
			},
			_getPanelInner = function() {
				var $pannelInner = $el.find( "." + self.options.classes.panelInner );
				if ( $pannelInner.length === 0 ) {
					$pannelInner = $el.children().wrapAll( '
' ).parent();
				}
				return $pannelInner;
			},
			_getWrapper = function() {
				var $wrapper = page.find( "." + self.options.classes.contentWrap );
				if ( $wrapper.length === 0 ) {
					$wrapper = page.children( ".ui-header:not(:jqmData(position='fixed')), .ui-content:not(.ui-popup), .ui-footer:not(:jqmData(position='fixed'))" ).wrapAll( '
' ).parent();
					if ( $.support.cssTransform3d && !!self.options.animate ) {
						$wrapper.addClass( self.options.classes.animate );
					}
				}
				return $wrapper;
			},
			_getFixedToolbar = function() {
				var $fixedToolbar = page.find( "." + self.options.classes.contentFixedToolbar );
				if ( $fixedToolbar.length === 0 ) {
					$fixedToolbar = page.find( ".ui-header:jqmData(position='fixed'), .ui-footer:jqmData(position='fixed')" ).addClass( self.options.classes.contentFixedToolbar );
					if ( $.support.cssTransform3d && !!self.options.animate ) {
						$fixedToolbar.addClass( self.options.classes.animate );
					}
				}
				return $fixedToolbar;
			};

		// expose some private props to other methods
		$.extend( this, {
			_panelID: $el.attr( "id" ),
			_closeLink: $el.find( ":jqmData(rel='close')" ),
			_page: $el.closest( ":jqmData(role='page')" ),
			_pageTheme: _getPageTheme(),
			_pannelInner: _getPanelInner(),
			_wrapper: _getWrapper(),
			_fixedToolbar: _getFixedToolbar()
		});

		self._addPanelClasses();
		self._wrapper.addClass( this.options.classes.contentWrapClosed );
		self._fixedToolbar.addClass( this.options.classes.contentFixedToolbarClosed );
		// add class to page so we can set "overflow-x: hidden;" for it to fix Android zoom issue
		self._page.addClass( self.options.classes.pagePanel );

		// if animating, add the class to do so
		if ( $.support.cssTransform3d && !!self.options.animate ) {
			this.element.addClass( self.options.classes.animate );
		}

		self._bindCloseEvents();
		self._bindLinkListeners();
		self._bindPageEvents();

		if ( self.options.dismissible ) {
			self._createModal();
		}
	},

	_createModal: function( options ) {
		var self = this;
		self._modal = $( "

" )
			.on( "mousedown" , function() {
				self.close();
			})
			.appendTo( this._page );
	},

	_getPosDisplayClasses: function( prefix ) {
		return prefix + "-position-" + this.options.position + " " + prefix + "-display-" + this.options.display;
	},

	_getPanelClasses: function() {
		var panelClasses = this.options.classes.panel +
			" " + this._getPosDisplayClasses( this.options.classes.panel ) +
			" " + this.options.classes.panelClosed;

		if ( this.options.theme ) {
			panelClasses += " ui-body-" + this.options.theme;
		}
		if ( !!this.options.positionFixed ) {
			panelClasses += " " + this.options.classes.panelFixed;
		}
		return panelClasses;
	},

	_addPanelClasses: function() {
		this.element.addClass( this._getPanelClasses() );
	},

	_bindCloseEvents: function() {
		var self = this;
		self._closeLink.on( "click.panel" , function( e ) {
			e.preventDefault();
			self.close();
			return false;
		});
	},

	_positionPanel: function() {
		var self = this,
			pannelInnerHeight = self._pannelInner.outerHeight(),
			expand = pannelInnerHeight > $.mobile.getScreenHeight();

		if ( expand || !self.options.positionFixed ) {
			if ( expand ) {
				self._unfixPanel();
				$.mobile.resetActivePageHeight( pannelInnerHeight );
			}
			self._scrollIntoView( pannelInnerHeight );
		} else {
			self._fixPanel();
		}
	},

	_scrollIntoView: function( pannelInnerHeight ) {
		if ( pannelInnerHeight < $( window ).scrollTop() ) {
			window.scrollTo( 0, 0 );
		}
	},

	_bindFixListener: function() {
		this._on( $( window ), { "throttledresize": "_positionPanel" });
	},

	_unbindFixListener: function() {
		this._off( $( window ), "throttledresize" );
	},

	_unfixPanel: function() {
		if ( !!this.options.positionFixed && $.support.fixedPosition ) {
			this.element.removeClass( this.options.classes.panelFixed );
		}
	},

	_fixPanel: function() {
		if ( !!this.options.positionFixed && $.support.fixedPosition ) {
			this.element.addClass( this.options.classes.panelFixed );
		}
	},

	_bindLinkListeners: function() {
		var self = this;

		this._page.on( "click.panel" , "a", function( e ) {
			if ( this.href.split( "#" )[ 1 ] === self._panelID && self._panelID !== undefined ) {
				e.preventDefault();
				var $link = $( this );
				$link.addClass( $.mobile.activeBtnClass );
				self.element.one( "panelopen panelclose", function() {
					$link.removeClass( $.mobile.activeBtnClass );
				});
				self.toggle();
				return false;
			}
		});
	},

	_bindPageEvents: function() {
		var self = this;
		if( !!this.options.swipeClose ){
			self.element
				// on swipe, close the panel (should swipe open too?)
				.on( "swipe.panel" , function( e ){
					self.close( );
				});
		}
		self._page
			// Close immediately if another panel on the page opens
			.on( "panelbeforeopen", function( e ) {
				if ( self._open && e.target !== self.element[ 0 ] ) {
					self.close( true );
				}
			})
			// clean up open panels after page hide
			.on(  "pagehide", function( e ) {
				if ( self._open ) {
					self.close( true );
				}
			})
			// on escape, close? might need to have a target check too...
			.on( "keyup.panel", function( e ) {
				if( e.keyCode === 27 && self._open ){
					self.close( );
				}
			});
	},

	// state storage of open or closed
	_open: false,

	_contentWrapOpenClasses: null,
	_fixedToolbarOpenClasses: null,
	_modalOpenClasses: null,

	open: function( immediate ) {
		if ( !this._open ) {
			var self = this,
				o = self.options,
				complete = function() {
					self.element.add( self._wrapper ).add( self._fixedToolbar ).off( self._transitionEndEvents, complete );
					self._page.addClass( o.classes.pagePanelOpen );
					self._positionPanel();
					self._bindFixListener();
					self._trigger( "open" );
				};

			if ( this.element.closest( ".ui-page-active" ).length < 0 ) {
				immediate = true;
			}
			self._trigger( "beforeopen" );

			if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
				self.element.add( self._wrapper ).add( self._fixedToolbar ).on( self._transitionEndEvents, complete );
			} else {
				setTimeout( complete, 0 );
			}
			if ( self.options.theme ) {
				self._page.removeClass( self._pageTheme ).addClass( "ui-body-" + self.options.theme );
			}
			self.element.removeClass( o.classes.panelClosed );
			self.element.addClass( o.classes.panelOpen );
			self._contentWrapOpenClasses = self._getPosDisplayClasses( o.classes.contentWrap );
			self._wrapper.removeClass( o.classes.contentWrapClosed );
			self._wrapper.addClass( self._contentWrapOpenClasses + " " + o.classes.contentWrapOpen );
			self._fixedToolbarOpenClasses = self._getPosDisplayClasses( o.classes.contentFixedToolbar );
			self._fixedToolbar.removeClass( o.classes.contentFixedToolbarClosed );
			self._fixedToolbar.addClass( self._fixedToolbarOpenClasses + " " + o.classes.contentFixedToolbarOpen );
			self._modalOpenClasses = self._getPosDisplayClasses( o.classes.modal ) + " " + o.classes.modalOpen;
			if ( this._modal ) {
				self._modal.addClass( self._modalOpenClasses );
			}
			self._open = true;
		}
	},

	close: function( immediate ) {
		if ( this._open ) {
			var o = this.options,
				self = this,
				complete = function() {
					if ( self.options.theme ) {
						self._page.removeClass( "ui-body-" + self.options.theme ).addClass( self._pageTheme );
					}
					self.element.add( self._wrapper ).add( self._fixedToolbar ).off( self._transitionEndEvents, complete );
					self.element.addClass( o.classes.panelClosed );
					self._wrapper.removeClass( self._contentWrapOpenClasses );
					self._wrapper.addClass( o.classes.contentWrapClosed );
					self._fixedToolbar.removeClass( self._fixedToolbarOpenClasses );
					self._fixedToolbar.addClass( o.classes.contentFixedToolbarClosed );
					self._page.removeClass( o.classes.pagePanelOpen );
					self._fixPanel();
					self._unbindFixListener();
					$.mobile.resetActivePageHeight();
					self._trigger( "close" );
				};
			if ( this.element.closest( ".ui-page-active" ).length < 0 ) {
				immediate = true;
			}
			self._trigger( "beforeclose" );

			if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
				self.element.add( self._wrapper ).add( self._fixedToolbar ).on( self._transitionEndEvents, complete );
			} else{
				setTimeout( complete, 0 );
			}

			self.element.removeClass( o.classes.panelOpen );
			if ( this._modal ) {
				self._modal.removeClass( self._modalOpenClasses );
			}
			self._wrapper.removeClass( o.classes.contentWrapOpen );
			self._fixedToolbar.removeClass( o.classes.contentFixedToolbarOpen );

			self._open = false;
		}
	},

	toggle: function( options ) {
		this[ this._open ? "close" : "open" ]();
	},

	_transitionEndEvents: "webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",

	_destroy: function() {
		var classes = this.options.classes,
			theme = this.options.theme,
			hasOtherSiblingPanels = this.element.siblings( "." + classes.panel ).length;

		// create
		if ( !hasOtherSiblingPanels ) {
			this._wrapper.children().unwrap();
			this._page.find( "a" ).unbind( "panelopen panelclose" );
			this._page.removeClass( classes.pagePanel );
			if ( this._open ) {
				this._page.removeClass( classes.pagePanelOpen );
				if ( theme ) {
					this._page.removeClass( "ui-body-" + theme ).addClass( this._pageTheme );
				}
				$.mobile.resetActivePageHeight();
			}
		} else if ( this._open ) {
			this._wrapper.removeClass( classes.contentWrapOpen );
			this._fixedToolbar.removeClass( classes.contentFixedToolbarOpen );
			this._page.removeClass( classes.pagePanelOpen );
			if ( theme ) {
				this._page.removeClass( "ui-body-" + theme ).addClass( this._pageTheme );
			}
		}

		this._pannelInner.children().unwrap();

		this.element.removeClass( [ this._getPanelClasses(), classes.panelAnimate ].join( " " ) )
			.off( "swipe.panel" )
			.off( "panelbeforeopen" )
			.off( "panelbeforehide" )
			.off( "keyup.panel" );

		this._closeLink.off( "click.panel" );

		if ( this._modal ) {
			this._modal.remove();
		}

		// open and close
		this.element.off( this._transitionEndEvents )
			.removeClass( [ classes.panelUnfixed, classes.panelClosed, classes.panelOpen ].join( " " ) );
	}
});

//auto self-init widgets
$( document ).bind( "pagecreate create", function( e ) {
	$.mobile.panel.prototype.enhanceWithin( e.target );
});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>/* global Zepto:true */
/* global WebKitCSSMatrix:true */

(function($) {
     "use strict";
    ['width', 'height'].forEach(function(dimension) {
        var  Dimension = dimension.replace(/./, function(m) {
            return m[0].toUpperCase();
        });
        $.fn['outer' + Dimension] = function(margin) {
            var elem = this;
            if (elem) {
                var size = elem[dimension]();
                var sides = {
                    'width': ['left', 'right'],
                    'height': ['top', 'bottom']
                };
                sides[dimension].forEach(function(side) {
                    if (margin) size += parseInt(elem.css('margin-' + side), 10);
                });
                return size;
            } else {
                return null;
            }
        };
    });

    //support
    $.support = (function() {
        var support = {
            touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch)
        };
        return support;
    })();

    $.touchEvents = {
        start: $.support.touch ? 'touchstart' : 'mousedown',
        move: $.support.touch ? 'touchmove' : 'mousemove',
        end: $.support.touch ? 'touchend' : 'mouseup'
    };

    $.getTranslate = function (el, axis) {
      var matrix, curTransform, curStyle, transformMatrix;

      // automatic axis detection
      if (typeof axis === 'undefined') {
        axis = 'x';
      }

      curStyle = window.getComputedStyle(el, null);
      if (window.WebKitCSSMatrix) {
        // Some old versions of Webkit choke when 'none' is passed; pass
        // empty string instead in this case
        transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
      }
      else {
        transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform  || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
        matrix = transformMatrix.toString().split(',');
      }

      if (axis === 'x') {
        //Latest Chrome and webkits Fix
        if (window.WebKitCSSMatrix)
          curTransform = transformMatrix.m41;
        //Crazy IE10 Matrix
        else if (matrix.length === 16)
          curTransform = parseFloat(matrix[12]);
        //Normal Browsers
        else
          curTransform = parseFloat(matrix[4]);
      }
      if (axis === 'y') {
        //Latest Chrome and webkits Fix
        if (window.WebKitCSSMatrix)
          curTransform = transformMatrix.m42;
        //Crazy IE10 Matrix
        else if (matrix.length === 16)
          curTransform = parseFloat(matrix[13]);
        //Normal Browsers
        else
          curTransform = parseFloat(matrix[5]);
      }

      return curTransform || 0;
    };
    $.requestAnimationFrame = function (callback) {
      if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
      else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
      else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
      else {
        return window.setTimeout(callback, 1000 / 60);
      }
    };

    $.cancelAnimationFrame = function (id) {
      if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id);
      else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id);
      else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id);
      else {
        return window.clearTimeout(id);
      }
    };

    $.fn.transitionEnd = function(callback) {
        var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],
            i, dom = this;

        function fireCallBack(e) {
            /*jshint validthis:true */
            if (e.target !== this) return;
            callback.call(this, e);
            for (i = 0; i < events.length; i++) {
                dom.off(events[i], fireCallBack);
            }
        }
        if (callback) {
            for (i = 0; i < events.length; i++) {
                dom.on(events[i], fireCallBack);
            }
        }
        return this;
    };
    $.fn.dataset = function() {
        var el = this[0];
        if (el) {
            var dataset = {};
            if (el.dataset) {

                for (var dataKey in el.dataset) { // jshint ignore:line
                    dataset[dataKey] = el.dataset[dataKey];
                }
            } else {
                for (var i = 0; i < el.attributes.length; i++) {
                    var attr = el.attributes[i];
                    if (attr.name.indexOf('data-') >= 0) {
                        dataset[$.toCamelCase(attr.name.split('data-')[1])] = attr.value;
                    }
                }
            }
            for (var key in dataset) {
                if (dataset[key] === 'false') dataset[key] = false;
                else if (dataset[key] === 'true') dataset[key] = true;
                else if (parseFloat(dataset[key]) === dataset[key] * 1) dataset[key] = dataset[key] * 1;
            }
            return dataset;
        } else return undefined;
    };
    $.fn.data = function(key, value) {
        if (typeof value === 'undefined') {
            // Get value
            if (this[0] && this[0].getAttribute) {
                var dataKey = this[0].getAttribute('data-' + key);

                if (dataKey) {
                    return dataKey;
                } else if (this[0].smElementDataStorage && (key in this[0].smElementDataStorage)) {

                    return this[0].smElementDataStorage[key];

                } else {
                    return undefined;
                }
            } else return undefined;

        } else {
            // Set value
            for (var i = 0; i < this.length; i++) {
                var el = this[i];
                if (!el.smElementDataStorage) el.smElementDataStorage = {};
                el.smElementDataStorage[key] = value;
            }
            return this;
        }
    };
    $.fn.animationEnd = function(callback) {
        var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'],
            i, dom = this;

        function fireCallBack(e) {
            callback(e);
            for (i = 0; i < events.length; i++) {
                dom.off(events[i], fireCallBack);
            }
        }
        if (callback) {
            for (i = 0; i < events.length; i++) {
                dom.on(events[i], fireCallBack);
            }
        }
        return this;
    };
    $.fn.transition = function(duration) {
        if (typeof duration !== 'string') {
            duration = duration + 'ms';
        }
        for (var i = 0; i < this.length; i++) {
            var elStyle = this[i].style;
            elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;
        }
        return this;
    };
    $.fn.transform = function(transform) {
        for (var i = 0; i < this.length; i++) {
            var elStyle = this[i].style;
            elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
        }
        return this;
    };
    $.fn.prevAll = function (selector) {
      var prevEls = [];
      var el = this[0];
      if (!el) return $([]);
      while (el.previousElementSibling) {
        var prev = el.previousElementSibling;
        if (selector) {
          if($(prev).is(selector)) prevEls.push(prev);
        }
        else prevEls.push(prev);
        el = prev;
      }
      return $(prevEls);
    };
    $.fn.nextAll = function (selector) {
      var nextEls = [];
      var el = this[0];
      if (!el) return $([]);
      while (el.nextElementSibling) {
        var next = el.nextElementSibling;
        if (selector) {
          if($(next).is(selector)) nextEls.push(next);
        }
        else nextEls.push(next);
        el = next;
      }
      return $(nextEls);
    };

    //重置zepto的show方法，防止有些人引用的版本中 show 方法操作 opacity 属性影响动画执行
    $.fn.show = function(){
      var elementDisplay = {};
      function defaultDisplay(nodeName) {
        var element, display;
        if (!elementDisplay[nodeName]) {
          element = document.createElement(nodeName);
          document.body.appendChild(element);
          display = getComputedStyle(element, '').getPropertyValue("display");
          element.parentNode.removeChild(element);
          display === "none" && (display = "block");
          elementDisplay[nodeName] = display;
        }
        return elementDisplay[nodeName];
      }

      return this.each(function(){
        this.style.display === "none" && (this.style.display = '');
        if (getComputedStyle(this, '').getPropertyValue("display") === "none");
          this.style.display = defaultDisplay(this.nodeName);
      });
    };
})(Zepto);
<commit_msg>Improve data function of zepto-adapter.<commit_after>/* global Zepto:true */
/* global WebKitCSSMatrix:true */

(function($) {
     "use strict";
    ['width', 'height'].forEach(function(dimension) {
        var  Dimension = dimension.replace(/./, function(m) {
            return m[0].toUpperCase();
        });
        $.fn['outer' + Dimension] = function(margin) {
            var elem = this;
            if (elem) {
                var size = elem[dimension]();
                var sides = {
                    'width': ['left', 'right'],
                    'height': ['top', 'bottom']
                };
                sides[dimension].forEach(function(side) {
                    if (margin) size += parseInt(elem.css('margin-' + side), 10);
                });
                return size;
            } else {
                return null;
            }
        };
    });

    //support
    $.support = (function() {
        var support = {
            touch: !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch)
        };
        return support;
    })();

    $.touchEvents = {
        start: $.support.touch ? 'touchstart' : 'mousedown',
        move: $.support.touch ? 'touchmove' : 'mousemove',
        end: $.support.touch ? 'touchend' : 'mouseup'
    };

    $.getTranslate = function (el, axis) {
      var matrix, curTransform, curStyle, transformMatrix;

      // automatic axis detection
      if (typeof axis === 'undefined') {
        axis = 'x';
      }

      curStyle = window.getComputedStyle(el, null);
      if (window.WebKitCSSMatrix) {
        // Some old versions of Webkit choke when 'none' is passed; pass
        // empty string instead in this case
        transformMatrix = new WebKitCSSMatrix(curStyle.webkitTransform === 'none' ? '' : curStyle.webkitTransform);
      }
      else {
        transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform  || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,');
        matrix = transformMatrix.toString().split(',');
      }

      if (axis === 'x') {
        //Latest Chrome and webkits Fix
        if (window.WebKitCSSMatrix)
          curTransform = transformMatrix.m41;
        //Crazy IE10 Matrix
        else if (matrix.length === 16)
          curTransform = parseFloat(matrix[12]);
        //Normal Browsers
        else
          curTransform = parseFloat(matrix[4]);
      }
      if (axis === 'y') {
        //Latest Chrome and webkits Fix
        if (window.WebKitCSSMatrix)
          curTransform = transformMatrix.m42;
        //Crazy IE10 Matrix
        else if (matrix.length === 16)
          curTransform = parseFloat(matrix[13]);
        //Normal Browsers
        else
          curTransform = parseFloat(matrix[5]);
      }

      return curTransform || 0;
    };
    $.requestAnimationFrame = function (callback) {
      if (window.requestAnimationFrame) return window.requestAnimationFrame(callback);
      else if (window.webkitRequestAnimationFrame) return window.webkitRequestAnimationFrame(callback);
      else if (window.mozRequestAnimationFrame) return window.mozRequestAnimationFrame(callback);
      else {
        return window.setTimeout(callback, 1000 / 60);
      }
    };

    $.cancelAnimationFrame = function (id) {
      if (window.cancelAnimationFrame) return window.cancelAnimationFrame(id);
      else if (window.webkitCancelAnimationFrame) return window.webkitCancelAnimationFrame(id);
      else if (window.mozCancelAnimationFrame) return window.mozCancelAnimationFrame(id);
      else {
        return window.clearTimeout(id);
      }
    };

    $.fn.transitionEnd = function(callback) {
        var events = ['webkitTransitionEnd', 'transitionend', 'oTransitionEnd', 'MSTransitionEnd', 'msTransitionEnd'],
            i, dom = this;

        function fireCallBack(e) {
            /*jshint validthis:true */
            if (e.target !== this) return;
            callback.call(this, e);
            for (i = 0; i < events.length; i++) {
                dom.off(events[i], fireCallBack);
            }
        }
        if (callback) {
            for (i = 0; i < events.length; i++) {
                dom.on(events[i], fireCallBack);
            }
        }
        return this;
    };
    $.fn.dataset = function() {
        var el = this[0];
        if (el) {
            var dataset = {};
            if (el.dataset) {

                for (var dataKey in el.dataset) { // jshint ignore:line
                    dataset[dataKey] = el.dataset[dataKey];
                }
            } else {
                for (var i = 0; i < el.attributes.length; i++) {
                    var attr = el.attributes[i];
                    if (attr.name.indexOf('data-') >= 0) {
                        dataset[$.toCamelCase(attr.name.split('data-')[1])] = attr.value;
                    }
                }
            }
            for (var key in dataset) {
                if (dataset[key] === 'false') dataset[key] = false;
                else if (dataset[key] === 'true') dataset[key] = true;
                else if (parseFloat(dataset[key]) === dataset[key] * 1) dataset[key] = dataset[key] * 1;
            }
            return dataset;
        } else return undefined;
    };
    $.fn.data = function(key, value) {
		if (typeof key === 'undefined') {
            var data = {};
            $.each(this[0].attributes, function (i, node) {
                if (node.name.indexOf('data-') === 0) {
                    data[node.name.substring(5)] = node.nodeValue;
                }
            });
            return data;
        }
        if (typeof value === 'undefined') {
            // Get value
            if (this[0] && this[0].getAttribute) {
                var dataKey = this[0].getAttribute('data-' + key);

                if (dataKey) {
                    return dataKey;
                } else if (this[0].smElementDataStorage && (key in this[0].smElementDataStorage)) {

                    return this[0].smElementDataStorage[key];

                } else {
                    return undefined;
                }
            } else return undefined;

        } else {
            // Set value
            for (var i = 0; i < this.length; i++) {
                var el = this[i];
                if (!el.smElementDataStorage) el.smElementDataStorage = {};
                el.smElementDataStorage[key] = value;
            }
            return this;
        }
    };
    $.fn.animationEnd = function(callback) {
        var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'],
            i, dom = this;

        function fireCallBack(e) {
            callback(e);
            for (i = 0; i < events.length; i++) {
                dom.off(events[i], fireCallBack);
            }
        }
        if (callback) {
            for (i = 0; i < events.length; i++) {
                dom.on(events[i], fireCallBack);
            }
        }
        return this;
    };
    $.fn.transition = function(duration) {
        if (typeof duration !== 'string') {
            duration = duration + 'ms';
        }
        for (var i = 0; i < this.length; i++) {
            var elStyle = this[i].style;
            elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = duration;
        }
        return this;
    };
    $.fn.transform = function(transform) {
        for (var i = 0; i < this.length; i++) {
            var elStyle = this[i].style;
            elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
        }
        return this;
    };
    $.fn.prevAll = function (selector) {
      var prevEls = [];
      var el = this[0];
      if (!el) return $([]);
      while (el.previousElementSibling) {
        var prev = el.previousElementSibling;
        if (selector) {
          if($(prev).is(selector)) prevEls.push(prev);
        }
        else prevEls.push(prev);
        el = prev;
      }
      return $(prevEls);
    };
    $.fn.nextAll = function (selector) {
      var nextEls = [];
      var el = this[0];
      if (!el) return $([]);
      while (el.nextElementSibling) {
        var next = el.nextElementSibling;
        if (selector) {
          if($(next).is(selector)) nextEls.push(next);
        }
        else nextEls.push(next);
        el = next;
      }
      return $(nextEls);
    };

    //重置zepto的show方法，防止有些人引用的版本中 show 方法操作 opacity 属性影响动画执行
    $.fn.show = function(){
      var elementDisplay = {};
      function defaultDisplay(nodeName) {
        var element, display;
        if (!elementDisplay[nodeName]) {
          element = document.createElement(nodeName);
          document.body.appendChild(element);
          display = getComputedStyle(element, '').getPropertyValue("display");
          element.parentNode.removeChild(element);
          display === "none" && (display = "block");
          elementDisplay[nodeName] = display;
        }
        return elementDisplay[nodeName];
      }

      return this.each(function(){
        this.style.display === "none" && (this.style.display = '');
        if (getComputedStyle(this, '').getPropertyValue("display") === "none");
          this.style.display = defaultDisplay(this.nodeName);
      });
    };
})(Zepto);
<|endoftext|>


<commit_before>/* eslint-disable import/no-extraneous-dependencies */
const path = require('path')

const glob = require('glob')

const { sourcesGlob, sourcesIgnoreGlobs } = require('../scripts/utils')

const root = path.join(__dirname, '..')
const include = glob
  .sync(sourcesGlob, { cwd: root, ignore: sourcesIgnoreGlobs })
  .map((source) => path.join(root, source))

module.exports = {
  source: {
    include,
    excludePattern: '([\\/]node_modules[\\/])|(\\.vue)$',
    includePattern: '.*',
  },

  opts: {
    destination: `${root}/packages/interactjs/dist/api/`,
    recurse: true,
  },

  plugins: ['plugins/markdown', 'jsdoc-stale', 'jsdoc-babel'],

  babel: {
    extensions: ['js', 'ts'],
    babelrc: false,
    configFile: false,
    presets: [[require.resolve('@babel/preset-typescript'), { allowDeclareFields: true }]],
  },

  markdown: {
    idInHeadings: true,
  },

  articles: ['**/*.md'],

  templates: {
    cleverLinks: true,
  },
}
<commit_msg>chore: jsdoc babel logical assignment operators<commit_after>/* eslint-disable import/no-extraneous-dependencies */
const path = require('path')

const glob = require('glob')

const { sourcesGlob, sourcesIgnoreGlobs } = require('../scripts/utils')

const root = path.join(__dirname, '..')
const include = glob
  .sync(sourcesGlob, { cwd: root, ignore: sourcesIgnoreGlobs })
  .map((source) => path.join(root, source))

module.exports = {
  source: {
    include,
    excludePattern: '([\\/]node_modules[\\/])|(\\.vue)$',
    includePattern: '.*',
  },

  opts: {
    destination: `${root}/packages/interactjs/dist/api/`,
    recurse: true,
  },

  plugins: ['plugins/markdown', 'jsdoc-stale', 'jsdoc-babel'],

  babel: {
    extensions: ['js', 'ts'],
    babelrc: false,
    configFile: false,
    presets: [[require.resolve('@babel/preset-typescript'), { allowDeclareFields: true }]],
    plugins: [require.resolve('@babel/plugin-proposal-logical-assignment-operators')],
  },

  markdown: {
    idInHeadings: true,
  },

  articles: ['**/*.md'],

  templates: {
    cleverLinks: true,
  },
}
<|endoftext|>


<commit_before>var colors = require('colours'),
    fs = require('fs'),
    glob = require('glob'),
    lodash = require('lodash-node'),
    mkdirp = require('mkdirp'),
    path = require('path'),
    util = require('util'),
    errors = {
        'NOT_ABE': 'This file is an invalid ABE JSON format'
    },
    opt = {
        'verbose': false,
        'location': './',
        'build': 'tmp/'
    };

exports.jsonBuilder = function (options, callback) {
    lodash.merge(opt, options);

    glob
        .sync(opt.location + '.json', {
            mark: true
        })
        .forEach(function (match) {
            var json = require(process.cwd() + '/' + match);

            // Check that the JSON passed to the function has examples / matches
            // ABE Spec
            if (!lodash.has(json, 'examples')) {
                return errors.NOT_ABE;
            }

            var folderArr = path.dirname(match).split('/'),
                filePath = process.cwd() + '/' + opt.build,
                buildName = folderArr[folderArr.length - 1] + '-',
                baseName = path.basename(match, '.json');

            if (!fs.existsSync(filePath)) {
                mkdirp(filePath, function (err) {
                    if (err) {
                        console.log(err);
                    } else if (opt.verbose) {
                        console.log(opt.build.yellow, ' created.');
                    }
                });
            }

            // Loop through each example within the JSON to create it's own
            // JSON file
            lodash.forEach(json.examples, function (obj, key) {
                var bodyData = json.examples[key].response.body,
                    fileData = JSON.stringify(bodyData, null, 4),
                    file = buildName + baseName + '-' + key + '.json';

                fs.writeFile(filePath + file, fileData, function (err) {
                    if (err) {
                        console.log(err);
                    } else if (opt.verbose) {
                        console.log(file.green, ' file was saved.');
                    }
                });
            });

        });

    if (lodash.isFunction(callback)) {
        callback();
    }
};
<commit_msg>Use synchronous functions to build files<commit_after>var colors = require('colours'),
    fs = require('fs'),
    glob = require('glob'),
    lodash = require('lodash-node'),
    mkdirp = require('mkdirp'),
    path = require('path'),
    util = require('util'),
    errors = {
        'NOT_ABE': 'This file is an invalid ABE JSON format'
    },
    opt = {
        'verbose': false,
        'location': './',
        'build': 'tmp/'
    };

exports.jsonBuilder = function (options, callback) {
    lodash.merge(opt, options);
    var folderPath = path.join(process.cwd(), opt.build);

    if (!fs.existsSync(folderPath)) {
        mkdirp.sync(folderPath);
    }

    glob
        .sync(opt.location + '.json', {
            mark: true
        })
        .forEach(function (match) {
            var json = require(path.join(process.cwd(), match));

            // Check that the JSON passed to the function has examples / matches
            // ABE Spec
            if (!lodash.has(json, 'examples')) {
                return errors.NOT_ABE;
            }

            var folderArr = path.dirname(match).split('/'),
                buildName = folderArr[folderArr.length - 1] + '-',
                baseName = path.basename(match, '.json');

            // Loop through each example within the JSON to create it's own
            // JSON file
            lodash.forEach(json.examples, function (obj, key) {
                var data = JSON.stringify(json
                        .examples[key].response.body, null, 4)
                    file = buildName + baseName + '-' + key + '.json',
                    filePath = path.join(folderPath, file);

                fs.writeFileSync(filePath, data);
            });

        });

    if (lodash.isFunction(callback)) {
        callback();
    }
};
<|endoftext|>


<commit_before>'use strict'
import { CATEGORY, TAG, TOPIC } from '../constants/index'
import { InternalServerError, NotFoundError } from '../lib/custom-error'
import { arrayOf, normalize } from 'normalizr'
import { article as articleSchema } from '../schemas/index'
import { camelizeKeys } from 'humps'
import { formatUrl, getArticleEmbeddedQuery } from '../utils/index'
import _ from 'lodash'
import * as types from '../constants/action-types'
import fetch from 'isomorphic-fetch'
import qs from 'qs'

function requestArticlesByUuid(id, url) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_REQUEST,
    id,
    url
  }
}

function failToReceiveArticlesByUuid(id, error) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_FAILURE,
    id,
    error,
    failedAt: Date.now()
  }
}

function receiveArticlesByUuid(response, id) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_SUCCESS,
    id,
    response,
    receivedAt: Date.now()
  }
}

function requestRelatedArticles(id, relatedIds, url) {
  return {
    type: types.FETCH_RELATED_ARTICLES_REQUEST,
    id,
    relatedIds,
    url
  }
}

function failToReceiveRelatedArticles(id, relatedIds, error) {
  return {
    type: types.FETCH_RELATED_ARTICLES_FAILURE,
    id,
    relatedIds,
    error,
    failedAt: Date.now()
  }
}

function receiveRelatedArticles(response, id, relatedIds) {
  return {
    type: types.FETCH_RELATED_ARTICLES_SUCCESS,
    id,
    relatedIds,
    response,
    receivedAt: Date.now()
  }
}

function requestFeatureArticles(url) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_REQUEST,
    url
  }
}

function failToReceiveFeatureArticles(error) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_FAILURE,
    error,
    failedAt: Date.now()
  }
}

function receiveFeaturedArticles(response) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_SUCCESS,
    response,
    receivedAt: Date.now()
  }
}

function _buildQuery(params = {}) {
  let query = {}
  let whitelist = [ 'where', 'embedded', 'max_results', 'page', 'sort' ]
  _.forEach(whitelist, (ele) => {
    if (params.hasOwnProperty(ele)) {
      if (ele === 'where' || ele === 'embedded') {
        query[ele] = JSON.stringify(params[ele])
      } else {
        query[ele] = params[ele]
      }
    }
  })
  query = qs.stringify(query)
  return query
}

function _buildPostQueryUrl(params = {}) {
  let query = _buildQuery(params)
  return formatUrl(`posts?${query}`)
}

function _buildMetaQueryUrl(params = {}) {
  let query = _buildQuery(params)
  return formatUrl(`meta?${query}`)
}

function _buildUrl(params = {}, target) {
  params = params || {}
  params.sort = params.sort || '-publishedDate'
  if (target === 'meta') {
    return _buildMetaQueryUrl(params)
  }
  return _buildPostQueryUrl(params)
}

function _setupWhereInParam(key, value, params={}) {
  params = params || {}
  value = Array.isArray(value) ? value : [ value ]
  let where = {}
  if (value.length > 0) {
    _.merge(where, params.where, {
      [key]: {
        '$in': value
      }
    })
  }
  params.where = where
  return params
}

function _fetchArticles(url) {
  return fetch(url)
    .then((response) => {
      let status = response.status
      if (status === 404) {
        throw new NotFoundError('Resource are not found by url: ', url)
      } else if (status >= 400) {
        throw new InternalServerError('Bad response from API, response: ' + JSON.stringify(response))
      }
      return response.json()
    })
}

/* Fetch related articles' meta of one certain article
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {string} articleId
 * @param {string[]} relatedIds - related article ids
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchRelatedArticlesIfNeeded(articleId, relatedIds, params = {}, isOnlyMeta = true) {
  return (dispatch, getState) => {
    if (!articleId) {
      return Promise.resolve()
    }
    let articles = _.get(getState(), [ 'entities', 'articles' ], {})
    let idsToFetch = []

    _.forEach(relatedIds, (id) => {
      if (!_.has(articles, id)) {
        idsToFetch.push(id)
      }
    })

    if (_.get(idsToFetch, 'length', 0) === 0) {
      return Promise.resolve()
    }

    params = _setupWhereInParam('_id', idsToFetch, params)

    if (!isOnlyMeta) {
      // add default embedded
      params.embedded = params.embedded ? params.embedded : getArticleEmbeddedQuery()
    }

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestRelatedArticles(articleId, idsToFetch, url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        return dispatch(receiveRelatedArticles(normalized, articleId, idsToFetch))
      }, (error) => {
        return dispatch(failToReceiveRelatedArticles(articleId, idsToFetch, error))
      })
  }
}

/* Fetch meta of articles according to one group uuid.
 * Group uuid could be Category, Tag or Topic uuid.
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {string} uuid - Category, Tag or Topic uuid
 * @param {string} type - CATEGORY, TAG or TOPIC
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchArticlesByUuidIfNeeded(uuid = '', type = '', params = {}, isOnlyMeta = true) {
  return (dispatch, getState) => {
    if (!uuid || _.get(getState(), [ 'articlesByUuids', uuid, 'hasMore' ]) === false) {
      return Promise.resolve()
    }

    switch (type) {
      case CATEGORY:
        params = _setupWhereInParam('categories', [ uuid ], params)
        break
      case TAG:
        params = _setupWhereInParam('tags', [ uuid ], params)
        break
      case TOPIC:
        params = _setupWhereInParam('topics', [ uuid ], params)
        break
      default:
        return Promise.resolve()
    }

    if (!isOnlyMeta) {
      // add default embedded
      params.embedded = params.embedded ? params.embedded : getArticleEmbeddedQuery()
    }

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestArticlesByUuid(uuid, url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        _.merge(normalized, { links: camelizedJson.links, meta: camelizedJson.meta })
        return dispatch(receiveArticlesByUuid(normalized, uuid))
      }, (error) => {
        return dispatch(failToReceiveArticlesByUuid(uuid, error))
      })

  }
}

/* Fetch meta of articles whose 'isFeature' field is true
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchFeatureArticlesIfNeeded(params = {}, isOnlyMeta = true) {
  const limit = 6
  const page = 1

  return (dispatch, getState) => {

    if (_.get(getState(), 'featureArticles.items.length', 0) > 0) {
      return Promise.resolve()
    }

    params = params || {}
    params.where = _.merge({}, params.where, {
      isFeatured: true
    })
    params.max_results = params.max_results || limit
    params.page = params.page || page

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestFeatureArticles(url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        return dispatch(receiveFeaturedArticles(normalized))
      }, (error) => {
        return dispatch(failToReceiveFeatureArticles(error))
      })
  }
}

// no need temporarily
/*
function _getListId(target = 'category') {
  if (__DEVELOPMENT__) { // eslint-disable-line
    return target === 'category' ? devCatListId : devTopicListId
  }
  return target === 'category' ? prodCatListId : prodTopicListId
}

export function fetchArticlesByTopicIdNameIfNeeded(topicName = '', params = {} , isOnlyMeta = true) {
  let listId = _getListId('topic')
  let topicId = listId[topicName]
  return fetchArticlesByTopicIdIfNeeded(topicId, params, isOnlyMeta)
}

function _fetchArticlesByListName(name = '', params = {}, isOnlyMeta = true, target) {
  let listId = _getListId(target)
  let id = listId[name]
  params = params || {}
  params.max_results = params.max_results || 10
  params.page = params.page || 1
  return target === 'category' ? fetchArticlesByCatIdIfNeeded(id, params, isOnlyMeta) : fetchArticlesByTagIdIfNeeded(id, params, isOnlyMeta)
}

export function fetchArticlesByCatNameIfNeeded(catName = '', params = {}, isOnlyMeta = true) {
  return _fetchArticlesByListName(catName, params, isOnlyMeta, 'category')
}

export function fetchArticlesByTagNameIfNeeded(tagName = '', params = {}, isOnlyMeta = true) {
  return _fetchArticlesByListName(tagName, params, isOnlyMeta, 'tag')
}

*/
<commit_msg>Update actions/articles.js - Replace fetchFeatureArticlesIfNeeded by fetchFeatureArticles<commit_after>'use strict'
import { CATEGORY, TAG, TOPIC } from '../constants/index'
import { InternalServerError, NotFoundError } from '../lib/custom-error'
import { arrayOf, normalize } from 'normalizr'
import { article as articleSchema } from '../schemas/index'
import { camelizeKeys } from 'humps'
import { formatUrl, getArticleEmbeddedQuery } from '../utils/index'
import _ from 'lodash'
import * as types from '../constants/action-types'
import fetch from 'isomorphic-fetch'
import qs from 'qs'

function requestArticlesByUuid(id, url) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_REQUEST,
    id,
    url
  }
}

function failToReceiveArticlesByUuid(id, error) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_FAILURE,
    id,
    error,
    failedAt: Date.now()
  }
}

function receiveArticlesByUuid(response, id) {
  return {
    type: types.FETCH_ARTICLES_BY_GROUP_UUID_SUCCESS,
    id,
    response,
    receivedAt: Date.now()
  }
}

function requestRelatedArticles(id, relatedIds, url) {
  return {
    type: types.FETCH_RELATED_ARTICLES_REQUEST,
    id,
    relatedIds,
    url
  }
}

function failToReceiveRelatedArticles(id, relatedIds, error) {
  return {
    type: types.FETCH_RELATED_ARTICLES_FAILURE,
    id,
    relatedIds,
    error,
    failedAt: Date.now()
  }
}

function receiveRelatedArticles(response, id, relatedIds) {
  return {
    type: types.FETCH_RELATED_ARTICLES_SUCCESS,
    id,
    relatedIds,
    response,
    receivedAt: Date.now()
  }
}

function requestFeatureArticles(url) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_REQUEST,
    url
  }
}

function failToReceiveFeatureArticles(error) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_FAILURE,
    error,
    failedAt: Date.now()
  }
}

function receiveFeaturedArticles(response) {
  return {
    type: types.FETCH_FEATURE_ARTICLES_SUCCESS,
    response,
    receivedAt: Date.now()
  }
}

function _buildQuery(params = {}) {
  let query = {}
  let whitelist = [ 'where', 'embedded', 'max_results', 'page', 'sort' ]
  _.forEach(whitelist, (ele) => {
    if (params.hasOwnProperty(ele)) {
      if (ele === 'where' || ele === 'embedded') {
        query[ele] = JSON.stringify(params[ele])
      } else {
        query[ele] = params[ele]
      }
    }
  })
  query = qs.stringify(query)
  return query
}

function _buildPostQueryUrl(params = {}) {
  let query = _buildQuery(params)
  return formatUrl(`posts?${query}`)
}

function _buildMetaQueryUrl(params = {}) {
  let query = _buildQuery(params)
  return formatUrl(`meta?${query}`)
}

function _buildUrl(params = {}, target) {
  params = params || {}
  params.sort = params.sort || '-publishedDate'
  if (target === 'meta') {
    return _buildMetaQueryUrl(params)
  }
  return _buildPostQueryUrl(params)
}

function _setupWhereInParam(key, value, params={}) {
  params = params || {}
  value = Array.isArray(value) ? value : [ value ]
  let where = {}
  if (value.length > 0) {
    _.merge(where, params.where, {
      [key]: {
        '$in': value
      }
    })
  }
  params.where = where
  return params
}

function _fetchArticles(url) {
  return fetch(url)
    .then((response) => {
      let status = response.status
      if (status === 404) {
        throw new NotFoundError('Resource are not found by url: ', url)
      } else if (status >= 400) {
        throw new InternalServerError('Bad response from API, response: ' + JSON.stringify(response))
      }
      return response.json()
    })
}

/* Fetch related articles' meta of one certain article
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {string} articleId
 * @param {string[]} relatedIds - related article ids
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchRelatedArticlesIfNeeded(articleId, relatedIds, params = {}, isOnlyMeta = true) {
  return (dispatch, getState) => {
    if (!articleId) {
      return Promise.resolve()
    }
    let articles = _.get(getState(), [ 'entities', 'articles' ], {})
    let idsToFetch = []

    _.forEach(relatedIds, (id) => {
      if (!_.has(articles, id)) {
        idsToFetch.push(id)
      }
    })

    if (_.get(idsToFetch, 'length', 0) === 0) {
      return Promise.resolve()
    }

    params = _setupWhereInParam('_id', idsToFetch, params)

    if (!isOnlyMeta) {
      // add default embedded
      params.embedded = params.embedded ? params.embedded : getArticleEmbeddedQuery()
    }

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestRelatedArticles(articleId, idsToFetch, url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        return dispatch(receiveRelatedArticles(normalized, articleId, idsToFetch))
      }, (error) => {
        return dispatch(failToReceiveRelatedArticles(articleId, idsToFetch, error))
      })
  }
}

/* Fetch meta of articles according to one group uuid.
 * Group uuid could be Category, Tag or Topic uuid.
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {string} uuid - Category, Tag or Topic uuid
 * @param {string} type - CATEGORY, TAG or TOPIC
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchArticlesByUuidIfNeeded(uuid = '', type = '', params = {}, isOnlyMeta = true) {
  return (dispatch, getState) => {
    if (!uuid || _.get(getState(), [ 'articlesByUuids', uuid, 'hasMore' ]) === false) {
      return Promise.resolve()
    }

    switch (type) {
      case CATEGORY:
        params = _setupWhereInParam('categories', [ uuid ], params)
        break
      case TAG:
        params = _setupWhereInParam('tags', [ uuid ], params)
        break
      case TOPIC:
        params = _setupWhereInParam('topics', [ uuid ], params)
        break
      default:
        return Promise.resolve()
    }

    if (!isOnlyMeta) {
      // add default embedded
      params.embedded = params.embedded ? params.embedded : getArticleEmbeddedQuery()
    }

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestArticlesByUuid(uuid, url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        _.merge(normalized, { links: camelizedJson.links, meta: camelizedJson.meta })
        return dispatch(receiveArticlesByUuid(normalized, uuid))
      }, (error) => {
        return dispatch(failToReceiveArticlesByUuid(uuid, error))
      })

  }
}

/* Fetch meta of articles whose 'isFeature' field is true
 * properties of meta: subtitle, name, heroImage, title, topics, publishedDate, slug, links, created and id
 * @param {object} params - params for composing query param of api
 * @param {string} [params.sort=-publishedDate] -the way returned articles are sorted by
 * @param {object} params.where - where query param
 * @param {object} params.embedded - embedded query param
 * @param {boolean} isOnlyMeta - if true, only get metadata of articles. Otherwise, get full articles.
 */
export function fetchFeatureArticles(params = {}, isOnlyMeta = true) {
  const limit = 6
  const page = 1

  return (dispatch) => {
    params = params || {}
    params.where = _.merge({}, params.where, {
      isFeatured: true
    })
    params.max_results = params.max_results || limit
    params.page = params.page || page

    let url = _buildUrl(params, isOnlyMeta ? 'meta' : 'article')
    dispatch(requestFeatureArticles(url))
    return _fetchArticles(url)
      .then((response) => {
        let camelizedJson = camelizeKeys(response)
        let normalized = normalize(camelizedJson.items, arrayOf(articleSchema))
        return dispatch(receiveFeaturedArticles(normalized))
      }, (error) => {
        return dispatch(failToReceiveFeatureArticles(error))
      })
  }
}

// no need temporarily
/*
function _getListId(target = 'category') {
  if (__DEVELOPMENT__) { // eslint-disable-line
    return target === 'category' ? devCatListId : devTopicListId
  }
  return target === 'category' ? prodCatListId : prodTopicListId
}

export function fetchArticlesByTopicIdNameIfNeeded(topicName = '', params = {} , isOnlyMeta = true) {
  let listId = _getListId('topic')
  let topicId = listId[topicName]
  return fetchArticlesByTopicIdIfNeeded(topicId, params, isOnlyMeta)
}

function _fetchArticlesByListName(name = '', params = {}, isOnlyMeta = true, target) {
  let listId = _getListId(target)
  let id = listId[name]
  params = params || {}
  params.max_results = params.max_results || 10
  params.page = params.page || 1
  return target === 'category' ? fetchArticlesByCatIdIfNeeded(id, params, isOnlyMeta) : fetchArticlesByTagIdIfNeeded(id, params, isOnlyMeta)
}

export function fetchArticlesByCatNameIfNeeded(catName = '', params = {}, isOnlyMeta = true) {
  return _fetchArticlesByListName(catName, params, isOnlyMeta, 'category')
}

export function fetchArticlesByTagNameIfNeeded(tagName = '', params = {}, isOnlyMeta = true) {
  return _fetchArticlesByListName(tagName, params, isOnlyMeta, 'tag')
}

*/
<|endoftext|>


<commit_before>var toStringFormatter = function(answer){
	return answer.toString();
};

var toLowerCaseFormatter = function(answer){
	return answer.toLowerCase();
};

var stringFormUtils = require('string-form-utils');
var fullwidthFormatter = function(answer){
	return stringFormUtils.transformToHalfwidth(answer);
};

var removeSpaceFormatter = function(answer){
	return answer.replace(/\s/g, '');
};

var removeTailPeriodFormatter = function(answer){
	return answer.replace(/^(.+)。$/, '$1');
};

var numberFormatter = function(answer){
	return answer.replace(/(\d?),(\d?)/g, "$1$2");
};

var formatters = [
	toStringFormatter,
	toLowerCaseFormatter,
	fullwidthFormatter,
	removeSpaceFormatter,
	removeTailPeriodFormatter,
	numberFormatter
];

var format = function (answer){
	var result = answer;
	for(var i=0 ; i<formatters.length ; i++){
		result = formatters[i](result);
	}
	return result;
};

var equals = function (answer1, answer2){
	return  format(answer1) == format(answer2)
};

var answerFormatter = {
	format : format,
	equals : equals
};

if (typeof module !== "undefined" && module !== null) {
  module.exports = answerFormatter;
}

if (typeof window !== "undefined" && window !== null) {
  window.answerFormatter = answerFormatter;
}<commit_msg>about #821 加入注音 ㄧ、ㄚ 和國字的轉換<commit_after>var toStringFormatter = function(answer){
	return answer.toString();
};

var toLowerCaseFormatter = function(answer){
	return answer.toLowerCase();
};

var stringFormUtils = require('string-form-utils');
var fullwidthFormatter = function(answer){
	return stringFormUtils.transformToHalfwidth(answer);
};

var removeSpaceFormatter = function(answer){
	return answer.replace(/\s/g, '');
};

var removeTailPeriodFormatter = function(answer){
	return answer.replace(/^(.+)。$/, '$1');
};

var numberFormatter = function(answer){
	return answer.replace(/(\d?),(\d?)/g, "$1$2");
};

var phoneticFormatter = function(answer){
	return answer
		.replace(/ㄧ/g, '一')
		.replace(/ㄚ/g, '丫');
};

var formatters = [
	toStringFormatter,
	toLowerCaseFormatter,
	fullwidthFormatter,
	removeSpaceFormatter,
	removeTailPeriodFormatter,
	numberFormatter,
	phoneticFormatter
];

var format = function (answer){
	var result = answer;
	for(var i=0 ; i<formatters.length ; i++){
		result = formatters[i](result);
	}
	return result;
};

var equals = function (answer1, answer2){
	return  format(answer1) == format(answer2)
};

var answerFormatter = {
	format : format,
	equals : equals
};

if (typeof module !== "undefined" && module !== null) {
  module.exports = answerFormatter;
}

if (typeof window !== "undefined" && window !== null) {
  window.answerFormatter = answerFormatter;
}<|endoftext|>


<commit_before>'use strict';

import $ from 'jquery';

module.exports = {
  get: function (url) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          dataType: 'json',
          success: success,
          error: error
        });
      }
    );
  },
  post: function (url, data) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'POST',
          data: data,
          success: success,
          error: error
        });
      }
    );
  },
  patch: function (url, data) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'PATCH',
          data: data,
          success: success,
          error: error
        });
      }
    );
  },
  del: function (url) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'DELETE',
          success: success,
          error: error
        });
      }
    );
  }
};
<commit_msg>Add callback to promise<commit_after>'use strict';

import $ from 'jquery';

module.exports = {
  get: function (url) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          dataType: 'json',
          success: success,
          error: error
        });
      }
    );
  },
  post: function (url, data, callback) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'POST',
          data: data,
          success: success,
          error: error
        });
      }
    ).then(function(data) {
      callback(data);
    });
  },
  patch: function (url, data) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'PATCH',
          data: data,
          success: success,
          error: error
        });
      }
    );
  },
  del: function (url) {
    return new Promise(
      function (success, error) {
        $.ajax({
          url: url,
          type: 'DELETE',
          success: success,
          error: error
        });
      }
    );
  }
};
<|endoftext|>


<commit_before>const config = require('../../../config')
const authorisedRequest = require('../../lib/authorised-request')

const Order = {
  save (token, data) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order`,
      method: 'POST',
      body: data,
    })
  },

  update (token, id, data) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}`,
      method: 'PATCH',
      body: data,
    })
  },

  saveSubscribers (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/subscriber-list`,
      method: 'PUT',
      body,
    })
  },

  getAssignees (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/assignee`)
  },

  saveAssignees (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/assignee`,
      method: 'PATCH',
      body,
    })
  },

  forceSaveAssignees (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/assignee?force-delete=1`,
      method: 'PATCH',
      body,
    })
  },

  getSubscribers (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/subscriber-list`)
  },

  getById (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}`)
  },
}

module.exports = {
  Order,
}
<commit_msg>Extend order model to support quote endpoints<commit_after>const config = require('../../../config')
const authorisedRequest = require('../../lib/authorised-request')

const Order = {
  save (token, data) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order`,
      method: 'POST',
      body: data,
    })
  },

  update (token, id, data) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}`,
      method: 'PATCH',
      body: data,
    })
  },

  saveSubscribers (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/subscriber-list`,
      method: 'PUT',
      body,
    })
  },

  getAssignees (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/assignee`)
  },

  saveAssignees (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/assignee`,
      method: 'PATCH',
      body,
    })
  },

  forceSaveAssignees (token, id, body) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/assignee?force-delete=1`,
      method: 'PATCH',
      body,
    })
  },

  getSubscribers (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/subscriber-list`)
  },

  getById (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}`)
  },

  getQuotePreview (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/quote/preview`)
  },

  getQuote (token, id) {
    return authorisedRequest(token, `${config.apiRoot}/v3/omis/order/${id}/quote`)
  },

  createQuote (token, id) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/quote`,
      method: 'POST',
    })
  },

  cancelQuote (token, id) {
    return authorisedRequest(token, {
      url: `${config.apiRoot}/v3/omis/order/${id}/quote/cancel`,
      method: 'POST',
    })
  },
}

module.exports = {
  Order,
}
<|endoftext|>