<commit_before>/*
 * Copyright (c) 2015 Internet of Protocols Alliance (IOPA)
 *
 * Licensed 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.
 */

/*
 * Module dependencies.
 */

 const EventEmitter = require('events').EventEmitter,
    Stream = require('stream'),
    Readable = Stream.Readable,
    Writable = Stream.Writable,
    util = require('util'),

    constants = require('./constants'),
    IOPA = constants.IOPA,
    SERVER = constants.SERVER,

    cloneFilter = require('../util/shallow').cloneFilter,
    cloneKeyBehaviors = require('../util/prototype').cloneKeyBehaviors;

/**
 * Extend Context Prototype
 *
 * @method init
 * @returns (void)
 * @private
 */
 function PrototypeExtend(contextPrototype) {
     // Put Stream/EventEmitter methods on context to proxy context["iopa.Body"] methods
     cloneKeyBehaviors(contextPrototype, EventEmitter.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Stream.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Readable.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Writable.prototype, IOPA.Body, true);

     // Put Header methods on context to proxy context["iopa.Header"] methods (note assume context and context.response share prototype)
     contextPrototype.writeHead = function () { this[IOPA.WriteHead].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.getHeader = function () { this[IOPA.GetHeader].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.removeHeader = function () { this[IOPA.RemoveHeader].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.setHeader = function () { this[IOPA.SetHeader].apply(this, Array.prototype.slice.call(arguments)); };

     contextPrototype.toString = function () {

      //  return JSON.stringify(this, null, 4)
         return util.inspect(cloneFilter(this,
             [SERVER.CancelTokenSource,
                 IOPA.CancelToken,
                 SERVER.Logger,
                 "log",
                 "response",
                 IOPA.Events,
                 SERVER.CancelTokenSource,
                 IOPA.CancelToken,
                 IOPA.Events,
                 "dispose",
                 SERVER.Factory,
                  ]));

         ; //.replace(/\n/g, "\r");
     }

     contextPrototype[IOPA.WriteHead] = function iopa_WriteHead(statusCode, headers) {
         this[IOPA.StatusCode] = statusCode;

         var keys = Object.keys(headers);
         for (var i = 0; i < keys.length; i++) {
             var k = keys[i];
             if (k) {
                 this[IOPA.SetHeader].call(this, k, headers[k]);
             }
         }
     };

     contextPrototype[IOPA.SetHeader] = function iopa_SetHeader(key, val) {
         _setIgnoreCase(this[IOPA.Headers], key, val);
     }

     contextPrototype[IOPA.GetHeader] = function iopa_GetHeader(key) {
         return _getIgnoreCase(this[IOPA.Headers], key);
     }

     contextPrototype[IOPA.RemoveHeader] = function iopa_RemoveHeader(key, value) {
         return _deleteIgnoreCase(this[IOPA.Headers], key);
     }

     Object.defineProperty(contextPrototype, "log", {
                       get: function () { return  this[SERVER.Logger] ;
                       }  });

     contextPrototype.using = function(appFuncPromiseOrValue){
         if (typeof(appFuncPromiseOrValue) === 'function')
            return _using(this, appFuncPromiseOrValue(this));
         else
           return _using(this, appFuncPromiseOrValue);
     }
 }

 /*
* ES6 finally/dispose pattern for IOPA Context
* @param context Iopa
* @param p Promise or value
* returns Promise that always ultimately resolves to callback's result or rejects
*/
function _using(context, p) {

    /**  bluebird version only -- not used:
    *  	 return Promise.using(Promise.resolve(context)
    *	 .disposer(function(context, promise){
    *		  context.dispose();
              context = null;
    *	 }), cb);
    */

     return new Promise(function (resolve, reject) {
         if (typeof p === 'undefined')
             p = null;
         resolve(p);
     }).then(function (value) {
         return Promise.resolve(function () {
             process.nextTick(function () { if (context.dispose) context.dispose() });
             return value;
         } ());
     },
         function (err) {
             context.log.error(err);
             process.nextTick(function () { if (context.dispose) context.dispose() });
             throw err;
         });
 };

 /**
  * DEFAULT EXPORT
  */
 exports.addTo = PrototypeExtend;

 /**
  * Adds or updates a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @param val (string) the new property value
  * @private
  */
 function _setIgnoreCase(obj, key, val) {
     key = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key == p.toLowerCase()) {
             obj[p] = val;
             return;
         }
     }
     obj[key] = val;

 }

 /**
  * Returns a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @param val (string) the new property value
  * @private
  */
 function _getIgnoreCase(obj, key) {
     key = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key == p.toLowerCase()) {
             return obj[p];
         }
     }
     return null;
 }

 /**
  * Returns a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @return (bool) true if successful, false if not
  * @private
  */
 function _deleteIgnoreCase(obj, key) {
     key = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key == p.toLowerCase()) {
             delete obj[p];
             return true;
         }
     }
     return false;
 }

<commit_msg>dev test<commit_after>/*
 * Copyright (c) 2015 Internet of Protocols Alliance (IOPA)
 *
 * Licensed 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.
 */

/*
 * Module dependencies.
 */

 const EventEmitter = require('events').EventEmitter,
    Stream = require('stream'),
    Readable = Stream.Readable,
    Writable = Stream.Writable,
    util = require('util'),

    constants = require('./constants'),
    IOPA = constants.IOPA,
    SERVER = constants.SERVER,

    cloneFilter = require('../util/shallow').cloneFilter,
    cloneKeyBehaviors = require('../util/prototype').cloneKeyBehaviors;

/**
 * Extend Context Prototype
 *
 * @method init
 * @returns (void)
 * @private
 */
 function PrototypeExtend(contextPrototype) {
     // Put Stream/EventEmitter methods on context to proxy context["iopa.Body"] methods
     cloneKeyBehaviors(contextPrototype, EventEmitter.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Stream.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Readable.prototype, IOPA.Body, false);
     cloneKeyBehaviors(contextPrototype, Writable.prototype, IOPA.Body, true);

     // Put Header methods on context to proxy context["iopa.Header"] methods (note assume context and context.response share prototype)
     contextPrototype.writeHead = function () { this[IOPA.WriteHead].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.getHeader = function () { this[IOPA.GetHeader].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.removeHeader = function () { this[IOPA.RemoveHeader].apply(this, Array.prototype.slice.call(arguments)); };
     contextPrototype.setHeader = function () { this[IOPA.SetHeader].apply(this, Array.prototype.slice.call(arguments)); };

     contextPrototype.toString = function () {

      //  return JSON.stringify(this, null, 4)
         return util.inspect(cloneFilter(this,
             [SERVER.CancelTokenSource,
                 IOPA.CancelToken,
                 SERVER.Logger,
                 "log",
                 "response",
                 IOPA.Events,
                 SERVER.CancelTokenSource,
                 IOPA.CancelToken,
                 IOPA.Events,
                 "dispose",
                 SERVER.Factory,
                  ]));

         ; //.replace(/\n/g, "\r");
     }

     contextPrototype[IOPA.WriteHead] = function iopa_WriteHead(statusCode, headers) {
         this[IOPA.StatusCode] = statusCode;

         var keys = Object.keys(headers);
         for (var i = 0; i < keys.length; i++) {
             var k = keys[i];
             if (k) {
                 this[IOPA.SetHeader].call(this, k, headers[k]);
             }
         }
     };

     contextPrototype[IOPA.SetHeader] = function iopa_SetHeader(key, val) {
         _setIgnoreCase(this[IOPA.Headers], key, val);
     }

     contextPrototype[IOPA.GetHeader] = function iopa_GetHeader(key) {
         return _getIgnoreCase(this[IOPA.Headers], key);
     }

     contextPrototype[IOPA.RemoveHeader] = function iopa_RemoveHeader(key, value) {
         return _deleteIgnoreCase(this[IOPA.Headers], key);
     }

     Object.defineProperty(contextPrototype, "log", {
                       get: function () { return  this[SERVER.Logger] ;
                       }  });

     contextPrototype.using = function(appFuncPromiseOrValue){
         if (typeof(appFuncPromiseOrValue) === 'function')
            return _using(this, appFuncPromiseOrValue(this));
         else
           return _using(this, appFuncPromiseOrValue);
     }
 }

 /*
* ES6 finally/dispose pattern for IOPA Context
* @param context Iopa
* @param p Promise or value
* returns Promise that always ultimately resolves to callback's result or rejects
*/
function _using(context, p) {

    /**  bluebird version only -- not used:
    *  	 return Promise.using(Promise.resolve(context)
    *	 .disposer(function(context, promise){
    *		  context.dispose();
              context = null;
    *	 }), cb);
    */

     return new Promise(function (resolve, reject) {
         if (typeof p === 'undefined')
             p = null;
         resolve(p);
     }).then(function (value) {
         return Promise.resolve(function () {
             process.nextTick(function () { if (context.dispose) context.dispose() });
             return value;
         } ());
     },
         function (err) {
             context.log.error(err);
             process.nextTick(function () { if (context.dispose) context.dispose() });
             throw err;
         });
 };

 /**
  * DEFAULT EXPORT
  */
 exports.addTo = PrototypeExtend;

 /**
  * Adds or updates a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @param val (string) the new property value
  * @private
  */
 function _setIgnoreCase(obj, key, val) {
     var key_lower = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key_lower == p.toLowerCase()) {
             obj[p] = val;
             return;
         }
     }
     obj[key] = val;

 }

 /**
  * Returns a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @param val (string) the new property value
  * @private
  */
 function _getIgnoreCase(obj, key) {
     key = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key == p.toLowerCase()) {
             return obj[p];
         }
     }
     return null;
 }

 /**
  * Returns a javascript object, case insensitive for key property
  *
  * @method private_setIgnoreCase
  * @param obj (object)  the object to search
  * @param key (string) the new or existing property name
  * @return (bool) true if successful, false if not
  * @private
  */
 function _deleteIgnoreCase(obj, key) {
     key = key.toLowerCase();
     for (var p in obj) {
         if (obj.hasOwnProperty(p) && key == p.toLowerCase()) {
             delete obj[p];
             return true;
         }
     }
     return false;
 }

