<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('ambariAdminConsole')
.factory('Cluster', ['$http', '$q', 'Settings', function($http, $q, Settings) {
  return {
    repoStatusCache : {},

    orderedRoles : [
      'CLUSTER.ADMINISTRATOR',
      'CLUSTER.OPERATOR',
      'SERVICE.ADMINISTRATOR',
      'SERVICE.OPERATOR',
      'CLUSTER.USER'
    ],

    orderedAuthorizations : [
      "SERVICE.VIEW_METRICS",
      "SERVICE.VIEW_STATUS_INFO",
      "SERVICE.VIEW_CONFIGS",
      "SERVICE.COMPARE_CONFIGS",
      "SERVICE.VIEW_ALERTS",
      "SERVICE.START_STOP",
      "SERVICE.DECOMMISSION_RECOMMISSION",
      "SERVICE.RUN_SERVICE_CHECK",
      "SERVICE.TOGGLE_MAINTENANCE",
      "SERVICE.RUN_CUSTOM_COMMAND",
      "SERVICE.MODIFY_CONFIGS",
      "SERVICE.MANAGE_CONFIG_GROUPS",
      "SERVICE.MOVE",
      "SERVICE.ENABLE_HA",
      "SERVICE.MANAGE_ALERTS",
      "SERVICE.TOGGLE_ALERTS",
      "SERVICE.ADD_DELETE_SERVICES",
      "HOST.VIEW_CONFIGS",
      "HOST.VIEW_METRICS",
      "HOST.VIEW_STATUS_INFO",
      "HOST.ADD_DELETE_COMPONENTS",
      "HOST.ADD_DELETE_HOSTS",
      "HOST.TOGGLE_MAINTENANCE",
      "CLUSTER.VIEW_ALERTS",
      "CLUSTER.VIEW_CONFIGS",
      "CLUSTER.VIEW_METRICS",
      "CLUSTER.VIEW_STACK_DETAILS",
      "CLUSTER.VIEW_STATUS_INFO",
      "CLUSTER.MANAGE_ALERTS",
      "CLUSTER.MANAGE_CONFIG_GROUPS",
      "CLUSTER.MANAGE_CREDENTIALS",
      "CLUSTER.MODIFY_CONFIGS",
      "CLUSTER.TOGGLE_ALERTS",
      "CLUSTER.TOGGLE_KERBEROS",
      "CLUSTER.UPGRADE_DOWNGRADE_STACK",
      "AMBARI.ADD_DELETE_CLUSTERS",
      "AMBARI.ASSIGN_ROLES",
      "AMBARI.EDIT_STACK_REPOS",
      "AMBARI.MANAGE_GROUPS",
      "AMBARI.MANAGE_SETTINGS",
      "AMBARI.MANAGE_STACK_VERSIONS",
      "AMBARI.MANAGE_USERS",
      "AMBARI.MANAGE_VIEWS",
      "AMBARI.RENAME_CLUSTER",
      "AMBARI.SET_SERVICE_USERS_GROUPS"
    ],

    orderedLevels: ['SERVICE', 'HOST', 'CLUSTER', 'AMBARI'],

    ineditableRoles : ['VIEW.USER', 'AMBARI.ADMINISTRATOR'],

    getAllClusters: function() {
      var deferred = $q.defer();
      $http.get(Settings.baseUrl + '/clusters?fields=Clusters/cluster_id', {mock: 'cluster/clusters.json'})
      .then(function(data, status, headers) {
        deferred.resolve(data.data.items);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getStatus: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/clusters?fields=Clusters/provisioning_state', {mock: 'cluster/init.json'})
      .then(function(data, status, headers) {
        deferred.resolve(data.data.items[0]);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getAmbariVersion: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/component_version,RootServiceComponents/properties/server.os_family&minimal_response=true', {mock: '2.1'})
      .then(function(data) {
        deferred.resolve(data.data.RootServiceComponents.component_version);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getClusterOS: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/properties/server.os_family&minimal_response=true', {mock: 'redhat6'})
      .then(function(data) {
        deferred.resolve(data.data.RootServiceComponents.properties['server.os_family']);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getAmbariTimeout: function() {
      var deferred = $q.defer();
      var url = '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/properties/user.inactivity.timeout.default';
      $http.get(Settings.baseUrl + url)
      .then(function(data) {
        var properties = data.data.RootServiceComponents.properties;
        var timeout = properties? properties['user.inactivity.timeout.default'] : 0;
        deferred.resolve(timeout);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPermissions: function() {
      var deferred = $q.defer();

      $http({
        method: 'GET',
        url: Settings.baseUrl + '/permissions',
        mock: 'permission/permissions.json',
        params: {
          fields: 'PermissionInfo',
          'PermissionInfo/resource_name': 'CLUSTER'
        }
      })
      .success(function(data) {
        deferred.resolve(data.items);
      })
      .catch(function(data) {
        deferred.reject(data); });

      return deferred.promise;
    },
    getRolesWithAuthorizations: function() {
      var self = this;
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: Settings.baseUrl + '/permissions?PermissionInfo/resource_name.in(CLUSTER,AMBARI)',
        mock: 'permission/permissions.json',
        params: {
          fields: 'PermissionInfo/*,authorizations/AuthorizationInfo/*'
        }
      })
        .success(function(data) {
          deferred.resolve(data.items);
        })
        .catch(function(data) {
          deferred.reject(data); });

      return deferred.promise;
    },

    getPrivileges: function(params) {
      var deferred = $q.defer();

      $http({
        method: 'GET',
        url: Settings.baseUrl + '/clusters/'+params.clusterId,
        params : {
          'fields': 'privileges/PrivilegeInfo'
        }
      })
      .success(function(data) {
        deferred.resolve(data.privileges);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPrivilegesWithFilters: function(params) {
      var deferred = $q.defer();
      var isUser = params.typeFilter.value == 'USER';
      var endpoint = isUser? '/users' : '/groups';
      var nameURL = isUser? '&Users/user_name.matches(.*' : '&Groups/group_name.matches(.*';
      var nameFilter = params.nameFilter? nameURL + params.nameFilter + '.*)' : '';
      var roleFilter = params.roleFilter.value? '&privileges/PrivilegeInfo/permission_name.matches(.*' + params.roleFilter.value + '.*)' : '';
      $http({
        method: 'GET',
        url: Settings.baseUrl + endpoint + '?'
        + 'fields=privileges/PrivilegeInfo/*'
        + nameFilter
        + roleFilter
        + '&from=' + (params.currentPage - 1) * params.usersPerPage
        + '&page_size=' + params.usersPerPage
      })
      .success(function(data) {
        deferred.resolve(data);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPrivilegesForResource: function(params) {
      var deferred = $q.defer();
      var isUser = (params.typeFilter.value == 'USER');
      var endpoint = isUser ? '/users' : '/groups';
      var nameURL = isUser ? '&Users/user_name.matches(' : '&Groups/group_name.matches(';
      var nameFilter = params.nameFilter ? (nameURL + params.nameFilter + ')') : '';
      $http({
        method : 'GET',
        url : Settings.baseUrl + endpoint + '?' + 'fields=privileges/PrivilegeInfo/*' + nameFilter
      })
      .success(function(data) {
        deferred.resolve(data);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    createPrivileges: function(params, data) {
      return $http({
        method: 'POST',
        url: Settings.baseUrl + '/clusters/'+params.clusterId+'/privileges',
        data: data
      });
    },
    deletePrivileges: function(params, data) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+params.clusterId+'/privileges',
        data: data
      });
    },
    deleteMultiplePrivileges: function(clusterId, privilege_ids) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+clusterId+'/privileges?PrivilegeInfo/privilege_id.in\('+privilege_ids+'\)'
      });
    },
    updatePrivileges: function(params, privileges) {
      return $http({
        method: 'PUT',
        url: Settings.baseUrl + '/clusters/' + params.clusterId + '/privileges',
        data: privileges
      });
    },
    deletePrivilege: function(clusterId, id) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+clusterId+'/privileges/' + id
      });
    },
    editName: function(oldName, newName) {
      return $http({
        method: 'PUT',
        url: Settings.baseUrl + '/clusters/' + oldName,
        data: {
          Clusters: {
            "cluster_name": newName
          }
        }
      });
    },
    getRepoVersionStatus: function (clusterName, repoId ) {
      var me = this;
      var deferred = $q.defer();
      var url = Settings.baseUrl + '/clusters/' + clusterName +
        '/stack_versions?fields=*&ClusterStackVersions/repository_version=' + repoId;
      $http.get(url, {mock: 'cluster/repoVersionStatus.json'})
      .success(function (data) {
        data = data.items;
        var response = {};
        if (data.length > 0) {
          var hostStatus = data[0].ClusterStackVersions.host_states;
          var currentHosts = hostStatus['CURRENT'].length;
          var installedHosts = hostStatus['INSTALLED'].length;
          var totalHosts = 0;
          // collect hosts on all status
          angular.forEach(hostStatus, function(status) {
            totalHosts += status.length;
          });
          response.status = currentHosts > 0? 'current' :
                            installedHosts > 0? 'installed' : '';
          response.currentHosts = currentHosts;
          response.installedHosts = installedHosts;
          response.totalHosts = totalHosts;
          response.stackVersionId = data[0].ClusterStackVersions.id;
        } else {
          response.status = '';
        }
        me.repoStatusCache[repoId] = response.status;
        deferred.resolve(response);
      })
      .catch(function (data) {
        deferred.reject(data);
      });
      return deferred.promise;
    }
  };
}]);
<commit_msg>AMBARI-17298 Add View Operational Logs role info to RBAC help table	(akovalenko)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('ambariAdminConsole')
.factory('Cluster', ['$http', '$q', 'Settings', function($http, $q, Settings) {
  return {
    repoStatusCache : {},

    orderedRoles : [
      'CLUSTER.ADMINISTRATOR',
      'CLUSTER.OPERATOR',
      'SERVICE.ADMINISTRATOR',
      'SERVICE.OPERATOR',
      'CLUSTER.USER'
    ],

    orderedAuthorizations : [
      "SERVICE.VIEW_METRICS",
      "SERVICE.VIEW_STATUS_INFO",
      "SERVICE.VIEW_CONFIGS",
      "SERVICE.COMPARE_CONFIGS",
      "SERVICE.VIEW_ALERTS",
      "SERVICE.START_STOP",
      "SERVICE.DECOMMISSION_RECOMMISSION",
      "SERVICE.RUN_SERVICE_CHECK",
      "SERVICE.TOGGLE_MAINTENANCE",
      "SERVICE.RUN_CUSTOM_COMMAND",
      "SERVICE.MODIFY_CONFIGS",
      "SERVICE.MANAGE_CONFIG_GROUPS",
      "SERVICE.MOVE",
      "SERVICE.ENABLE_HA",
      "SERVICE.MANAGE_ALERTS",
      "SERVICE.TOGGLE_ALERTS",
      "SERVICE.ADD_DELETE_SERVICES",
      "SERVICE.VIEW_OPERATIONAL_LOGS",
      "HOST.VIEW_CONFIGS",
      "HOST.VIEW_METRICS",
      "HOST.VIEW_STATUS_INFO",
      "HOST.ADD_DELETE_COMPONENTS",
      "HOST.ADD_DELETE_HOSTS",
      "HOST.TOGGLE_MAINTENANCE",
      "CLUSTER.VIEW_ALERTS",
      "CLUSTER.VIEW_CONFIGS",
      "CLUSTER.VIEW_METRICS",
      "CLUSTER.VIEW_STACK_DETAILS",
      "CLUSTER.VIEW_STATUS_INFO",
      "CLUSTER.MANAGE_ALERTS",
      "CLUSTER.MANAGE_CONFIG_GROUPS",
      "CLUSTER.MANAGE_CREDENTIALS",
      "CLUSTER.MODIFY_CONFIGS",
      "CLUSTER.TOGGLE_ALERTS",
      "CLUSTER.TOGGLE_KERBEROS",
      "CLUSTER.UPGRADE_DOWNGRADE_STACK",
      "AMBARI.ADD_DELETE_CLUSTERS",
      "AMBARI.ASSIGN_ROLES",
      "AMBARI.EDIT_STACK_REPOS",
      "AMBARI.MANAGE_GROUPS",
      "AMBARI.MANAGE_SETTINGS",
      "AMBARI.MANAGE_STACK_VERSIONS",
      "AMBARI.MANAGE_USERS",
      "AMBARI.MANAGE_VIEWS",
      "AMBARI.RENAME_CLUSTER",
      "AMBARI.SET_SERVICE_USERS_GROUPS"
    ],

    orderedLevels: ['SERVICE', 'HOST', 'CLUSTER', 'AMBARI'],

    ineditableRoles : ['VIEW.USER', 'AMBARI.ADMINISTRATOR'],

    getAllClusters: function() {
      var deferred = $q.defer();
      $http.get(Settings.baseUrl + '/clusters?fields=Clusters/cluster_id', {mock: 'cluster/clusters.json'})
      .then(function(data, status, headers) {
        deferred.resolve(data.data.items);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getStatus: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/clusters?fields=Clusters/provisioning_state', {mock: 'cluster/init.json'})
      .then(function(data, status, headers) {
        deferred.resolve(data.data.items[0]);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getAmbariVersion: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/component_version,RootServiceComponents/properties/server.os_family&minimal_response=true', {mock: '2.1'})
      .then(function(data) {
        deferred.resolve(data.data.RootServiceComponents.component_version);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getClusterOS: function() {
      var deferred = $q.defer();

      $http.get(Settings.baseUrl + '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/properties/server.os_family&minimal_response=true', {mock: 'redhat6'})
      .then(function(data) {
        deferred.resolve(data.data.RootServiceComponents.properties['server.os_family']);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getAmbariTimeout: function() {
      var deferred = $q.defer();
      var url = '/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/properties/user.inactivity.timeout.default';
      $http.get(Settings.baseUrl + url)
      .then(function(data) {
        var properties = data.data.RootServiceComponents.properties;
        var timeout = properties? properties['user.inactivity.timeout.default'] : 0;
        deferred.resolve(timeout);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPermissions: function() {
      var deferred = $q.defer();

      $http({
        method: 'GET',
        url: Settings.baseUrl + '/permissions',
        mock: 'permission/permissions.json',
        params: {
          fields: 'PermissionInfo',
          'PermissionInfo/resource_name': 'CLUSTER'
        }
      })
      .success(function(data) {
        deferred.resolve(data.items);
      })
      .catch(function(data) {
        deferred.reject(data); });

      return deferred.promise;
    },
    getRolesWithAuthorizations: function() {
      var self = this;
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: Settings.baseUrl + '/permissions?PermissionInfo/resource_name.in(CLUSTER,AMBARI)',
        mock: 'permission/permissions.json',
        params: {
          fields: 'PermissionInfo/*,authorizations/AuthorizationInfo/*'
        }
      })
        .success(function(data) {
          deferred.resolve(data.items);
        })
        .catch(function(data) {
          deferred.reject(data); });

      return deferred.promise;
    },

    getPrivileges: function(params) {
      var deferred = $q.defer();

      $http({
        method: 'GET',
        url: Settings.baseUrl + '/clusters/'+params.clusterId,
        params : {
          'fields': 'privileges/PrivilegeInfo'
        }
      })
      .success(function(data) {
        deferred.resolve(data.privileges);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPrivilegesWithFilters: function(params) {
      var deferred = $q.defer();
      var isUser = params.typeFilter.value == 'USER';
      var endpoint = isUser? '/users' : '/groups';
      var nameURL = isUser? '&Users/user_name.matches(.*' : '&Groups/group_name.matches(.*';
      var nameFilter = params.nameFilter? nameURL + params.nameFilter + '.*)' : '';
      var roleFilter = params.roleFilter.value? '&privileges/PrivilegeInfo/permission_name.matches(.*' + params.roleFilter.value + '.*)' : '';
      $http({
        method: 'GET',
        url: Settings.baseUrl + endpoint + '?'
        + 'fields=privileges/PrivilegeInfo/*'
        + nameFilter
        + roleFilter
        + '&from=' + (params.currentPage - 1) * params.usersPerPage
        + '&page_size=' + params.usersPerPage
      })
      .success(function(data) {
        deferred.resolve(data);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    getPrivilegesForResource: function(params) {
      var deferred = $q.defer();
      var isUser = (params.typeFilter.value == 'USER');
      var endpoint = isUser ? '/users' : '/groups';
      var nameURL = isUser ? '&Users/user_name.matches(' : '&Groups/group_name.matches(';
      var nameFilter = params.nameFilter ? (nameURL + params.nameFilter + ')') : '';
      $http({
        method : 'GET',
        url : Settings.baseUrl + endpoint + '?' + 'fields=privileges/PrivilegeInfo/*' + nameFilter
      })
      .success(function(data) {
        deferred.resolve(data);
      })
      .catch(function(data) {
        deferred.reject(data);
      });

      return deferred.promise;
    },
    createPrivileges: function(params, data) {
      return $http({
        method: 'POST',
        url: Settings.baseUrl + '/clusters/'+params.clusterId+'/privileges',
        data: data
      });
    },
    deletePrivileges: function(params, data) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+params.clusterId+'/privileges',
        data: data
      });
    },
    deleteMultiplePrivileges: function(clusterId, privilege_ids) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+clusterId+'/privileges?PrivilegeInfo/privilege_id.in\('+privilege_ids+'\)'
      });
    },
    updatePrivileges: function(params, privileges) {
      return $http({
        method: 'PUT',
        url: Settings.baseUrl + '/clusters/' + params.clusterId + '/privileges',
        data: privileges
      });
    },
    deletePrivilege: function(clusterId, id) {
      return $http({
        method: 'DELETE',
        url: Settings.baseUrl + '/clusters/'+clusterId+'/privileges/' + id
      });
    },
    editName: function(oldName, newName) {
      return $http({
        method: 'PUT',
        url: Settings.baseUrl + '/clusters/' + oldName,
        data: {
          Clusters: {
            "cluster_name": newName
          }
        }
      });
    },
    getRepoVersionStatus: function (clusterName, repoId ) {
      var me = this;
      var deferred = $q.defer();
      var url = Settings.baseUrl + '/clusters/' + clusterName +
        '/stack_versions?fields=*&ClusterStackVersions/repository_version=' + repoId;
      $http.get(url, {mock: 'cluster/repoVersionStatus.json'})
      .success(function (data) {
        data = data.items;
        var response = {};
        if (data.length > 0) {
          var hostStatus = data[0].ClusterStackVersions.host_states;
          var currentHosts = hostStatus['CURRENT'].length;
          var installedHosts = hostStatus['INSTALLED'].length;
          var totalHosts = 0;
          // collect hosts on all status
          angular.forEach(hostStatus, function(status) {
            totalHosts += status.length;
          });
          response.status = currentHosts > 0? 'current' :
                            installedHosts > 0? 'installed' : '';
          response.currentHosts = currentHosts;
          response.installedHosts = installedHosts;
          response.totalHosts = totalHosts;
          response.stackVersionId = data[0].ClusterStackVersions.id;
        } else {
          response.status = '';
        }
        me.repoStatusCache[repoId] = response.status;
        deferred.resolve(response);
      })
      .catch(function (data) {
        deferred.reject(data);
      });
      return deferred.promise;
    }
  };
}]);
<|endoftext|>


<commit_before>/* eslint no-shadow: 0*/

/**
 * @fileoverview
 *  This module provides some functions for custom events.

 *  And it is implemented in the observer design pattern.
 * @author NHN Ent.
 *         FE Development Team <e0242@nhnent.com>
 * @dependency type.js, collection.js object.js
 */

(function(tui) {
    'use strict';
    /* istanbul ignore if */
    if (!tui) {
        tui = window.tui = {};
    }

    /* istanbul ignore if */
    if (!tui.util) {
        tui.util = window.tui.util = {};
    }

    var R_EVENTNAME_SPLIT = /\s+/g;  // eslint-disable-line

    /**
     * A unit of event handler item.
     * @ignore
     * @typedef {object} HandlerItem
     * @property {function} fn - event handler
     * @property {object} ctx - context of event handler
     */

    /**
     * @constructor
     * @memberof tui.util
     */
    function CustomEvents() {
        /**
         * @type {HandlerItem[]}
         */
        this.events = null;

        /**
         * only for checking specific context event was binded
         * @type {object[]}
         */
        this.contexts = null;
    }

    /**
     * Mixin custom events feature to specific constructor
     * @param {function} func - constructor
     * @example
     *  function Model() {
     *      this.name = '';
     *  }
     *  tui.util.CustomEvents.mixin(Model);
     *
     *  var model = new Model();
     *  model.on('change', function() { this.name = 'model'; }, this);
     *
     *  model.fire('change');
     *  alert(model.name); // 'model';
     */
    CustomEvents.mixin = function(func) {
        tui.util.extend(func.prototype, CustomEvents.prototype);
    };

    /**
     * Get HandlerItem object
     * @param {function} handler - handler function
     * @param {object} [context] - context for handler
     * @returns {HandlerItem} HandlerItem object
     * @private
     */
    CustomEvents.prototype._getHandlerItem = function(handler, context) {
        var item = {handler: handler};

        if (context) {
            item.context = context;
        }

        return item;
    };

    /**
     * Get event object safely
     * @param {string} [eventName] - create sub event map if not exist.
     * @returns {(object|array)} event object. if you supplied `eventName`
     *  parameter then make new array and return it
     * @private
     */
    CustomEvents.prototype._safeEvent = function(eventName) {
        var events = this.events,
            byName;

        if (!events) {
            events = this.events = {};
        }

        if (eventName) {
            byName = events[eventName];

            if (!byName) {
                byName = [];
                events[eventName] = byName;
            }

            events = byName;
        }

        return events;
    };

    /**
     * Get context array safely
     * @returns {array} context array
     */
    CustomEvents.prototype._safeContext = function() {
        var context = this.contexts;

        if (!context) {
            context = this.contexts = [];
        }

        return context;
    };

    /**
     * Get index of context
     * @param {object} ctx - context that used for bind custom event
     * @returns {number} index of context
     */
    CustomEvents.prototype._indexOfContext = function(ctx) {
        var context = this._safeContext(),
            index = 0;

        while (context[index]) {
            if (ctx === context[index][0]) {
                return index;
            }

            index += 1;
        }

        return -1;
    };

    /**
     * Memorize supplied context for recognize supplied object is context or
     *  name: handler pair object when off()
     * @param {object} ctx - context object to memorize
     */
    CustomEvents.prototype._memorizeContext = function(ctx) {
        var context, index;

        if (!tui.util.isExisty(ctx)) {
            return;
        }

        context = this._safeContext();
        index = this._indexOfContext(ctx);

        if (index > -1) {
            context[index][1] += 1;
        } else {
            context.push([ctx, 1]);
        }
    };

    /**
     * Forget supplied context object
     * @param {object} ctx - context object to forget
     */
    CustomEvents.prototype._forgetContext = function(ctx) {
        var context, contextIndex;

        if (!tui.util.isExisty(ctx)) {
            return;
        }

        context = this._safeContext();
        contextIndex = this._indexOfContext(ctx);

        if (contextIndex > -1) {
            context[contextIndex][1] -= 1;

            if (context[contextIndex][1] <= 0) {
                context.splice(contextIndex, 1);
            }
        }
    };

    /**
     * Bind event handler
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     * @private
     */
    CustomEvents.prototype._bindEvent = function(eventName, handler, context) {
        var events = this._safeEvent(eventName);
        this._memorizeContext(context);
        events.push(this._getHandlerItem(handler, context));
    };

    /**
     * Bind event handlers
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     * @example
     *  // 1. Basic
     *  customEvent.on('onload', handler);
     *
     *  // 2. With context
     *  customEvent.on('onload', handler, myObj);
     *
     *  // 3. Bind by object that name, handler pairs
     *  customEvent.on({
     *    'play': handler,
     *    'pause': handler2
     *  });
     *
     *  // 4. Bind by object that name, handler pairs with context object
     *  customEvent.on({
     *    'play': handler
     *  }, myObj);
     */
    CustomEvents.prototype.on = function(eventName, handler, context) {
        var self = this;

        if (tui.util.isString(eventName)) {
            // [syntax 1, 2]
            eventName = eventName.split(R_EVENTNAME_SPLIT);
            tui.util.forEach(eventName, function(eventName) {
                self._bindEvent(eventName, handler, context);
            });
        } else if (tui.util.isObject(eventName)) {
            // [syntax 3, 4]
            context = handler;
            tui.util.forEach(eventName, function(handler, eventName) {
                self.on(eventName, handler, context);
            });
        }
    };

    /**
     * Bind one-shot event handlers
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     */
    CustomEvents.prototype.once = function(eventName, handler, context) {
        var self = this;

        if (tui.util.isObject(eventName)) {
            context = handler;
            tui.util.forEach(eventName, function(handler, eventName) {
                self.once(eventName, handler, context);
            });
            return;
        }

        function onceHandler() {
            handler.apply(context, arguments);
            self.off(eventName, onceHandler, context);
        }

        this.on(eventName, onceHandler, context);
    };

    /**
     * Splice supplied array by omit() callback result
     * @param {array} arr - array to splice
     * @param {function} predicate - function return boolean
     * @private
     */
    CustomEvents.prototype._removeArray = function(arr, predicate) {
        var i, len;

        if (!tui.util.isArray(arr)) {
            return;
        }

        for (i = 0, len = arr.length; i < len; i += 1) {
            if (predicate(arr[i]) === true) {
                arr.splice(i, 1);
                len -= 1;
                i -= 1;
            }
        }
    };

    /**
     * Get matcher for unbind specific handler events
     * @param {function} handler - handler function
     * @returns {function} handler matcher
     * @private
     */
    CustomEvents.prototype._matchHandler = function(handler) {
        var self = this;

        return function(item) {
            var needRemove = handler === item.handler;

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Get matcher for unbind specific context events
     * @param {object} context - context
     * @returns {function} object matcher
     * @private
     */
    CustomEvents.prototype._matchContext = function(context) {
        var self = this;

        return function(item) {
            var needRemove = context === item.context;

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Get matcher for unbind specific hander, context pair events
     * @param {function} handler - handler function
     * @param {object} context - context
     * @returns {function} handler, context matcher
     * @private
     */
    CustomEvents.prototype._matchHandlerAndContext = function(handler, context) {
        var self = this;

        return function(item) {
            var matchHandler = (handler === item.handler),
                matchContext = (context === item.context),
                needRemove = (matchHandler && matchContext);

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Unbind event by event name
     * @param {string} eventName - custom event name to unbind
     * @param {function} [handler] - handler function
     * @private
     */
    CustomEvents.prototype._offByEventName = function(eventName, handler) {
        var self = this,
            forEach = tui.util.forEachArray,
            andByHandler = tui.util.isFunction(handler);

        eventName = eventName.split(R_EVENTNAME_SPLIT);

        forEach(eventName, function(eventName) {
            var handlerItems = self._safeEvent(eventName);

            if (andByHandler) {
                self._removeArray(handlerItems, self._matchHandler(handler));
            } else {
                forEach(handlerItems, function(item) {
                    self._forgetContext(item.context);
                });

                self.events[eventName] = [];
            }
        });
    };

    /**
     * Unbind event by handler function
     * @param {function} handler - handler function
     * @private
     */
    CustomEvents.prototype._offByHandler = function(handler) {
        var self = this,
            matchHandler = this._matchHandler(handler);

        tui.util.forEach(this._safeEvent(), function(handlerItems) {
            self._removeArray(handlerItems, matchHandler);
        });
    };

    /**
     * Unbind event by object(name: handler pair object or context object)
     * @param {object} obj - context or {name: handler} pair object
     * @param {function} handler - handler function
     * @private
     */
    CustomEvents.prototype._offByObject = function(obj, handler) {
        var self = this,
            matchFunc;

        if (this._indexOfContext(obj) < 0) {
            tui.util.forEach(obj, function(handler, eventName) {
                self.off(eventName, handler);
            });
        } else if (tui.util.isString(handler)) {
            matchFunc = this._matchContext(obj);

            self._removeArray(this._safeEvent(handler), matchFunc);
        } else if (tui.util.isFunction(handler)) {
            matchFunc = this._matchHandlerAndContext(handler, obj);

            tui.util.forEach(this._safeEvent(), function(handlerItems) {
                self._removeArray(handlerItems, matchFunc);
            });
        } else {
            matchFunc = this._matchContext(obj);

            tui.util.forEach(this._safeEvent(), function(handlerItems) {
                self._removeArray(handlerItems, matchFunc);
            });
        }
    };

    /**
     * Unbind custom events
     * @param {(string|object|function)} eventName - event name or context or
     *  {name: handler} pair object or handler function
     * @param {(function)} handler - handler function
     * @example
     * // 1. off by event name
     * customEvent.off('onload');
     *
     * // 2. off by event name and handler
     * customEvent.off('play', handler);
     *
     * // 3. off by handler
     * customEvent.off(handler);
     *
     * // 4. off by context
     * customEvent.off(myObj);
     *
     * // 5. off by context and handler
     * customEvent.off(myObj, handler);
     *
     * // 6. off by context and event name
     * customEvent.off(myObj, 'onload');
     *
     * // 7. off by an Object.<string, function> that is {eventName: handler}
     * customEvent.off({
     *   'play': handler,
     *   'pause': handler2
     * });
     *
     * // 8. off the all events
     * customEvent.off();
     */
    CustomEvents.prototype.off = function(eventName, handler) {
        if (tui.util.isString(eventName)) {
            // [syntax 1, 2]
            this._offByEventName(eventName, handler);
        } else if (!arguments.length) {
            // [syntax 8]
            this.events = {};
            this.contexts = [];
        } else if (tui.util.isFunction(eventName)) {
            // [syntax 3]
            this._offByHandler(eventName);
        } else if (tui.util.isObject(eventName)) {
            // [syntax 4, 5, 6]
            this._offByObject(eventName, handler);
        }
    };

    /**
     * Fire custom event
     * @param {string} eventName - name of custom event
     */
    CustomEvents.prototype.fire = function(eventName) {  // eslint-disable-line
        this.invoke.apply(this, arguments);
    };

    /**
     * Fire a event and returns the result of operation 'boolean AND' with all
     *  listener's results.
     *
     * So, It is different from {@link CustomEvents#fire}.
     *
     * In service code, use this as a before event in component level usually
     *  for notifying that the event is cancelable.
     * @param {string} eventName - Custom event name
     * @param {...*} data - Data for event
     * @returns {boolean} The result of operation 'boolean AND'
     * @example
     *  if (this.invoke('beforeZoom')) {    // check the result of 'beforeZoom'
     *      // if true,
     *      // doSomething
     *  }
     *
     *  // In service code,
     *  map.on({
     *      'beforeZoom': function() {
     *          // It should cancel the 'zoom' event by some conditions.
     *          if (that.disabled && this.getState()) {
     *              return false;
     *          }
     *          return true;
     *      }
     *  });
     */
    CustomEvents.prototype.invoke = function(eventName) {
        var events, args, index, item;

        if (!this.hasListener(eventName)) {
            return true;
        }

        events = this._safeEvent(eventName);
        args = Array.prototype.slice.call(arguments, 1);
        index = 0;

        while (events[index]) {
            item = events[index];

            if (item.handler.apply(item.context, args) === false) {
                return false;
            }

            index += 1;
        }

        return true;
    };

    /**
     * Return whether at least one of the handlers is registered in the given
     *  event name.
     * @param {string} eventName - Custom event name
     * @returns {boolean} Is there at least one handler in event name?
     */
    CustomEvents.prototype.hasListener = function(eventName) {
        return this.getListenerLength(eventName) > 0;
    };

    /**
     * Return a count of events registered.
     * @param {string} eventName - Custom event name
     * @returns {number} number of event
     */
    CustomEvents.prototype.getListenerLength = function(eventName) {
        if (!eventName) {
            return 0;
        }

        return this._safeEvent(eventName).length;
    };

    tui.util.CustomEvents = CustomEvents;
})(window.tui);
<commit_msg>refactor: _removeArray -> _spliceMatches<commit_after>/* eslint no-shadow: 0*/

/**
 * @fileoverview
 *  This module provides some functions for custom events.

 *  And it is implemented in the observer design pattern.
 * @author NHN Ent.
 *         FE Development Team <e0242@nhnent.com>
 * @dependency type.js, collection.js object.js
 */

(function(tui) {
    'use strict';
    /* istanbul ignore if */
    if (!tui) {
        tui = window.tui = {};
    }

    /* istanbul ignore if */
    if (!tui.util) {
        tui.util = window.tui.util = {};
    }

    var R_EVENTNAME_SPLIT = /\s+/g;  // eslint-disable-line

    /**
     * A unit of event handler item.
     * @ignore
     * @typedef {object} HandlerItem
     * @property {function} fn - event handler
     * @property {object} ctx - context of event handler
     */

    /**
     * @constructor
     * @memberof tui.util
     */
    function CustomEvents() {
        /**
         * @type {HandlerItem[]}
         */
        this.events = null;

        /**
         * only for checking specific context event was binded
         * @type {object[]}
         */
        this.contexts = null;
    }

    /**
     * Mixin custom events feature to specific constructor
     * @param {function} func - constructor
     * @example
     *  function Model() {
     *      this.name = '';
     *  }
     *  tui.util.CustomEvents.mixin(Model);
     *
     *  var model = new Model();
     *  model.on('change', function() { this.name = 'model'; }, this);
     *
     *  model.fire('change');
     *  alert(model.name); // 'model';
     */
    CustomEvents.mixin = function(func) {
        tui.util.extend(func.prototype, CustomEvents.prototype);
    };

    /**
     * Get HandlerItem object
     * @param {function} handler - handler function
     * @param {object} [context] - context for handler
     * @returns {HandlerItem} HandlerItem object
     * @private
     */
    CustomEvents.prototype._getHandlerItem = function(handler, context) {
        var item = {handler: handler};

        if (context) {
            item.context = context;
        }

        return item;
    };

    /**
     * Get event object safely
     * @param {string} [eventName] - create sub event map if not exist.
     * @returns {(object|array)} event object. if you supplied `eventName`
     *  parameter then make new array and return it
     * @private
     */
    CustomEvents.prototype._safeEvent = function(eventName) {
        var events = this.events,
            byName;

        if (!events) {
            events = this.events = {};
        }

        if (eventName) {
            byName = events[eventName];

            if (!byName) {
                byName = [];
                events[eventName] = byName;
            }

            events = byName;
        }

        return events;
    };

    /**
     * Get context array safely
     * @returns {array} context array
     */
    CustomEvents.prototype._safeContext = function() {
        var context = this.contexts;

        if (!context) {
            context = this.contexts = [];
        }

        return context;
    };

    /**
     * Get index of context
     * @param {object} ctx - context that used for bind custom event
     * @returns {number} index of context
     */
    CustomEvents.prototype._indexOfContext = function(ctx) {
        var context = this._safeContext(),
            index = 0;

        while (context[index]) {
            if (ctx === context[index][0]) {
                return index;
            }

            index += 1;
        }

        return -1;
    };

    /**
     * Memorize supplied context for recognize supplied object is context or
     *  name: handler pair object when off()
     * @param {object} ctx - context object to memorize
     */
    CustomEvents.prototype._memorizeContext = function(ctx) {
        var context, index;

        if (!tui.util.isExisty(ctx)) {
            return;
        }

        context = this._safeContext();
        index = this._indexOfContext(ctx);

        if (index > -1) {
            context[index][1] += 1;
        } else {
            context.push([ctx, 1]);
        }
    };

    /**
     * Forget supplied context object
     * @param {object} ctx - context object to forget
     */
    CustomEvents.prototype._forgetContext = function(ctx) {
        var context, contextIndex;

        if (!tui.util.isExisty(ctx)) {
            return;
        }

        context = this._safeContext();
        contextIndex = this._indexOfContext(ctx);

        if (contextIndex > -1) {
            context[contextIndex][1] -= 1;

            if (context[contextIndex][1] <= 0) {
                context.splice(contextIndex, 1);
            }
        }
    };

    /**
     * Bind event handler
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     * @private
     */
    CustomEvents.prototype._bindEvent = function(eventName, handler, context) {
        var events = this._safeEvent(eventName);
        this._memorizeContext(context);
        events.push(this._getHandlerItem(handler, context));
    };

    /**
     * Bind event handlers
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     * @example
     *  // 1. Basic
     *  customEvent.on('onload', handler);
     *
     *  // 2. With context
     *  customEvent.on('onload', handler, myObj);
     *
     *  // 3. Bind by object that name, handler pairs
     *  customEvent.on({
     *    'play': handler,
     *    'pause': handler2
     *  });
     *
     *  // 4. Bind by object that name, handler pairs with context object
     *  customEvent.on({
     *    'play': handler
     *  }, myObj);
     */
    CustomEvents.prototype.on = function(eventName, handler, context) {
        var self = this;

        if (tui.util.isString(eventName)) {
            // [syntax 1, 2]
            eventName = eventName.split(R_EVENTNAME_SPLIT);
            tui.util.forEach(eventName, function(eventName) {
                self._bindEvent(eventName, handler, context);
            });
        } else if (tui.util.isObject(eventName)) {
            // [syntax 3, 4]
            context = handler;
            tui.util.forEach(eventName, function(handler, eventName) {
                self.on(eventName, handler, context);
            });
        }
    };

    /**
     * Bind one-shot event handlers
     * @param {(string|{name:string, handler:function})} eventName - custom
     *  event name or an object {eventName: handler}
     * @param {(function|object)} [handler] - handler function or context
     * @param {object} [context] - context for binding
     */
    CustomEvents.prototype.once = function(eventName, handler, context) {
        var self = this;

        if (tui.util.isObject(eventName)) {
            context = handler;
            tui.util.forEach(eventName, function(handler, eventName) {
                self.once(eventName, handler, context);
            });
            return;
        }

        function onceHandler() {
            handler.apply(context, arguments);
            self.off(eventName, onceHandler, context);
        }

        this.on(eventName, onceHandler, context);
    };

    /**
     * Splice supplied array by callback result
     * @param {array} arr - array to splice
     * @param {function} predicate - function return boolean
     * @private
     */
    CustomEvents.prototype._spliceMatches = function(arr, predicate) {
        var i, len;

        if (!tui.util.isArray(arr)) {
            return;
        }

        for (i = 0, len = arr.length; i < len; i += 1) {
            if (predicate(arr[i]) === true) {
                arr.splice(i, 1);
                len -= 1;
                i -= 1;
            }
        }
    };

    /**
     * Get matcher for unbind specific handler events
     * @param {function} handler - handler function
     * @returns {function} handler matcher
     * @private
     */
    CustomEvents.prototype._matchHandler = function(handler) {
        var self = this;

        return function(item) {
            var needRemove = handler === item.handler;

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Get matcher for unbind specific context events
     * @param {object} context - context
     * @returns {function} object matcher
     * @private
     */
    CustomEvents.prototype._matchContext = function(context) {
        var self = this;

        return function(item) {
            var needRemove = context === item.context;

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Get matcher for unbind specific hander, context pair events
     * @param {function} handler - handler function
     * @param {object} context - context
     * @returns {function} handler, context matcher
     * @private
     */
    CustomEvents.prototype._matchHandlerAndContext = function(handler, context) {
        var self = this;

        return function(item) {
            var matchHandler = (handler === item.handler),
                matchContext = (context === item.context),
                needRemove = (matchHandler && matchContext);

            if (needRemove) {
                self._forgetContext(item.context);
            }

            return needRemove;
        };
    };

    /**
     * Unbind event by event name
     * @param {string} eventName - custom event name to unbind
     * @param {function} [handler] - handler function
     * @private
     */
    CustomEvents.prototype._offByEventName = function(eventName, handler) {
        var self = this,
            forEach = tui.util.forEachArray,
            andByHandler = tui.util.isFunction(handler),
            matchHandler = self._matchHandler(handler);

        eventName = eventName.split(R_EVENTNAME_SPLIT);

        forEach(eventName, function(eventName) {
            var handlerItems = self._safeEvent(eventName);

            if (andByHandler) {
                self._spliceMatches(handlerItems, matchHandler);
            } else {
                forEach(handlerItems, function(item) {
                    self._forgetContext(item.context);
                });

                self.events[eventName] = [];
            }
        });
    };

    /**
     * Unbind event by handler function
     * @param {function} handler - handler function
     * @private
     */
    CustomEvents.prototype._offByHandler = function(handler) {
        var self = this,
            matchHandler = this._matchHandler(handler);

        tui.util.forEach(this._safeEvent(), function(handlerItems) {
            self._spliceMatches(handlerItems, matchHandler);
        });
    };

    /**
     * Unbind event by object(name: handler pair object or context object)
     * @param {object} obj - context or {name: handler} pair object
     * @param {function} handler - handler function
     * @private
     */
    CustomEvents.prototype._offByObject = function(obj, handler) {
        var self = this,
            matchFunc;

        if (this._indexOfContext(obj) < 0) {
            tui.util.forEach(obj, function(handler, eventName) {
                self.off(eventName, handler);
            });
        } else if (tui.util.isString(handler)) {
            matchFunc = this._matchContext(obj);

            self._spliceMatches(this._safeEvent(handler), matchFunc);
        } else if (tui.util.isFunction(handler)) {
            matchFunc = this._matchHandlerAndContext(handler, obj);

            tui.util.forEach(this._safeEvent(), function(handlerItems) {
                self._spliceMatches(handlerItems, matchFunc);
            });
        } else {
            matchFunc = this._matchContext(obj);

            tui.util.forEach(this._safeEvent(), function(handlerItems) {
                self._spliceMatches(handlerItems, matchFunc);
            });
        }
    };

    /**
     * Unbind custom events
     * @param {(string|object|function)} eventName - event name or context or
     *  {name: handler} pair object or handler function
     * @param {(function)} handler - handler function
     * @example
     * // 1. off by event name
     * customEvent.off('onload');
     *
     * // 2. off by event name and handler
     * customEvent.off('play', handler);
     *
     * // 3. off by handler
     * customEvent.off(handler);
     *
     * // 4. off by context
     * customEvent.off(myObj);
     *
     * // 5. off by context and handler
     * customEvent.off(myObj, handler);
     *
     * // 6. off by context and event name
     * customEvent.off(myObj, 'onload');
     *
     * // 7. off by an Object.<string, function> that is {eventName: handler}
     * customEvent.off({
     *   'play': handler,
     *   'pause': handler2
     * });
     *
     * // 8. off the all events
     * customEvent.off();
     */
    CustomEvents.prototype.off = function(eventName, handler) {
        if (tui.util.isString(eventName)) {
            // [syntax 1, 2]
            this._offByEventName(eventName, handler);
        } else if (!arguments.length) {
            // [syntax 8]
            this.events = {};
            this.contexts = [];
        } else if (tui.util.isFunction(eventName)) {
            // [syntax 3]
            this._offByHandler(eventName);
        } else if (tui.util.isObject(eventName)) {
            // [syntax 4, 5, 6]
            this._offByObject(eventName, handler);
        }
    };

    /**
     * Fire custom event
     * @param {string} eventName - name of custom event
     */
    CustomEvents.prototype.fire = function(eventName) {  // eslint-disable-line
        this.invoke.apply(this, arguments);
    };

    /**
     * Fire a event and returns the result of operation 'boolean AND' with all
     *  listener's results.
     *
     * So, It is different from {@link CustomEvents#fire}.
     *
     * In service code, use this as a before event in component level usually
     *  for notifying that the event is cancelable.
     * @param {string} eventName - Custom event name
     * @param {...*} data - Data for event
     * @returns {boolean} The result of operation 'boolean AND'
     * @example
     *  if (this.invoke('beforeZoom')) {    // check the result of 'beforeZoom'
     *      // if true,
     *      // doSomething
     *  }
     *
     *  // In service code,
     *  map.on({
     *      'beforeZoom': function() {
     *          // It should cancel the 'zoom' event by some conditions.
     *          if (that.disabled && this.getState()) {
     *              return false;
     *          }
     *          return true;
     *      }
     *  });
     */
    CustomEvents.prototype.invoke = function(eventName) {
        var events, args, index, item;

        if (!this.hasListener(eventName)) {
            return true;
        }

        events = this._safeEvent(eventName);
        args = Array.prototype.slice.call(arguments, 1);
        index = 0;

        while (events[index]) {
            item = events[index];

            if (item.handler.apply(item.context, args) === false) {
                return false;
            }

            index += 1;
        }

        return true;
    };

    /**
     * Return whether at least one of the handlers is registered in the given
     *  event name.
     * @param {string} eventName - Custom event name
     * @returns {boolean} Is there at least one handler in event name?
     */
    CustomEvents.prototype.hasListener = function(eventName) {
        return this.getListenerLength(eventName) > 0;
    };

    /**
     * Return a count of events registered.
     * @param {string} eventName - Custom event name
     * @returns {number} number of event
     */
    CustomEvents.prototype.getListenerLength = function(eventName) {
        var events = this._safeEvent(eventName);
        return events.length;
    };

    tui.util.CustomEvents = CustomEvents;
})(window.tui);
<|endoftext|>


<commit_before>var Immutable = require('immutable');
const initialState = new Immutable.Map();

module.exports = function(state = initialState, action) {
  if (action.type === 'SET_MEASURES' && action.measures) {
    return Immutable.fromJS(action.measures);
  }
  if (action.type === 'CREATE_MEASURE') {
    return state.set(action.measureId, Immutable.Map({
      list: new Immutable.List()
      }));
    }

  if (action.type === 'SET_KIND') {
    if (action.kind !== 'numeric') {
      state = state.setIn([action.measureId, 'unit'], null);
    }
    if (action.kind === 'qualitative') {
      state = state.setIn([action.measureId, 'list'], null);
    }
    if (action.kind === 'list') {
      state = state.setIn([action.measureId, 'list'], Immutable.List())
      state = state.setIn([action.measureId, 'scale'], null);
    }
    if (action.kind === 'numeric') {
      state = state.setIn([action.measureId, 'scale'], null);
      state = state.setIn([action.measureId, 'list'], null);
    }
    return state.setIn([action.measureId, 'kind'], action.kind);
  }

  if (action.type === 'SET_UNIT') {
    return state.setIn([action.measureId, 'unit'], action.unit);
  }
  if (action.type === 'SET_SCALE') {
    return state.setIn([action.measureId, 'scale'], action.scale);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MIN') {
    return state.setIn([action.measureId, 'scaleDescriptionMin'], action.description);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MIDDLE') {
    return state.setIn([action.measureId, 'scaleDescriptionMiddle'], action.description);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MAX') {
    return state.setIn([action.measureId, 'scaleDescriptionMax'], action.description);
  }
  if (action.type === 'ADD_SAMPLE') {
    return state.updateIn([action.measureId, 'samples'], function(samples) {
      return samples.push(action.sampleId);
    });
  }
  if (action.type === 'ADD_LIST_ITEM') {
    var newList = state.getIn([action.measureId, 'list']);
    // Only add unique items
    if (newList.indexOf(action.item) === -1) {
      newList = newList.push(action.item);
    }
    console.log('new list new list', newList);
    return state.setIn([action.measureId, 'list'], newList);
  }
  if (action.type === 'REMOVE_LIST_ITEM') {
    var newList = state.get(action.measureId).get('list');
    // Find index of item
    var itemIndex = newList.indexOf(action.item);
    // Remove if in list
    if (itemIndex >= 0) {
      newList = newList.splice(itemIndex, 1);
    }
    return state.setIn([action.measureId, 'list'], newList);
  }
  if(action.type === 'SET_MEASURE_NAME') {
    return state.setIn([action.measureId, 'name'], action.name);
  }
  return state;
};
<commit_msg>fixed bug not being able to click on newly created exp<commit_after>var Immutable = require('immutable');
const initialState = new Immutable.Map();

module.exports = function(state = initialState, action) {
  if (action.type === 'SET_MEASURES' && action.measures) {
    return Immutable.fromJS(action.measures);
  }
  if (action.type === 'CREATE_MEASURE') {
    return state.set(action.measureId, Immutable.Map({
      list: new Immutable.List(),
      samples: new Immutable.List()
      }));
    }

  if (action.type === 'SET_KIND') {
    if (action.kind !== 'numeric') {
      state = state.setIn([action.measureId, 'unit'], null);
    }
    if (action.kind === 'qualitative') {
      state = state.setIn([action.measureId, 'list'], null);
    }
    if (action.kind === 'list') {
      state = state.setIn([action.measureId, 'list'], Immutable.List())
      state = state.setIn([action.measureId, 'scale'], null);
    }
    if (action.kind === 'numeric') {
      state = state.setIn([action.measureId, 'scale'], null);
      state = state.setIn([action.measureId, 'list'], null);
    }
    return state.setIn([action.measureId, 'kind'], action.kind);
  }

  if (action.type === 'SET_UNIT') {
    return state.setIn([action.measureId, 'unit'], action.unit);
  }
  if (action.type === 'SET_SCALE') {
    return state.setIn([action.measureId, 'scale'], action.scale);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MIN') {
    return state.setIn([action.measureId, 'scaleDescriptionMin'], action.description);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MIDDLE') {
    return state.setIn([action.measureId, 'scaleDescriptionMiddle'], action.description);
  }
  if (action.type === 'SET_SCALE_DESCRIPTION_MAX') {
    return state.setIn([action.measureId, 'scaleDescriptionMax'], action.description);
  }
  if (action.type === 'ADD_SAMPLE') {
    return state.updateIn([action.measureId, 'samples'], function(samples) {
      return samples.push(action.sampleId);
    });
  }
  if (action.type === 'ADD_LIST_ITEM') {
    var newList = state.getIn([action.measureId, 'list']);
    // Only add unique items
    if (newList.indexOf(action.item) === -1) {
      newList = newList.push(action.item);
    }
    console.log('new list new list', newList);
    return state.setIn([action.measureId, 'list'], newList);
  }
  if (action.type === 'REMOVE_LIST_ITEM') {
    var newList = state.get(action.measureId).get('list');
    // Find index of item
    var itemIndex = newList.indexOf(action.item);
    // Remove if in list
    if (itemIndex >= 0) {
      newList = newList.splice(itemIndex, 1);
    }
    return state.setIn([action.measureId, 'list'], newList);
  }
  if(action.type === 'SET_MEASURE_NAME') {
    return state.setIn([action.measureId, 'name'], action.name);
  }
  return state;
};
<|endoftext|>


<commit_before>var env = process.env.NODE_ENV || 'development';
var config = require('./knexfile');
var knex = require('knex')(config[env]);
var Promise = require('bluebird');
var parseString = Promise.promisify(require('xml2js').parseString);
var request = require('request');

var feedsTableName = 'jmaxml_feeds';

function extractUUID(feedid) {
  var colon = feedid.lastIndexOf(':');
  return feedid.substring(colon + 1);
}

function downloadText(url) {
  return new Promise(function(resolve, reject) {
    var options = {
      url: url,
      encoding: 'utf8'
    };
    request(options, function(error, response, body) {
      if (error) {
        reject(error);
      } else if (response.statusCode !== 200) {
        reject(response.statusCode);
      } else {
        resolve(body);
      }
    });
  });
};

function extractContentURL(uuid, links) {
  return links.map(function(ln) {
    return ln.$.href
  }).filter(function(href) {
    return 0 <= href.indexOf(uuid);
  })[0];
}

function createTableIfNotExists() {
  return knex.schema.hasTable(feedsTableName).then(function(exists) {
    if (!exists) {
      return knex.schema.createTable(feedsTableName, function(table) {
        table.uuid('uuid').primary();
        table.string('title').notNull();
        table.timestamp('updated').notNull();
        table.string('author');
        table.json('doc', true)
      });
    }
  });
}

function parseAtomEntries(xml) {
  return parseString(xml).then(function(atom) {
    var entries = atom.feed.entry;
    return entries.map(function(entry) {
      var uuid = extractUUID(entry.id[0]);
      var url = extractContentURL(uuid, entry.link);
      return {
        uuid: uuid,
        title: entry.title[0],
        updated: new Date(entry.updated[0]),
        author: entry.author[0].name,
        doc_url: url
      };
    });
  });
}

function downloadDocument(entry) {
  if (!entry.doc_url) {
    return Promise.reject(new Error('feed has no valid url for content'));
  } else {
    return downloadText(entry.doc_url).then(function(xml) {
      return parseString(xml);
    }).then(function(doc) {
      entry.doc = doc;
      return entry;
    });
  }
}

function loadEntry(entry) {
  return downloadDocument(entry).then(function(entryWithDoc) {
    return Promise.all([entryWithDoc, createTableIfNotExists()]);
  }).spread(function(entry) {
    return knex(feedsTableName).insert({
      uuid: entry.uuid,
      title: entry.title,
      updated: entry.updated,
      author: entry.author,
      doc: entry.doc
    });
  });
}

function loadEntries(xml) {
  return parseAtomEntries(xml).then(function(entries) {
    return entries.map(function(entry) {
      return loadEntry(entry);
    }).reduce(function(prev, curr) {
      return prev.then(function() {
        console.log('Loaded: %j', {
          title: prev.title,
          updated: prev.updated,
          author: prev.author,
          doc_url: prev.doc_url
        });
        return curr;
      });
    });
  });
}

module.exports = {
  loadEntries: loadEntries
};

<commit_msg>Fixed incorrect format of author<commit_after>var env = process.env.NODE_ENV || 'development';
var config = require('./knexfile');
var knex = require('knex')(config[env]);
var Promise = require('bluebird');
var parseString = Promise.promisify(require('xml2js').parseString);
var request = require('request');

var feedsTableName = 'jmaxml_feeds';

function extractUUID(feedid) {
  var colon = feedid.lastIndexOf(':');
  return feedid.substring(colon + 1);
}

function downloadText(url) {
  return new Promise(function(resolve, reject) {
    var options = {
      url: url,
      encoding: 'utf8'
    };
    request(options, function(error, response, body) {
      if (error) {
        reject(error);
      } else if (response.statusCode !== 200) {
        reject(response.statusCode);
      } else {
        resolve(body);
      }
    });
  });
};

function extractContentURL(uuid, links) {
  return links.map(function(ln) {
    return ln.$.href
  }).filter(function(href) {
    return 0 <= href.indexOf(uuid);
  })[0];
}

function createTableIfNotExists() {
  return knex.schema.hasTable(feedsTableName).then(function(exists) {
    if (!exists) {
      return knex.schema.createTable(feedsTableName, function(table) {
        table.uuid('uuid').primary();
        table.string('title').notNull();
        table.timestamp('updated').notNull();
        table.string('author');
        table.json('doc', true)
      });
    }
  });
}

function parseAtomEntries(xml) {
  return parseString(xml).then(function(atom) {
    var entries = atom.feed.entry;
    return entries.map(function(entry) {
      var uuid = extractUUID(entry.id[0]);
      var url = extractContentURL(uuid, entry.link);
      return {
        uuid: uuid,
        title: entry.title[0],
        updated: new Date(entry.updated[0]),
        author: entry.author[0].name[0],
        doc_url: url
      };
    });
  });
}

function downloadDocument(entry) {
  if (!entry.doc_url) {
    return Promise.reject(new Error('feed has no valid url for content'));
  } else {
    return downloadText(entry.doc_url).then(function(xml) {
      return parseString(xml);
    }).then(function(doc) {
      entry.doc = doc;
      return entry;
    });
  }
}

function loadEntry(entry) {
  return downloadDocument(entry).then(function(entryWithDoc) {
    return Promise.all([entryWithDoc, createTableIfNotExists()]);
  }).spread(function(entry) {
    return knex(feedsTableName).insert({
      uuid: entry.uuid,
      title: entry.title,
      updated: entry.updated,
      author: entry.author,
      doc: entry.doc
    });
  });
}

function loadEntries(xml) {
  return parseAtomEntries(xml).then(function(entries) {
    return entries.map(function(entry) {
      return loadEntry(entry);
    }).reduce(function(prev, curr) {
      return prev.then(function() {
        console.log('Loaded: %j', {
          title: prev.title,
          updated: prev.updated,
          author: prev.author,
          doc_url: prev.doc_url
        });
        return curr;
      });
    });
  });
}

module.exports = {
  loadEntries: loadEntries
};

<|endoftext|>


<commit_before>(function () {
    "use strict";

    var cfg         = require('nconf'),
        when        = require('when'),
        node        = require('when/node'),
        moment      = require('moment'),
        _           = require('lodash'),
        request     = require('request'),
        url         = require('url'),
        format      = require('util').format,
        CronJob     = require('cron').CronJob,
        get         = node.lift(request),
        helper      = require('../../core/server/helpers/templatehelper'),
        provider    = require('../../core/server/providers').postsProvider,
        job, apiKey, forum;

    module.exports = {
        code: "disqus-comments-synch",

        name: "Disqus Comments for Panda",

        description: "
The Disqus Comments for Panda plugin seamlessly integrates using the Disqus API and by syncing with Panda comments.
",

        configuration: "
The Disqus Comments for Panda plugin uses following configuration properties under " +
            "plugins:disqus-comments-synch configuration element:

" +
            "
cron - the time to fire off your job. " +
            "The time needs to be in the form of cron syntax.
" +
            "
forum - Disqus forum name (aka short name).
" +
            "
apiKey - Disqus API key. " +
            "The key can be teken from Disqus applications.
" +
            "
",

        author: {
            name: "Panda Team",
            url: "https://github.com/T-PWK/panda"
        },

        start: function () {
            var config = cfg.get('plugins:disqus-comments-synch');
            console.info(config)
            if (!config || !config.cron || !config.forum || !config.apiKey) {
                this.status = 'W';
                this.messages = [
                    {msg: "Plugin could not start up properly due to missing configuration."}
                ];
            } else {
                forum = config.forum;
                apiKey = config.apiKey;
                job = new CronJob(config.cron, synchronize.bind(this, this), null, true);
            }
        },

        stop: function () {
            if (job) {
                job.stop();
            }
        }
    };

    require('pkginfo')(module, 'version');

    function synchronize(plugin) {
        var info = {
            start: process.hrtime(),
            timeSpan: null,
            posts: 0,
            updates: 0
        };

        var time = process.hrtime();

        when
            .resolve(provider.findAll({ live: true }))
            .tap(function (posts) {
                info.posts = posts.length;
            })
            .then(function (posts) {

                return when.map(posts, function (post) {
                    return get({
                        uri: "https://disqus.com/api/3.0/threads/details.json",
                        qs: { api_key: apiKey, forum: forum, "thread:link": helper.postUrl(null, post, true).replace('127.0.0.1:3000', 'blog.tompawlak.org') }
                    }).spread(function (res, body) {
                        return JSON.parse(body);
                    }).then(function (obj) {
                        if (obj.code === 0) {
                            info.updates++;
                            return provider.updateProperties(post.id, {commentsCount: obj.response.posts || 0});
                        }
                    });
                });

            })
            .then(updateMessages)
            .otherwise(function (err) {
                console.error(" error ", err)
            });

        function updateMessages () {
            info.timeSpan = process.hrtime(time);

            (plugin.messages || (plugin.messages = [])).push({
                type: 'success',
                msg: format(
                    "Synchronization detail:
" +
                        "
competion time

%s
" +
                        "
time span

%d.%ds
" +
                        "
processed posts

%d
" +
                        "
updated posts

%d

",
                    moment().format('llll'), info.timeSpan[0], Math.ceil(info.timeSpan[1]/1000000), info.posts, info.updates
                )
            });
            plugin.messages = _.last(plugin.messages, 5);
        }
    }

}());<commit_msg>Plugin updates<commit_after>(function () {
    "use strict";

    var cfg         = require('nconf'),
        when        = require('when'),
        node        = require('when/node'),
        moment      = require('moment'),
        _           = require('lodash'),
        request     = require('request'),
        url         = require('url'),
        format      = require('util').format,
        CronJob     = require('cron').CronJob,
        get         = node.lift(request),
        helper      = require('../../core/server/helpers/templatehelper'),
        provider    = require('../../core/server/providers').postsProvider,
        job, apiKey, forum;

    module.exports = {
        code: "disqus-comments-synch",

        name: "Disqus Comments for Panda",

        description: "
The Disqus Comments for Panda plugin seamlessly integrates using the Disqus API and by syncing with Panda comments.
",

        configuration: "
The Disqus Comments for Panda plugin uses following configuration properties under " +
            "plugins:disqus-comments-synch configuration element:

" +
            "
cron - the time to fire off your job. " +
            "The time needs to be in the form of cron syntax.
" +
            "
forum - Disqus forum name (aka short name).
" +
            "
apiKey - Disqus API key. " +
            "The key can be teken from Disqus applications.
" +
            "
",

        author: {
            name: "Panda Team",
            url: "https://github.com/T-PWK/panda"
        },

        start: function () {
            var config = cfg.get('plugins:disqus-comments-synch');
            console.info(config)
            if (!config || !config.cron || !config.forum || !config.apiKey) {
                this.status = 'W';
                this.messages = [
                    {msg: "Plugin could not start up properly due to missing configuration."}
                ];
            } else {
                forum = config.forum;
                apiKey = config.apiKey;
                job = new CronJob(config.cron, synchronize.bind(this, this), null, true);
            }
        },

        stop: function () {
            if (job) {
                job.stop();
            }
        }
    };

    require('pkginfo')(module, 'version');

    function synchronize(plugin) {
        var info = {
            start: process.hrtime(),
            timeSpan: null,
            posts: 0,
            updates: 0
        };

        var time = process.hrtime();

        when
            .resolve(provider.findAll({ live: true }))
            .tap(function (posts) {
                info.posts = posts.length;
            })
            .then(function (posts) {

                return when.map(posts, function (post) {
                    return get({
                        uri: "https://disqus.com/api/3.0/threads/details.json",
                        qs: {
                            api_key: apiKey,
                            forum: forum,
                            "thread:link": helper.postUrl(null, post, true)
                        }
                    }).spread(function (res, body) {
                        return JSON.parse(body);
                    }).then(function (obj) {
                        if (obj.code === 0) {
                            info.updates++;
                            return provider.updateProperties(post.id, {commentsCount: obj.response.posts || 0});
                        }
                    });
                });

            })
            .then(updateMessages)
            .otherwise(function (err) {
                console.error(" error ", err)
            });

        function updateMessages () {
            info.timeSpan = process.hrtime(time);

            (plugin.messages || (plugin.messages = [])).push({
                type: 'success',
                msg: format(
                    "Synchronization detail:
" +
                        "
competion time

%s
" +
                        "
time span

%d.%ds
" +
                        "
processed posts

%d
" +
                        "
updated posts

%d

",
                    moment().format('llll'), info.timeSpan[0], Math.ceil(info.timeSpan[1]/1000000), info.posts, info.updates
                )
            });
            plugin.messages = _.last(plugin.messages, 5);
        }
    }

}());<|endoftext|>