<|endoftext|>


<commit_before>import _ from 'lodash';
import { RECEIVE_CATEGORY_RESULTS, CLEAR_FINDER_STATE, RECEIVE_ARTICLE_PAGEVIEWS, RECEIVE_ARTICLE_PAGEASSESSMENT, RECEIVE_ARTICLE_REVISION, RECEIVE_ARTICLE_REVISIONSCORE, API_FAIL } from "../constants";
import { queryUrl, categoryQueryGenerator, pageviewQueryGenerator, pageAssessmentQueryGenerator, pageRevisionQueryGenerator, pageRevisionScoreQueryGenerator } from '../utils/article_finder_utils.js';

const mediawikiApiBase = 'https://en.wikipedia.org/w/api.php?action=query&format=json';
const oresApiBase = 'https://ores.wikimedia.org/v3/scores/enwiki';
export const fetchCategoryResults = (category, depth) => dispatch => {
  dispatch({
    type: CLEAR_FINDER_STATE
  });
  return getDataForCategory(`Category:${category}`, depth, 0, dispatch)
  .catch(response => (dispatch({ type: API_FAIL, data: response })));
};

const getDataForCategory = (category, depth, namespace = 0, dispatch) => {
  const query = categoryQueryGenerator(category, namespace);
  return queryUrl(mediawikiApiBase, query)
  .then((data) => {
    if (depth > 0) {
        depth -= 1;
        getDataForSubCategories(category, depth, namespace, dispatch);
      }
    dispatch({
      type: RECEIVE_CATEGORY_RESULTS,
      data: data.query.categorymembers
    });
    return data.query.categorymembers;
  })
  .then((data) => {
    fetchPageViews(data, dispatch);
    fetchPageAssessment(data, dispatch);
    fetchPageRevision(data, dispatch);
  });
};

export const findSubcategories = (category) => {
  const subcatQuery = categoryQueryGenerator(category, 14);
  return queryUrl(mediawikiApiBase, subcatQuery)
  .then((data) => {
    return data.query.categorymembers;
  });
};

const getDataForSubCategories = (category, depth, namespace, dispatch) => {
  return findSubcategories(category)
  .then((subcats) => {
    subcats.forEach((subcat) => {
      getDataForCategory(subcat.title, depth, namespace, dispatch);
    });
  });
};

const fetchPageViews = (articles, dispatch) => {
  articles.forEach((article) => {
    const url = pageviewQueryGenerator(article.title);
    queryUrl(url, {}, 'json')
    .then((data) => data.items)
    .then((data) => {
      const averagePageviews = Math.round((_.sumBy(data, (o) => { return o.views; }) / data.length) * 100) / 100;
      return { title: data[0].article, pageviews: averagePageviews };
    })
    .then((data) => {
      dispatch({
        type: RECEIVE_ARTICLE_PAGEVIEWS,
        data: data
      });
    })
    .catch(response => (dispatch({ type: API_FAIL, data: response })));
  });
};

const fetchPageAssessment = (articles, dispatch) => {
  const query = pageAssessmentQueryGenerator(_.map(articles, 'title'));
  queryUrl(mediawikiApiBase, query)
  .then((data) => data.query.pages)
  .then((data) => {
    _.forEach(data, (value) => {
      const title = value.title;
      const classGrade = extractClassGrade(value.pageassessments);
      dispatch({
        type: RECEIVE_ARTICLE_PAGEASSESSMENT,
        data: { title: title, classGrade: classGrade }
      });
    });
  })
  .catch(response => (dispatch({ type: API_FAIL, data: response })));
};

const extractClassGrade = (pageAssessments) => {
  let classGrade = '';
  _.forEach(pageAssessments, (pageAssessment) => {
    if (pageAssessment.class) {
      classGrade = pageAssessment.class;
      return false;
    }
  });
  return classGrade;
};

const fetchPageRevision = (articles, dispatch) => {
  const query = pageRevisionQueryGenerator(_.map(articles, 'title'));
  queryUrl(mediawikiApiBase, query)
  .then((data) => data.query.pages)
  .then((data) => {
    const revids = _.map(data, (page) => {
      return { title: page.title, revid: page.revisions[0].revid };
    });
    dispatch({
      type: RECEIVE_ARTICLE_REVISION,
      data: revids
    });
    return revids;
  })
  .then((revids) => {
    fetchPageRevisionScore(revids, dispatch);
  })
  .catch(response => (dispatch({ type: API_FAIL, data: response })));
};

const fetchPageRevisionScore = (revids, dispatch) => {
  const query = pageRevisionScoreQueryGenerator(_.map(revids, 'revid'));
  queryUrl(oresApiBase, query)
  .then((data) => data.enwiki.scores)
  .then((data) => {
    const WP10Weights = { FA: 100, GA: 80, B: 60, C: 40, Start: 20, Stub: 0 };
    const revScores = _.map(data, (scores, revid) => {
      const revScore = _.reduce(WP10Weights, (result, value, key) => {
        return result + value * scores.wp10.score.probability[key];
      }, 0);
      return { revid: revid, revScore: Math.round(revScore * 100) / 100 };
    });
    dispatch({
      type: RECEIVE_ARTICLE_REVISIONSCORE,
      data: revScores
    });
  })
  .catch(response => (dispatch({ type: API_FAIL, data: response })));
};
<commit_msg>refactor: refactor fetching pattern pageviews, pageassessments and revision scores<commit_after>import _ from 'lodash';
import { RECEIVE_CATEGORY_RESULTS, CLEAR_FINDER_STATE, RECEIVE_ARTICLE_PAGEVIEWS, RECEIVE_ARTICLE_PAGEASSESSMENT, RECEIVE_ARTICLE_REVISION, RECEIVE_ARTICLE_REVISIONSCORE, API_FAIL } from "../constants";
import { queryUrl, categoryQueryGenerator, pageviewQueryGenerator, pageAssessmentQueryGenerator, pageRevisionQueryGenerator, pageRevisionScoreQueryGenerator } from '../utils/article_finder_utils.js';

const mediawikiApiBase = 'https://en.wikipedia.org/w/api.php?action=query&format=json';
const oresApiBase = 'https://ores.wikimedia.org/v3/scores/enwiki';

export const fetchCategoryResults = (category, depth) => dispatch => {
  dispatch({
    type: CLEAR_FINDER_STATE
  });
  return getDataForCategory(`Category:${category}`, depth, 0, dispatch)
  .catch(response => (dispatch({ type: API_FAIL, data: response })));
};

const getDataForCategory = (category, depth, namespace = 0, dispatch) => {
  const query = categoryQueryGenerator(category, namespace);
  return queryUrl(mediawikiApiBase, query)
  .then((data) => {
    if (depth > 0) {
        depth -= 1;
        getDataForSubCategories(category, depth, namespace, dispatch);
      }
    dispatch({
      type: RECEIVE_CATEGORY_RESULTS,
      data: data.query.categorymembers
    });
    return data.query.categorymembers;
  })
  .then((data) => {
    fetchPageViews(data, dispatch);
  });
};

export const findSubcategories = (category) => {
  const subcatQuery = categoryQueryGenerator(category, 14);
  return queryUrl(mediawikiApiBase, subcatQuery)
  .then((data) => {
    return data.query.categorymembers;
  });
};

const getDataForSubCategories = (category, depth, namespace, dispatch) => {
  return findSubcategories(category)
  .then((subcats) => {
    subcats.forEach((subcat) => {
      getDataForCategory(subcat.title, depth, namespace, dispatch);
    });
  });
};

const fetchPageViews = (articles, dispatch) => {
  const promises = articles.map((article) => {
    const url = pageviewQueryGenerator(article.title);
    return queryUrl(url, {}, 'json')
    .then((data) => data.items)
    .then((data) => {
      const averagePageviews = Math.round((_.sumBy(data, (o) => { return o.views; }) / data.length) * 100) / 100;
      return { title: data[0].article, pageviews: averagePageviews };
    })
    .then((data) => {
      dispatch({
        type: RECEIVE_ARTICLE_PAGEVIEWS,
        data: data
      });
    })
    .catch(response => (dispatch({ type: API_FAIL, data: response })));
  });

  Promise.all(promises)
  .then(() => {
    fetchPageAssessment(articles, dispatch);
  });
};

const fetchPageAssessment = (articlesList, dispatch) => {
  const promises = _.chunk(articlesList, 20).map((articles) => {
    const query = pageAssessmentQueryGenerator(_.map(articles, 'title'));

    return queryUrl(mediawikiApiBase, query)
    .then((data) => data.query.pages)
    .then((data) => {
      _.forEach(data, (value) => {
        const title = value.title;
        const classGrade = extractClassGrade(value.pageassessments);
        dispatch({
          type: RECEIVE_ARTICLE_PAGEASSESSMENT,
          data: { title: title, classGrade: classGrade }
        });
      });
    })
    .catch(response => (dispatch({ type: API_FAIL, data: response })));
  });

  Promise.all(promises)
  .then(() => {
    fetchPageRevision(articlesList, dispatch);
  });
};

const extractClassGrade = (pageAssessments) => {
  let classGrade = '';
  _.forEach(pageAssessments, (pageAssessment) => {
    if (pageAssessment.class) {
      classGrade = pageAssessment.class;
      return false;
    }
  });
  return classGrade;
};

const fetchPageRevision = (articlesList, dispatch) => {
  _.chunk(articlesList, 20).forEach((articles) => {
    const query = pageRevisionQueryGenerator(_.map(articles, 'title'));
    queryUrl(mediawikiApiBase, query)
    .then((data) => data.query.pages)
    .then((data) => {
      const revids = _.map(data, (page) => {
        return { title: page.title, revid: page.revisions[0].revid };
      });
      dispatch({
        type: RECEIVE_ARTICLE_REVISION,
        data: revids
      });
      return revids;
    })
    .then((revids) => {
      fetchPageRevisionScore(revids, dispatch);
    })
    .catch(response => (dispatch({ type: API_FAIL, data: response })));
  });
};

const fetchPageRevisionScore = (revids, dispatch) => {
    const query = pageRevisionScoreQueryGenerator(_.map(revids, 'revid'));
    queryUrl(oresApiBase, query)
    .then((data) => data.enwiki.scores)
    .then((data) => {
      const WP10Weights = { FA: 100, GA: 80, B: 60, C: 40, Start: 20, Stub: 0 };
      const revScores = _.map(data, (scores, revid) => {
        const revScore = _.reduce(WP10Weights, (result, value, key) => {
          return result + value * scores.wp10.score.probability[key];
        }, 0);
        return { revid: revid, revScore: Math.round(revScore * 100) / 100 };
      });
      dispatch({
        type: RECEIVE_ARTICLE_REVISIONSCORE,
        data: revScores
      });
    })
    .catch(response => (dispatch({ type: API_FAIL, data: response })));
};
<|endoftext|>


<commit_before>/**
 * Resolve the properties of an object in a runtime.
 *
 * Singleton. Every instanciation will return the same instance. Contains
 * no state apart from the caching, which should be shared.
 */

window.cls = window.cls || {};
window.cls.PropertyFinder = function(rt_id) {
  if (window.cls.PropertyFinder.instance) {
    return window.PropertyFinder.instance;
  }
  else {
    window.cls.PropertyFinder.instance = this;
  }

  // this cond is here so we can instanciate the class even without running
  // with scope. Means we can run tests on functions that don't require
  // scope.
  if (window.services)
  {
    this._service = window.services['ecmascript-debugger'];
  }

  this._cache = {};

  /**
   * Method that does The Right Thing with regards to what method is
   * available for requesting an eval() in a connected Opera instance.
   *
   * Subclasses that adds support for never ecma service versions can
   * override this method
   *
   */
  this._requestEval = function(callback, js, scope, identifier, input, frameinfo) {
    var tag = tagManager.set_callback(this, this._onRequestEval,
                                      [callback, scope, identifier, input, frameinfo]);

    this._service.requestEval(
      tag, [frameinfo.runtime_id, frameinfo.thread_id, frameinfo.index, js]
    );
  };

  /**
   * Figure out the object to which input belongs.
   * foo.bar -> foo
   * window.bleh.meh -> window.bleh
   * phlebotinum -> this
   * phlebotinum. -> phlebotinum
   * foo(bar.bleh -> bar
   * foo[window -> window
   * foo[bar].a -> foo[bar]
   */
  this._find_input_parts = function(input)
  {
    var last_bracket = input.lastIndexOf('[');
    var last_brace = input.lastIndexOf('(');

    last_brace = input.lastIndexOf(')') <= last_brace ? last_brace : -1;
    last_bracket = input.lastIndexOf(']') <= last_bracket ? last_bracket : -1;
    input = input.slice( Math.max(
                  last_brace,
                  last_bracket,
                  input.lastIndexOf('=') ) + 1
                ).replace(/^ +/, '').replace(/ $/, '');

    var last_dot = input.lastIndexOf('.');
    var new_path = '';
    var new_id = '';
    var ret = '';

    if(last_dot > -1)
    {
      new_path = input.slice(0, last_dot);
      new_id = input.slice(last_dot + 1);
    }
    else
    {
      new_id = input;
    }

    return {scope: new_path, identifier: new_id};

//    return
    var scope = "";
    var index = input.lastIndexOf(".");

    if (index > 0) {
      scope = input.substring(0, index);
    }

    return scope;
  };

  this._onRequestEval = function(status, message, callback, scope, identifier, input, frameinfo) {
    var ret = {
      props: [],
      scope: scope,
      input: input,
      identifier: identifier,
      frameinfo: frameinfo
    };

    if (status == 0) {
      const STATUS = 0, TYPE = 1, VALUE = 2, OBJECT_VALUE = 3;
      if(message[STATUS] == 'completed')
      {
        if(message[VALUE])
        {
          ret.props = message[VALUE].split('_,_').filter(
                        function(e) { return e != ""; }
          );
        }
      }
    }

    this._cache_put(ret);
    ret.input = input;
    callback(ret);
  };

  /**
   * Returns a list of properties that match the input string in the given
   * runtime.
   *
   */
  this.find_props = function(callback, input, frameinfo) {
      frameinfo = frameinfo || {
        runtime_id: runtimes.getSelectedRuntimeId(),
        thread_id: 0,
        scope_id: null,
        index: 0
      };

    //rt_id, thread_id, frame_id) {

    var parts = this._find_input_parts(input);

    var props = this._cache_get(parts.scope, frameinfo);
    if (props) {
      props.input = input;
      props.identifier = parts.identifier;
      callback(props);
    }
    else {
      this._get_scope_contents(callback, parts.scope, parts.identifier, input, frameinfo);
    }
  };

  /**
   * Tell the caching mechanism that it need no longer keep track of data
   * about a particular runtime. Can be hooked up to messages about closed
   * tabs/runtimes
   */
  this.forget_runtime = function(rt_id) {
    // fixme
  };

  this._cache_key = function(scope, frameinfo) {
    return "" + frameinfo.scope_id + "." + frameinfo.runtime_id + "." + frameinfo.thread_id + "." + frameinfo.index;
  };

  this._cache_put = function(result)
  {
    var key = this._cache_key(result.scope, result.frameinfo);
    this._cache[key] = result;
  };

  this._cache_get = function(scope, frameinfo) {
    var key = this._cache_key(scope, frameinfo);
    return this._cache[key];
  };

  this._get_scope_contents = function(callback, scope, identifier, input, frameinfo) {
    var script = "(function(){var a = '', b= ''; for( a in %s ){ b += a + '_,_'; }; return b;})()";
    var eval_str = script.replace("%s", scope||"this");

    if (frameinfo.index !== undefined) {
      this._requestEval(callback, eval_str, scope, identifier, input, frameinfo);
    }
    // fixme: use inspectobject if we're in a stopped state
  };

  this.toString = function() {
    return "[PropertyFinder singleton instance]";
  };
};
<commit_msg>Fixed bug where cache keys were not generated properly<commit_after>/**
 * Resolve the properties of an object in a runtime.
 *
 * Singleton. Every instanciation will return the same instance. Contains
 * no state apart from the caching, which should be shared.
 */

window.cls = window.cls || {};
window.cls.PropertyFinder = function(rt_id) {
  if (window.cls.PropertyFinder.instance) {
    return window.PropertyFinder.instance;
  }
  else {
    window.cls.PropertyFinder.instance = this;
  }

  // this cond is here so we can instanciate the class even without running
  // with scope. Means we can run tests on functions that don't require
  // scope.
  if (window.services)
  {
    this._service = window.services['ecmascript-debugger'];
  }

  this._cache = {};

  /**
   * Method that does The Right Thing with regards to what method is
   * available for requesting an eval() in a connected Opera instance.
   *
   * Subclasses that adds support for never ecma service versions can
   * override this method
   *
   */
  this._requestEval = function(callback, js, scope, identifier, input, frameinfo) {
    var tag = tagManager.set_callback(this, this._onRequestEval,
                                      [callback, scope, identifier, input, frameinfo]);

    this._service.requestEval(
      tag, [frameinfo.runtime_id, frameinfo.thread_id, frameinfo.index, js]
    );
  };

  /**
   * Figure out the object to which input belongs.
   * foo.bar -> foo
   * window.bleh.meh -> window.bleh
   * phlebotinum -> this
   * phlebotinum. -> phlebotinum
   * foo(bar.bleh -> bar
   * foo[window -> window
   * foo[bar].a -> foo[bar]
   */
  this._find_input_parts = function(input)
  {
    var last_bracket = input.lastIndexOf('[');
    var last_brace = input.lastIndexOf('(');

    last_brace = input.lastIndexOf(')') <= last_brace ? last_brace : -1;
    last_bracket = input.lastIndexOf(']') <= last_bracket ? last_bracket : -1;
    input = input.slice( Math.max(
                  last_brace,
                  last_bracket,
                  input.lastIndexOf('=') ) + 1
                ).replace(/^ +/, '').replace(/ $/, '');

    var last_dot = input.lastIndexOf('.');
    var new_path = '';
    var new_id = '';
    var ret = '';

    if(last_dot > -1)
    {
      new_path = input.slice(0, last_dot);
      new_id = input.slice(last_dot + 1);
    }
    else
    {
      new_id = input;
    }

    return {scope: new_path, identifier: new_id};

//    return
    var scope = "";
    var index = input.lastIndexOf(".");

    if (index > 0) {
      scope = input.substring(0, index);
    }

    return scope;
  };

  this._onRequestEval = function(status, message, callback, scope, identifier, input, frameinfo) {
    var ret = {
      props: [],
      scope: scope,
      input: input,
      identifier: identifier,
      frameinfo: frameinfo
    };

    if (status == 0) {
      const STATUS = 0, TYPE = 1, VALUE = 2, OBJECT_VALUE = 3;
      if(message[STATUS] == 'completed')
      {
        if(message[VALUE])
        {
          ret.props = message[VALUE].split('_,_').filter(
                        function(e) { return e != ""; }
          );
        }
      }
    }

    this._cache_put(ret);
    ret.input = input;
    callback(ret);
  };

  /**
   * Returns a list of properties that match the input string in the given
   * runtime.
   *
   */
  this.find_props = function(callback, input, frameinfo) {
      frameinfo = frameinfo || {
        runtime_id: runtimes.getSelectedRuntimeId(),
        thread_id: 0,
        scope_id: null,
        index: 0
      };

    //rt_id, thread_id, frame_id) {

    var parts = this._find_input_parts(input);

    var props = this._cache_get(parts.scope, frameinfo);
    if (props) {
      props.input = input;
      props.identifier = parts.identifier;
      callback(props);
    }
    else {
      this._get_scope_contents(callback, parts.scope, parts.identifier, input, frameinfo);
    }
  };

  /**
   * Tell the caching mechanism that it need no longer keep track of data
   * about a particular runtime. Can be hooked up to messages about closed
   * tabs/runtimes
   */
  this.forget_runtime = function(rt_id) {
    // fixme
  };

  this._cache_key = function(scope, frameinfo) {
    return "" + scope + "." + frameinfo.runtime_id + "." + frameinfo.thread_id + "." + frameinfo.index;
  };

  this._cache_put = function(result)
  {
    var key = this._cache_key(result.scope, result.frameinfo);
    this._cache[key] = result;
  };

  this._cache_get = function(scope, frameinfo) {
    var key = this._cache_key(scope, frameinfo);
    return this._cache[key];
  };

  this._get_scope_contents = function(callback, scope, identifier, input, frameinfo) {
    var script = "(function(scope){var a = '', b= ''; for( a in scope ){ b += a + '_,_'; }; return b;})(%s)";
    var eval_str = script.replace("%s", scope||"this");

    if (frameinfo.index !== undefined) {
      this._requestEval(callback, eval_str, scope, identifier, input, frameinfo);
    }
    // fixme: use inspectobject if we're in a stopped state
  };

  this.toString = function() {
    return "[PropertyFinder singleton instance]";
  };
};
<|endoftext|>


<commit_before>(function (undefined) {
    "use strict";
    function Metadata(repositoryFactory, $q) {
        //Aqui guardamos cada uno los metadatos de cada entidad
        var metadataCache = {
            entities: {},
            getBestExistsProperty: function (entityName, propertyName) {
                propertyName = propertyName || "";
                if (propertyName.indexOf(",") >= 0) {
                    throw new Error("No se permiten comas en el nombre de la propiedad");
                }

                var keys = propertyName.split('.');
                var propertyExists = "";
                var current = this.getMetadata(entityName);
                if (!((keys.length === 1) && (keys[0] === ""))) {
                    for (var i = 0; i < keys.length; i++) {
                        current = current.properties[keys[i]];
                        if (current === undefined) {
                            return propertyExists;
                        } else {
                            propertyExists = propertyExists + (propertyExists === "" ? "" : ".") + keys[i];
                        }
                    }
                }
                return propertyExists;
            },
            existsMetadata: function (entityName, expands) {
                var metadata = this.getMetadata(entityName);
                if (metadata === null) {
                    return false;
                }

                expands = expands || "";
                var arrExpand = expands.split(",");
                if (!((arrExpand.length === 1) && (arrExpand[0] === ""))) {
                    for (var i = 0; i < arrExpand.length; i++) {
                        if (metadata.getMetadataProperty(arrExpand[i]) === null) {
                            return false;
                        }
                    }
                }
                return true;
            },
            getMetadata: function (entityName) {
                var metadata = this.entities[entityName];
                if (metadata === undefined) {
                    return null;
                } else {
                    return metadata;
                }
            },
            addMetadata: function (entityName, metadata) {
                if (this.getMetadata(entityName) === null) {
                    this.entities[entityName] = metadata;
                }
            }
        };
        var metadata = {
            load: load,
            getMetadata: getMetadata
        };
        return metadata;
        function load(entityName, expands) {
            var deferred = $q.defer();
            var exists = metadataCache.existsMetadata(entityName, expands);
            if (exists === true) {
                deferred.resolve(metadataCache.getMetadata(entityName));
            } else {
                var repository = repositoryFactory.getRepository(entityName);
                repository.metadata(expands).then(function (newMetadata) {
                    //Ahora hay que fusionar lo que acaba de llegar con lo que hay
                    if (metadataCache.getMetadata(entityName) === null) {
                        //No hay nada así que añadimos todo lo que acabamos de cargar
                        metadataCache.addMetadata(entityName, newMetadata);
                    } else {
                        //Ahora hay que añadir parte por parte de lo que falta.
                        var currentMetadata = metadataCache.getMetadata(entityName);
                        expands = expands || "";
                        var arrExpand = expands.split(",");
                        if (!((arrExpand.length === 1) && (arrExpand[0] === ""))) {
                            for (var i = 0; i < arrExpand.length; i++) {
                                var expand = arrExpand[i];
                                //Vamos a comprobar el expand que falta
                                if (metadataCache.existsMetadata(entityName, expand) === false) {
                                    //La variable existExpand es la parte que si que existe
                                    var existExpand = metadataCache.getBestExistsProperty(entityName,expand);
                                    var nextProperty;
                                    if (existExpand === "") {
                                        nextProperty = expand;
                                    } else {
                                        nextProperty = expand.substring(existExpand.length + 1);
                                    }
                                    nextProperty = nextProperty.split(".")[0];
                                    currentMetadata.getMetadataProperty(existExpand).properties[nextProperty] = newMetadata.getMetadataProperty(existExpand).properties[nextProperty];
                                }
                            }
                        }

                    }

                    deferred.resolve(metadataCache.getMetadata(entityName));
                }, function (data) {
                    deferred.reject(data);
                });
            }

            return deferred.promise;
        }

        function getMetadata(entityName) {
            return metadataCache.getMetadata(entityName);
        }

    }
    Metadata.$inyect = ["repositoryFactory", "$q"];
    angular.module("es.logongas.ix3").factory("metadata", Metadata);
})();<commit_msg>Reorganizar el código de "metadata"<commit_after>(function (undefined) {
    "use strict";

    function Metadata(repositoryFactory, $q) {
        //Aqui guardamos cada uno los metadatos de cada entidad
        var metadatasStorage = new MetadatasStorage();

        var metadata = {
            load: load,
            getMetadata: getMetadata
        };

        return metadata;

        function load(entityName, expands) {
            var deferred = $q.defer();

            var arrExpands = splitValues(expands, ",");

            var exists = metadatasStorage.existsMetadata(entityName, arrExpands);
            if (exists === true) {
                deferred.resolve(metadatasStorage.getMetadata(entityName));
            } else {
                var repository = repositoryFactory.getRepository(entityName);
                repository.metadata(expands).then(function (newMetadata) {
                    //Ahora hay que fusionar lo que acaba de llegar con lo que hay
                    if (metadatasStorage.getMetadata(entityName) === null) {
                        //No hay nada así que añadimos todo lo que acabamos de cargar
                        metadatasStorage.addMetadata(entityName, newMetadata);
                    } else {
                        //Ahora hay que añadir parte por parte de lo que falta.
                        var currentMetadata = metadatasStorage.getMetadata(entityName);

                        for (var i = 0; i < arrExpands.length; i++) {
                            var expand = arrExpands[i];
                            //Vamos a comprobar el expand que falta
                            if (metadatasStorage.existsMetadata(entityName, expand) === false) {
                                //La variable existExpand es la parte que si que existe
                                var existExpand = metadatasStorage.getBestExistsProperty(entityName, expand);
                                var nextProperty;
                                if (existExpand === "") {
                                    nextProperty = expand;
                                } else {
                                    nextProperty = expand.substring(existExpand.length + 1);
                                }
                                nextProperty = nextProperty.split(".")[0];
                                currentMetadata.getMetadataProperty(existExpand).properties[nextProperty] = newMetadata.getMetadataProperty(existExpand).properties[nextProperty];
                            }
                        }

                    }

                    deferred.resolve(metadatasStorage.getMetadata(entityName));
                }, function (data) {
                    deferred.reject(data);
                });
            }

            return deferred.promise;
        }

        function getMetadata(entityName) {
            return metadatasStorage.getMetadata(entityName);
        }

    }
    Metadata.$inyect = ["repositoryFactory", "$q"];

    angular.module("es.logongas.ix3").factory("metadata", Metadata);

    /**
     * Clase para gestionar varios objetos Metadata
     * Y guarda cada uno de los metadatos de cada entidad.
     */
    function MetadatasStorage() {

        /**
         * Aqui guardamos cada uno de los metadatos de cada entidad
         */
        this.entities = {};

        this.addMetadata = function (entityName, metadata) {
            if (this.getMetadata(entityName) === null) {
                this.entities[entityName] = metadata;
            }
        };

        this.getMetadata = function (entityName) {
            var metadata = this.entities[entityName];
            if (metadata === undefined) {
                return null;
            } else {
                return metadata;
            }
        };

        /**
         *
         * @param {String} entityName Nombre de la entidad
         * @param {Array[String]} arrExpands Los expandido que debe estar el metadata. Se permiten varios valores , por eso es un array
         * @returns {Boolean} Si esta entidad existe y está expanjdida como dicen todos y cada uno de los valores de "expands"
         */
        this.existsMetadata = function (entityName, arrExpands) {
            var metadata = this.getMetadata(entityName);
            if (metadata === null) {
                return false;
            }

            for (var i = 0; i < arrExpands.length; i++) {
                if (metadata.getMetadataProperty(arrExpands[i]) === null) {
                    return false;
                }
            }

            return true;
        };

        /**
         * Dado un "expand" obtiene el "expand" mas cercado que tiene esa propiedad.
         * Sirva para saber que parte ya está cargada de ese expand.
         * @param {type} entityName Nombre de la entidad
         * @param {type} propertyName Es como expand pero con un solo valor.
         * @returns {String}
         */
        this.getBestExistsProperty = function (entityName, propertyName) {
            propertyName = propertyName || "";
            if (propertyName.indexOf(",") >= 0) {
                throw new Error("No se permiten comas en el nombre de la propiedad");
            }

            var propertyExists = "";
            var current = this.getMetadata(entityName);

            var keys = splitValues(propertyName, ".");
            for (var i = 0; i < keys.length; i++) {
                current = current.properties[keys[i]];
                if (current === undefined) {
                    return propertyExists;
                } else {
                    //El operador ternario es para el primer elemento que no lleva "." al concatenar.
                    propertyExists = propertyExists + (propertyExists === "" ? "" : ".") + keys[i];
                }
            }

            return propertyExists;
        };

    }

    /**
     * Hace un split pero si solo hay un elemento en el array y es "", retorna un array vacio.
     * Si no hay nada retorna un array sin elementos.
     * @param {String} values
     * @param {String} separator El separador
     * @returns {Array[String]}
     */
    function splitValues(values, separator) {
        values = values || "";
        var arrValues = values.split(separator);
        if ((arrValues.length === 1) && (arrValues[0] === "")) {
            return [];
        } else {
            return arrValues;
        }
    }

})();<|endoftext|>


<commit_before>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Store Server Things",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Server Control",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const servers = ['Current Server', 'Temp Variable', 'Server Variable', 'Global Variable'];
	const info = ['Creation Date', 'Time to AFK', 'Is server available?', 'More than 250 members?', 'Date bot joined server', 'Total Channel Amount', 'Emoji amount', 'Embed Links', 'DND Members Count', 'Online Members Count (fixed)', 'Offline Members Count', 'Idle Members Count', 'Total Bots Count in Server', 'Server Channel IDs', 'Server Role IDs', 'Server Member IDs', 'Server Bot Count', 'Server Human Member Count', 'Server Member Count', 'Text Channel Amount', 'Voice Channel Amount'];
	return `${servers[parseInt(data.server)]} - ${info[parseInt(data.info)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "Lasse, EGGSY & EliteArtz",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.8.7",
	 //19&20&23 Added by Invarion in 1.8.8 | Taken from bashy's mod
	 //21&22 Added by Invarion in 1.8.8 |

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Stores more Server Information",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

variableStorage: function(data, varType) {
	const type = parseInt(data.storage);
	if(type !== varType) return;
	const info = parseInt(data.info);
	let dataType = 'Text';
	switch(info) {
		case 0:
			dataType = "Date";
			break;
		case 1:
			dataType = "Number";
			break;
		case 2:
			dataType = "Boolean";
			break;
		case 3:
			dataType = "Boolean";
			break;
		case 4:
			dataType = "Date";
			break;
		case 5:
			dataType = "Number";
			break;
		case 6:
			dataType = "Number";
			break;
		case 7:
			dataType = "Boolean";
			break;
		case 8:
			dataType = "Number";
			break;
		case 9:
			dataType = "Number";
			break;
		case 10:
			dataType = "Number";
			break;
		case 11:
			dataType = "Number";
			break;
		case 12:
			dataType = "Number";
			break;
		case 13:
			dataType = 'Server Channel IDs';
			break;
		case 14:
			dataType = 'Server Role IDs';
			break;
		case 15:
			dataType = 'Server Member IDs';
			break;
		case 16:
			dataType = 'Number';
			break;
		case 17:
			dataType = 'Number';
			break;
		case 18:
			dataType = 'Number';
			break;
		case 19:
			dataType = 'Number';
			break;
		case 20:
			dataType = 'Number';
			break;
	}
	return ([data.varName2, dataType]);
},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["server", "varName", "info", "storage", "varName2"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

			Mod Info:

			Created by EGGSY, EliteArtz, Invarion & Lasse!

		Source Server:

		<select id="server" class="round" onchange="glob.serverChange(this, 'varNameContainer')">
			${data.servers[isEvent ? 1 : 0]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Source Info:

		<select id="info" class="round">
			<option value="0" selected>Servers Creation Date</option>
			<option value="1">Time User gets AFK</option>
			<option value="2">Is Server available?</option>
			<option value="4">Date Bot Joined</option>
			<option value="5">Total Channel Amount</option>
			<option value="19">Text Channel Amount</option>
			<option value="20">Voice Channel Amount</option>
			<option value="21">Text Channel List</option>
			<option value="22">Voice Channel List</option>
			<option value="6">Server Emoji Amount</option>
			<option value="7">Embeds links?</option>
			<option value="9">Online Members Count</option
			<option value="11">Idle Members Count</option>
			<option value="8">DND Members Count</option>
			<option value="10">Offline Members Count</option>
			<option value="13">Server Channel IDs</option>
			<option value="14">Server Role IDs</option>
			<option value="15">Server Member IDs</option>
      <option value="18">Server Member Count</option>
			<option value="16">Server Bot Count</option>
			<option value="17">Server Human Member Count</option>
			<option value="12">Total Bots in Servers</option>
			</select>

		Store In:

		<select id="storage" class="round">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

	glob.serverChange(document.getElementById('server'), 'varNameContainer')
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const server = parseInt(data.server);
	const varName = this.evalMessage(data.varName, cache);
	const info = parseInt(data.info);
	const targetServer = this.getServer(server, varName, cache);
	if(!targetServer) {
		this.callNextAction(cache);
		return;
	}
	let result;
	switch(info) {
		case 0:
			result = targetServer.createdAt;
			break;
		case 1:
			result = targetServer.afkTimeout;
			break;
		case 2:
			result = targetServer.available;
			break;
		case 3:
			result = targetServer.large; //Deprecated in v1.8.5
			break;
		case 4:
			result = targetServer.joinedAt;
			break;
		case 5:
			result = targetServer.channels.array().length;
			break;
		case 6:
			result = targetServer.emojis.array().length;
			break;
		case 7:
			result = targetServer.embedEnabled;
			break;
		case 8:
			result = targetServer.members.filter(m => m.user.presence.status == "dnd").size;
			break;
		case 9:
			result = targetServer.members.filter(m => m.user.presence.status == "online").size;
			break;
		case 10:
			result = targetServer.members.filter(m => m.user.presence.status == "offline").size;
			break;
		case 11:
			result = targetServer.members.filter(m => m.user.presence.status == "idle").size;
			break;
		case 12:
			result = targetServer.members.filter(m => m.user.bot).size;
			break;
		case 13:
			result = targetServer.channels.map(channels => channels.id);
			break;
		case 14:
			result = targetServer.roles.map(roles => roles.id);
			break;
		case 15:
			result = targetServer.members.map(members => members.id);
			break;
		case 16:
			result = targetServer.members.filter(m => m.user.bot == true).size;
			break;
		case 17:
			result = targetServer.members.filter(m => m.user.bot == false).size;
			break;
		case 18:
			result = targetServer.memberCount; //Added by Lasse in 1.8.7
			break;
		case 19:
			result = targetServer.channels.findAll('type', 'text').length; //Added by Invarion in 1.8.8 | Taken from bashy's mod
			break;
		case 20:
			result = targetServer.channels.findAll('type', 'voice').length; //Added by Invarion in 1.8.8 | Taken from bashy's mod
			break;
		case 21:
			result = targetServer.channels.findAll('type', 'text');  //Added by Invarion in 1.8.8
	    break;
		case 22:
	    result = targetServer.channels.findAll('type', 'voice'); //Added by Invarion in 1.8.8
		default:
			break;
	}
	if(result !== undefined) {
		const storage = parseInt(data.storage);
		const varName2 = this.evalMessage(data.varName2, cache);
		this.storeValue(result, storage, varName2, cache);
	}
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<commit_msg>Update store_server_info_MOD.js<commit_after>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Store Server Things",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Server Control",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const servers = ['Current Server', 'Temp Variable', 'Server Variable', 'Global Variable'];
	const info = ['Creation Date', 'Time to AFK', 'Is server available?', 'More than 250 members?', 'Date bot joined server', 'Total Channel Amount', 'Emoji amount', 'Embed Links', 'DND Members Count', 'Online Members Count (fixed)', 'Offline Members Count', 'Idle Members Count', 'Total Bots Count in Server', 'Server Channel IDs', 'Server Role IDs', 'Server Member IDs', 'Server Bot Count', 'Server Human Member Count', 'Server Member Count', 'Text Channel Amount', 'Voice Channel Amount'];
	return `${servers[parseInt(data.server)]} - ${info[parseInt(data.info)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "Lasse, EGGSY & EliteArtz",

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.8.7",
	 //19&20 Added by Invarion in 1.8.8 | Taken from bashy's mod
	 //21&22 Added by Invarion in 1.8.8 |

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Stores more Server Information",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Storage Function
//
// Stores the relevant variable info for the editor.
//---------------------------------------------------------------------

variableStorage: function(data, varType) {
	const type = parseInt(data.storage);
	if(type !== varType) return;
	const info = parseInt(data.info);
	let dataType = 'Text';
	switch(info) {
		case 0:
			dataType = "Date";
			break;
		case 1:
			dataType = "Number";
			break;
		case 2:
			dataType = "Boolean";
			break;
		case 3:
			dataType = "Boolean";
			break;
		case 4:
			dataType = "Date";
			break;
		case 5:
			dataType = "Number";
			break;
		case 6:
			dataType = "Number";
			break;
		case 7:
			dataType = "Boolean";
			break;
		case 8:
			dataType = "Number";
			break;
		case 9:
			dataType = "Number";
			break;
		case 10:
			dataType = "Number";
			break;
		case 11:
			dataType = "Number";
			break;
		case 12:
			dataType = "Number";
			break;
		case 13:
			dataType = 'Server Channel IDs';
			break;
		case 14:
			dataType = 'Server Role IDs';
			break;
		case 15:
			dataType = 'Server Member IDs';
			break;
		case 16:
			dataType = 'Number';
			break;
		case 17:
			dataType = 'Number';
			break;
		case 18:
			dataType = 'Number';
			break;
		case 19:
			dataType = 'Number';
			break;
		case 20:
			dataType = 'Number';
			break;
	}
	return ([data.varName2, dataType]);
},

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["server", "varName", "info", "storage", "varName2"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

			Mod Info:

			Created by EGGSY, EliteArtz, Invarion & Lasse!

		Source Server:

		<select id="server" class="round" onchange="glob.serverChange(this, 'varNameContainer')">
			${data.servers[isEvent ? 1 : 0]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Source Info:

		<select id="info" class="round">
			<option value="0" selected>Servers Creation Date</option>
			<option value="1">Time User gets AFK</option>
			<option value="2">Is Server available?</option>
			<option value="4">Date Bot Joined</option>
			<option value="5">Total Channel Amount</option>
			<option value="19">Text Channel Amount</option>
			<option value="20">Voice Channel Amount</option>
			<option value="21">Text Channel List</option>
			<option value="22">Voice Channel List</option>
			<option value="6">Server Emoji Amount</option>
			<option value="7">Embeds links?</option>
			<option value="9">Online Members Count</option
			<option value="11">Idle Members Count</option>
			<option value="8">DND Members Count</option>
			<option value="10">Offline Members Count</option>
			<option value="13">Server Channel IDs</option>
			<option value="14">Server Role IDs</option>
			<option value="15">Server Member IDs</option>
      <option value="18">Server Member Count</option>
			<option value="16">Server Bot Count</option>
			<option value="17">Server Human Member Count</option>
			<option value="12">Total Bots in Servers</option>
			</select>

		Store In:

		<select id="storage" class="round">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName2" class="round" type="text">

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

	glob.serverChange(document.getElementById('server'), 'varNameContainer')
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const server = parseInt(data.server);
	const varName = this.evalMessage(data.varName, cache);
	const info = parseInt(data.info);
	const targetServer = this.getServer(server, varName, cache);
	if(!targetServer) {
		this.callNextAction(cache);
		return;
	}
	let result;
	switch(info) {
		case 0:
			result = targetServer.createdAt;
			break;
		case 1:
			result = targetServer.afkTimeout;
			break;
		case 2:
			result = targetServer.available;
			break;
		case 3:
			result = targetServer.large; //Deprecated in v1.8.5
			break;
		case 4:
			result = targetServer.joinedAt;
			break;
		case 5:
			result = targetServer.channels.array().length;
			break;
		case 6:
			result = targetServer.emojis.array().length;
			break;
		case 7:
			result = targetServer.embedEnabled;
			break;
		case 8:
			result = targetServer.members.filter(m => m.user.presence.status == "dnd").size;
			break;
		case 9:
			result = targetServer.members.filter(m => m.user.presence.status == "online").size;
			break;
		case 10:
			result = targetServer.members.filter(m => m.user.presence.status == "offline").size;
			break;
		case 11:
			result = targetServer.members.filter(m => m.user.presence.status == "idle").size;
			break;
		case 12:
			result = targetServer.members.filter(m => m.user.bot).size;
			break;
		case 13:
			result = targetServer.channels.map(channels => channels.id);
			break;
		case 14:
			result = targetServer.roles.map(roles => roles.id);
			break;
		case 15:
			result = targetServer.members.map(members => members.id);
			break;
		case 16:
			result = targetServer.members.filter(m => m.user.bot == true).size;
			break;
		case 17:
			result = targetServer.members.filter(m => m.user.bot == false).size;
			break;
		case 18:
			result = targetServer.memberCount; //Added by Lasse in 1.8.7
			break;
		case 19:
			result = targetServer.channels.findAll('type', 'text').length; //Added by Invarion in 1.8.8 | Taken from bashy's mod
			break;
		case 20:
			result = targetServer.channels.findAll('type', 'voice').length; //Added by Invarion in 1.8.8 | Taken from bashy's mod
			break;
		case 21:
			result = targetServer.channels.findAll('type', 'text');  //Added by Invarion in 1.8.8
	    break;
		case 22:
	    result = targetServer.channels.findAll('type', 'voice'); //Added by Invarion in 1.8.8
		default:
			break;
	}
	if(result !== undefined) {
		const storage = parseInt(data.storage);
		const varName2 = this.evalMessage(data.varName2, cache);
		this.storeValue(result, storage, varName2, cache);
	}
	this.callNextAction(cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<|endoftext|>


<commit_before>'use strict';
// Groups controller
angular.module('groups').controller('GroupsController', GroupsController);

function GroupsController($scope, $state, $stateParams, $location, Authentication, Groups, Search, lodash, dialogs, $q, growl, Users, UserService) {
    var _ = lodash;
    $scope.authentication = Authentication;
    $scope.user = $scope.authentication.user;
    if (!$scope.user) {
        $location.path('/');
    }

    $scope.$state = $state;
    // Create new Group
    $scope.create = create;
    // Remove existing Group
    $scope.remove = remove;
    // Update existing Group
    $scope.update = update;
    // Find a list of Groups
    $scope.find = find;
    // Find existing Group
    $scope.findOne = findOne;
    $scope.tempMembers = [];
    // Search for members
    $scope.getMembers = Search.getUsers;
    // Called when a member is selected
    $scope.onSelect = onSelect;
    // Remove member from group
    $scope.removeMember = removeMember;
    // Remove member from temporary group
    $scope.removeTempMember = removeTempMember;
    $scope.saveMember = saveMember;
    // show dialog for adding members
    $scope.addMember = addMember;
    $scope.isAdmin = isAdmin;
    $scope.isOwner = isOwner;
    $scope.isLoggedInOwner = isLoggedInOwner;
    $scope.makeAdmin = makeAdmin;
    $scope.removeAdmin = removeAdmin;
    $scope.canRemoveAdmin = canRemoveAdmin;
    $scope.isMember = isMember;
    $scope.joinGroup = joinGroup;
    $scope.absUrl = $location.absUrl();

    // user functions
    $scope.getUser = getUser;
    $scope.canRevokeAdminRights = canRevokeAdminRights;
    $scope.canMakeAdmin = canMakeAdmin;
    $scope.canRemoveMember = canRemoveMember;
    $scope.canRmMember = canRmMember;
    $scope.canRmvMember = canRmvMember;
    $scope._isAdmin = _isAdmin;

    $scope.shareGroup = shareGroup;

    // Group Functions
    function create() {
        // Create new Group object
        var group = new Groups($scope.group);
        // Redirect after save
        return group.$save(function(data) {
            $state.go('viewGroup.listMembers.viewMembers',{
                groupId:data._id
            });
            _notifySuccess('Group successfully created');
        });
    }

    function remove() {
        return $scope.group.$remove(function(data) {
            $location.path('groups');
        });
    }

    function update() {
        return $scope.group.$update();
    }

    function find() {
        $scope.groups = Groups.query();
    }
    // Member functions
    function _addIsAdminAttr() {
        _.each($scope.group.members, function(item) {
            if (_.include(_.pluck($scope.group.admins, '_id'), item._id)) {
                if (_.isUndefined(item.isAdmin)) {
                    item.isAdmin = true;
                } else {
                    item.isAdmin = false;
                }
            }
        });
    }

    function findOne() {
        return $scope.group = Groups.get({
            groupId: $stateParams.groupId
        });
    }

    function onSelect($model) {
        if (!_isUserInTempMembers($model) && !_isUserInMembers($model)) {
            $scope.tempMembers.push($model);
        } else {
            var header = 'Add Members',
                msg = ''+$model.username + ' already in the group.',
                opts = {
                    size: 'sm',
                    windowClass: 'modal-btn-sm'
                };
            dialogs.notify(header, msg, opts);
        }
    }

    function removeMember(member) {
        // var member = _getMember(index);
        if (member.isAdmin) {
            if (!canRemoveAdmin()) {
                _notifyCannotRemoveOwner();
                return _getPromise(false, member);
            }

            _deleteAdminMember(member);
        }

        _deleteMember(member);
        return update().then(success, failure);

        function success() {
            _notifySuccess('Member ' + member.username + ' removed');
        }

        function failure() {
            _addMember(member);
            _addAdmin(member);
        }
    }

    function _getMember(index) {
        return $scope.group.members[index];
    }

    function _removeMember(member) {
        _deleteMember(member);
        return update().then(success, failure);

        function success() {
        }

        function failure() {
            _addMember(member);
        }
    }

    function removeTempMember(index) {
        $scope.tempMembers.splice(index, 1);
    }

    function saveMember() {
        $scope.group.members = _.union($scope.group.members, $scope.tempMembers);
        return update().then(function() {
            $state.go('viewGroup.listMembers.viewMembers');
            $scope.tempMembers = [];
            _notifySuccess('Member successfully added');

        });
    }

    function addMember() {
        var opts = {
            size: 'sm'
        };
        dialogs.create('/modules/members/views/templ-add-member.client.view.html', 'MembersController', $scope.group, opts);
    }

    // Admin functions
    function isAdmin() {
        if (_.isUndefined($scope.group.admins)) {
            return false;
        }

        return _isUserInAdmins($scope.authentication.user);
    }

    function isOwner(member){
        if(_.isUndefined($scope.group.createdBy)){
            return false;
        }

        if(member._id !== $scope.group.createdBy._id){
            return false;
        }

        return _isUserInAdmins($scope.group.createdBy);
    }

    function isLoggedInOwner(){
        if(_.isUndefined($scope.group.createdBy)){
            return false;
        }

        return $scope.group.createdBy._id === $scope.authentication.user._id;
    }

    function makeAdmin(member) {
        // add member to admins array
        if (!_addAdmin(member)) {
            return _getPromise(false, member);
        }
        return update().then(success, failure);
        // on succes, add isAdmin & add to member array
        function success() {
            _notifySuccess('Admin successfully added');
        }
        // on failure, remove from admins array
        function failure() {
            _.dropRight($scope.group.admins);
        }
    }

    function removeAdmin(member) {
        if (isOwner(member)) {
            _notifyCannotRemoveOwner();
            return _getPromise(false, member);
        }
        // remove member from admin array
        _deleteAdminMember(member);
        // update
        return update().then(success, failure);
        // on succes, add isAdmin & add to member array
        function success() {
             _notifySuccess('Admin successfully removed');
        }
        // on failure, add member back to admins
        function failure() {
            _addAdmin(member);
        }
    }

    function canRemoveAdmin() {
        return _.size($scope.group.admins) > 1;
    }

    function _notifyCannotRemoveOwner() {
        var header = 'Remove Owner',
            msg = 'Can not remove the owner',
            opts = {
                size: 'sm',
                windowClass: 'modal-btn-sm'
            };
        dialogs.notify(header, msg, opts);
    }

    function _addAdmin(member) {
        if (_isUserInAdmins(member)) {
            return false;
        }
        $scope.group.admins.push(member);
        return true;
    }

    function _addMember(member) {
        if (_isUserInMembers(member)) {
            return false;
        }
        $scope.group.members.push(member);
        return true;
    }

    function _addTempMember(member) {
        if (_isUserInTempMembers(member)) {
            return false;
        }
        $scope.tempMembers.push(member);
        return true;
    }

    function _deleteAdminMember(member) {
        $scope.group.admins = _.reject($scope.group.admins, function(item) {
            return _.isEqual(item._id, member._id);
        });
    }

    function _deleteMember(member) {
        $scope.group.members = _.reject($scope.group.members, function(item) {
            return _.isEqual(item._id, member._id);
        });
    }

    function _isUserInTempMembers(user) {
        return _.include(_.pluck($scope.tempMembers, '_id'), user._id);
    }

    function _isUserInAdmins(user) {
        return _.includes(_.pluck($scope.group.admins, '_id'), user._id);
    }

    function _isUserInMembers(user) {
        return _.include(_.pluck($scope.group.members, '_id'), user._id);
    }

    function _getPromise(isSuccess, data) {
        var deferred = $q.defer();
        setTimeout(function() {
            if (isSuccess) {
                deferred.resolve(data);
            } else {
                deferred.reject(data);
            }
        }, 1);
        return deferred.promise;
    }

    function _notifySuccess(text){
        text = text || 'Group successfully updated';
        growl.success(text);
    }

    function getUser(){
        var user = Users.get({
            userId: $scope.user._id
        }, function() {
            Authentication.user = user;
            $scope.user = user;
        });
        return user.$promise;
    }

    // Checks whether the logged in user is a member of the group
    function isMember(){
        return _isUserInMembers($scope.user);
    }

    function joinGroup(){
        _addMember($scope.user);
        update().then(function() {
            $state.go('viewGroup.listMembers.viewMembers');
            _notifySuccess('You are now a member');
        }, function(){
            _removeMember($scope.user);
        });
    }

    function _joinGroup(user, group){
        return UserService.joinGroupAndUser(user, group);
    }

    function canRevokeAdminRights(member){
        return ( member.isAdmin && !isOwner(member) && isLoggedInAdmin(member)) || ( member.isAdmin && isLoggedInOwner() && !isOwner(member) );
    }

    function canMakeAdmin(member){
        return member.isAdmin;
    }

    function canRemoveMember(member){
        return !member.isAdmin ||
            ( !isOwner(member) && isLoggedInAdmin(member)) ||
            ( isLoggedInOwner() && !isOwner(member) );
    }

    function canRmMember(member){
        return ( !isOwner(member) && isLoggedInOwner() );
    }

    function canRmvMember(member){
        return ( !isOwner(member) && !member.isAdmin && !isLoggedInOwner() );
    }

    function _isAdmin(member){
        return ( member.isAdmin && !isOwner(member) );
    }

    function isLoggedInAdmin(member){
        if(_.isUndefined($scope.authentication.user._id)){
            return false;
        }

        return member._id === $scope.authentication.user._id;
    }

    function shareGroup(){
        var header = 'Share Group',
            msg = ['
Share either:
',
                '
',
                '
Group name (best)
'+$scope.group.name+'
',
                '
Group url
here
',
                '
'].join(' '),
            opts = {
                size: 'sm',
                windowClass: 'modal-btn-sm'
            };
        dialogs.notify(header, msg, opts);
    }
}
<commit_msg>Modified share group message<commit_after>'use strict';
// Groups controller
angular.module('groups').controller('GroupsController', GroupsController);

function GroupsController($scope, $state, $stateParams, $location, Authentication, Groups, Search, lodash, dialogs, $q, growl, Users, UserService) {
    var _ = lodash;
    $scope.authentication = Authentication;
    $scope.user = $scope.authentication.user;
    if (!$scope.user) {
        $location.path('/');
    }

    $scope.$state = $state;
    // Create new Group
    $scope.create = create;
    // Remove existing Group
    $scope.remove = remove;
    // Update existing Group
    $scope.update = update;
    // Find a list of Groups
    $scope.find = find;
    // Find existing Group
    $scope.findOne = findOne;
    $scope.tempMembers = [];
    // Search for members
    $scope.getMembers = Search.getUsers;
    // Called when a member is selected
    $scope.onSelect = onSelect;
    // Remove member from group
    $scope.removeMember = removeMember;
    // Remove member from temporary group
    $scope.removeTempMember = removeTempMember;
    $scope.saveMember = saveMember;
    // show dialog for adding members
    $scope.addMember = addMember;
    $scope.isAdmin = isAdmin;
    $scope.isOwner = isOwner;
    $scope.isLoggedInOwner = isLoggedInOwner;
    $scope.makeAdmin = makeAdmin;
    $scope.removeAdmin = removeAdmin;
    $scope.canRemoveAdmin = canRemoveAdmin;
    $scope.isMember = isMember;
    $scope.joinGroup = joinGroup;
    $scope.absUrl = $location.absUrl();

    // user functions
    $scope.getUser = getUser;
    $scope.canRevokeAdminRights = canRevokeAdminRights;
    $scope.canMakeAdmin = canMakeAdmin;
    $scope.canRemoveMember = canRemoveMember;
    $scope.canRmMember = canRmMember;
    $scope.canRmvMember = canRmvMember;
    $scope._isAdmin = _isAdmin;

    $scope.shareGroup = shareGroup;

    // Group Functions
    function create() {
        // Create new Group object
        var group = new Groups($scope.group);
        // Redirect after save
        return group.$save(function(data) {
            $state.go('viewGroup.listMembers.viewMembers',{
                groupId:data._id
            });
            _notifySuccess('Group successfully created');
        });
    }

    function remove() {
        return $scope.group.$remove(function(data) {
            $location.path('groups');
        });
    }

    function update() {
        return $scope.group.$update();
    }

    function find() {
        $scope.groups = Groups.query();
    }
    // Member functions
    function _addIsAdminAttr() {
        _.each($scope.group.members, function(item) {
            if (_.include(_.pluck($scope.group.admins, '_id'), item._id)) {
                if (_.isUndefined(item.isAdmin)) {
                    item.isAdmin = true;
                } else {
                    item.isAdmin = false;
                }
            }
        });
    }

    function findOne() {
        return $scope.group = Groups.get({
            groupId: $stateParams.groupId
        });
    }

    function onSelect($model) {
        if (!_isUserInTempMembers($model) && !_isUserInMembers($model)) {
            $scope.tempMembers.push($model);
        } else {
            var header = 'Add Members',
                msg = ''+$model.username + ' already in the group.',
                opts = {
                    size: 'sm',
                    windowClass: 'modal-btn-sm'
                };
            dialogs.notify(header, msg, opts);
        }
    }

    function removeMember(member) {
        // var member = _getMember(index);
        if (member.isAdmin) {
            if (!canRemoveAdmin()) {
                _notifyCannotRemoveOwner();
                return _getPromise(false, member);
            }

            _deleteAdminMember(member);
        }

        _deleteMember(member);
        return update().then(success, failure);

        function success() {
            _notifySuccess('Member ' + member.username + ' removed');
        }

        function failure() {
            _addMember(member);
            _addAdmin(member);
        }
    }

    function _getMember(index) {
        return $scope.group.members[index];
    }

    function _removeMember(member) {
        _deleteMember(member);
        return update().then(success, failure);

        function success() {
        }

        function failure() {
            _addMember(member);
        }
    }

    function removeTempMember(index) {
        $scope.tempMembers.splice(index, 1);
    }

    function saveMember() {
        $scope.group.members = _.union($scope.group.members, $scope.tempMembers);
        return update().then(function() {
            $state.go('viewGroup.listMembers.viewMembers');
            $scope.tempMembers = [];
            _notifySuccess('Member successfully added');

        });
    }

    function addMember() {
        var opts = {
            size: 'sm'
        };
        dialogs.create('/modules/members/views/templ-add-member.client.view.html', 'MembersController', $scope.group, opts);
    }

    // Admin functions
    function isAdmin() {
        if (_.isUndefined($scope.group.admins)) {
            return false;
        }

        return _isUserInAdmins($scope.authentication.user);
    }

    function isOwner(member){
        if(_.isUndefined($scope.group.createdBy)){
            return false;
        }

        if(member._id !== $scope.group.createdBy._id){
            return false;
        }

        return _isUserInAdmins($scope.group.createdBy);
    }

    function isLoggedInOwner(){
        if(_.isUndefined($scope.group.createdBy)){
            return false;
        }

        return $scope.group.createdBy._id === $scope.authentication.user._id;
    }

    function makeAdmin(member) {
        // add member to admins array
        if (!_addAdmin(member)) {
            return _getPromise(false, member);
        }
        return update().then(success, failure);
        // on succes, add isAdmin & add to member array
        function success() {
            _notifySuccess('Admin successfully added');
        }
        // on failure, remove from admins array
        function failure() {
            _.dropRight($scope.group.admins);
        }
    }

    function removeAdmin(member) {
        if (isOwner(member)) {
            _notifyCannotRemoveOwner();
            return _getPromise(false, member);
        }
        // remove member from admin array
        _deleteAdminMember(member);
        // update
        return update().then(success, failure);
        // on succes, add isAdmin & add to member array
        function success() {
             _notifySuccess('Admin successfully removed');
        }
        // on failure, add member back to admins
        function failure() {
            _addAdmin(member);
        }
    }

    function canRemoveAdmin() {
        return _.size($scope.group.admins) > 1;
    }

    function _notifyCannotRemoveOwner() {
        var header = 'Remove Owner',
            msg = 'Can not remove the owner',
            opts = {
                size: 'sm',
                windowClass: 'modal-btn-sm'
            };
        dialogs.notify(header, msg, opts);
    }

    function _addAdmin(member) {
        if (_isUserInAdmins(member)) {
            return false;
        }
        $scope.group.admins.push(member);
        return true;
    }

    function _addMember(member) {
        if (_isUserInMembers(member)) {
            return false;
        }
        $scope.group.members.push(member);
        return true;
    }

    function _addTempMember(member) {
        if (_isUserInTempMembers(member)) {
            return false;
        }
        $scope.tempMembers.push(member);
        return true;
    }

    function _deleteAdminMember(member) {
        $scope.group.admins = _.reject($scope.group.admins, function(item) {
            return _.isEqual(item._id, member._id);
        });
    }

    function _deleteMember(member) {
        $scope.group.members = _.reject($scope.group.members, function(item) {
            return _.isEqual(item._id, member._id);
        });
    }

    function _isUserInTempMembers(user) {
        return _.include(_.pluck($scope.tempMembers, '_id'), user._id);
    }

    function _isUserInAdmins(user) {
        return _.includes(_.pluck($scope.group.admins, '_id'), user._id);
    }

    function _isUserInMembers(user) {
        return _.include(_.pluck($scope.group.members, '_id'), user._id);
    }

    function _getPromise(isSuccess, data) {
        var deferred = $q.defer();
        setTimeout(function() {
            if (isSuccess) {
                deferred.resolve(data);
            } else {
                deferred.reject(data);
            }
        }, 1);
        return deferred.promise;
    }

    function _notifySuccess(text){
        text = text || 'Group successfully updated';
        growl.success(text);
    }

    function getUser(){
        var user = Users.get({
            userId: $scope.user._id
        }, function() {
            Authentication.user = user;
            $scope.user = user;
        });
        return user.$promise;
    }

    // Checks whether the logged in user is a member of the group
    function isMember(){
        return _isUserInMembers($scope.user);
    }

    function joinGroup(){
        _addMember($scope.user);
        update().then(function() {
            $state.go('viewGroup.listMembers.viewMembers');
            _notifySuccess('You are now a member');
        }, function(){
            _removeMember($scope.user);
        });
    }

    function _joinGroup(user, group){
        return UserService.joinGroupAndUser(user, group);
    }

    function canRevokeAdminRights(member){
        return ( member.isAdmin && !isOwner(member) && isLoggedInAdmin(member)) || ( member.isAdmin && isLoggedInOwner() && !isOwner(member) );
    }

    function canMakeAdmin(member){
        return member.isAdmin;
    }

    function canRemoveMember(member){
        return !member.isAdmin ||
            ( !isOwner(member) && isLoggedInAdmin(member)) ||
            ( isLoggedInOwner() && !isOwner(member) );
    }

    function canRmMember(member){
        return ( !isOwner(member) && isLoggedInOwner() );
    }

    function canRmvMember(member){
        return ( !isOwner(member) && !member.isAdmin && !isLoggedInOwner() );
    }

    function _isAdmin(member){
        return ( member.isAdmin && !isOwner(member) );
    }

    function isLoggedInAdmin(member){
        if(_.isUndefined($scope.authentication.user._id)){
            return false;
        }

        return member._id === $scope.authentication.user._id;
    }

    function shareGroup(){
        var header = 'Share Group',
            msg = ['
Want people to join this group?
',
                '
',
                '
Share group name
'+$scope.group.name+'
',
                '
Search for the group
',
                '
Click Join Group
',
                '
'].join(' '),
            opts = {
                size: 'sm',
                windowClass: 'modal-btn-sm'
            };
        dialogs.notify(header, msg, opts);
    }
}
<|endoftext|>


<commit_before>/*
 * JScript Render - StringLength class
 * http://www.pleets.org
 *
 * Copyright 2014, Pleets Apps
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

/* JScriptRender alias */
if (!window.hasOwnProperty('JScriptRender'))
   JScriptRender = {};

/* Namespace */
if (!JScriptRender.hasOwnProperty('validator'))
   JScriptRender.validator = new Object();

/* StringLength class */
JScriptRender.validator.StringLength = function(settings)
{
   var set = settings || {};

   if (typeof set.min !== "number")
      throw ("The minimun value must be number type");
   if (typeof set.max !== "number" && typeof set.max !== "undefined")
      throw ("The maximum value must be number type");

   // Get a natural number as parameter or set the default parameters to zero
   set.min = (set.min >= 0) ? set.min : 0;

   // Get a natural number as parameter grater tha the minumun value or set the maximum value to undefined
   set.max = (set.max > set.min) ? set.max : undefined;

   JScriptRender.validator.StringLength.prototype.min = set.min;
   JScriptRender.validator.StringLength.prototype.max = set.max;
}

JScriptRender.validator.StringLength.prototype =
{
   Messages: {},

   isValid: function(string, whitespaces)
   {
      // Allow whitespaces
      if (typeof whitespaces !== "boolean" && typeof whitespaces !== "undefined")
         throw "whitespaces must be boolean type!";
      whitespaces = (typeof whitespaces === "undefined") ? false: whitespaces;

      if (typeof string !== "string")
         throw "The first argument must be string type!";
      if (typeof whitespaces !== "boolean")
         throw "The second argument must be boolean type!";

      this.Messages = {};

      if (whitespaces)
      {
         if (typeof this.max !== "undefined")
         {
            if (string.length < this.min)
            {
               this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
               return false;
            }
            else if (string.length > this.max)
            {
               this.Messages.stringLengthTooLong = "The input is more than " + this.max + " characters long";
               return false;
            }
         }
         else {
            if (string.length < this.min)
            {
               this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
               return false;
            }
         }
      }
      else {
         if (typeof this.max !== "undefined")
         {
            if (string.trim().length < this.min)
            {
              this.Messages.stringLengthTooShort= "The input is less than " + this.min + " characters long";
              return false;
            }
            else if (string.trim().length > this.max)
            {
              this.Messages.stringLengthTooLong = "The input is more than " + this.max + " characters long";
              return false;
            }
         }
         else {
            if (string.trim().length < this.min)
            {
              this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
              return false;
            }
         }
      }
      return true;
   },
   getMessages: function()
   {
      return this.Messages;
   }
}
<commit_msg>Fix wrong static variable for objects<commit_after>/*
 * JScript Render - StringLength class
 * http://www.pleets.org
 *
 * Copyright 2014, Pleets Apps
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

/* JScriptRender alias */
if (!window.hasOwnProperty('JScriptRender'))
   JScriptRender = {};

/* Namespace */
if (!JScriptRender.hasOwnProperty('validator'))
   JScriptRender.validator = new Object();

/* StringLength class */
JScriptRender.validator.StringLength = function(settings)
{
   var set = settings || {};

   if (typeof set.min !== "number")
      throw ("The minimun value must be number type");
   if (typeof set.max !== "number" && typeof set.max !== "undefined")
      throw ("The maximum value must be number type");

   // Get a natural number as parameter or set the default parameters to zero
   set.min = (set.min >= 0) ? set.min : 0;

   // Get a natural number as parameter grater tha the minumun value or set the maximum value to undefined
   set.max = (set.max > set.min) ? set.max : undefined;

   this.min = set.min;
   this.max = set.max;
}

JScriptRender.validator.StringLength.prototype =
{
   Messages: {},

   isValid: function(string, whitespaces)
   {
      // Allow whitespaces
      if (typeof whitespaces !== "boolean" && typeof whitespaces !== "undefined")
         throw "whitespaces must be boolean type!";
      whitespaces = (typeof whitespaces === "undefined") ? false: whitespaces;

      if (typeof string !== "string")
         throw "The first argument must be string type!";
      if (typeof whitespaces !== "boolean")
         throw "The second argument must be boolean type!";

      this.Messages = {};

      if (whitespaces)
      {
         if (typeof this.max !== "undefined")
         {
            if (string.length < this.min)
            {
               this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
               return false;
            }
            else if (string.length > this.max)
            {
               this.Messages.stringLengthTooLong = "The input is more than " + this.max + " characters long";
               return false;
            }
         }
         else {
            if (string.length < this.min)
            {
               this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
               return false;
            }
         }
      }
      else {
         if (typeof this.max !== "undefined")
         {
            if (string.trim().length < this.min)
            {
              this.Messages.stringLengthTooShort= "The input is less than " + this.min + " characters long";
              return false;
            }
            else if (string.trim().length > this.max)
            {
              this.Messages.stringLengthTooLong = "The input is more than " + this.max + " characters long";
              return false;
            }
         }
         else {
            if (string.trim().length < this.min)
            {
              this.Messages.stringLengthTooShort = "The input is less than " + this.min + " characters long";
              return false;
            }
         }
      }
      return true;
   },
   getMessages: function()
   {
      return this.Messages;
   }
}
<|endoftext|>