<commit_before>'use strict';

const _ = require('lodash');
const delegate = require('delegates');

const {
  sanitizeEntity,
  webhook: webhookUtils,
  contentTypes: contentTypesUtils,
} = require('strapi-utils');
const uploadFiles = require('./utils/upload-files');

// TODO: those should be strapi events used by the webhooks not the other way arround
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;

module.exports = ctx => {
  const implementation = createDefaultImplementation(ctx);

  const service = {
    implementation,
    decorate(decorator) {
      if (typeof decorator !== 'function') {
        throw new Error(`Decorator must be a function, received ${typeof decorator}`);
      }

      this.implementation = Object.assign({}, this.implementation, decorator(this.implementation));
      return this;
    },
  };

  const delegator = delegate(service, 'implementation');

  // delegate every method in implementation
  Object.keys(service.implementation).forEach(key => delegator.method(key));

  return service;
};

const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({
  /**
   * expose some utils so the end users can use them
   */
  uploadFiles,

  /**
   * Returns default opt
   * it is async so decorators can do async processing
   * @param {object} params - query params to extend
   * @returns {object} wra
   */
  async wrapOptions(options = {} /*, ctx */) {
    return options;
  },

  /**
   * Returns a list of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async find(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    const { kind } = db.getModel(model);

    // return first element and ignore filters
    if (kind === 'singleType') {
      const results = await db.query(model).find({ ...params, _limit: 1 }, populate);
      return _.first(results) || null;
    }

    return db.query(model).find(params, populate);
  },

  /**
   * Returns a paginated list of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findPage(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findPage(params, populate);
  },

  /**
   * Returns a list of entries with relation counters
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findWithRelationCounts(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findWithRelationCounts(params, populate);
  },

  /**
   * Returns one entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findOne(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findOne(params, populate);
  },

  /**
   * Returns a count of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async count(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    return db.query(model).count(params);
  },

  /**
   * Creates & returns a new entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async create(opts, { model }) {
    const { data, files } = await this.wrapOptions(opts, { model });

    const modelDef = db.getModel(model);

    if (modelDef.kind === 'singleType') {
      // check if there is already one entry and throw
      const count = await db.query(model).count();
      if (count >= 1) {
        throw new Error('Single type entry can only be created once');
      }
    }

    const isDraft = contentTypesUtils.isDraft(data, modelDef);

    const validData = await entityValidator.validateEntityCreation(modelDef, data, { isDraft });

    let entry = await db.query(model).create(validData);

    if (files && Object.keys(files).length > 0) {
      await this.uploadFiles(entry, files, { model });
      entry = await this.findOne({ params: { id: entry.id } }, { model });
    }

    eventHub.emit(ENTRY_CREATE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Updates & returns an existing entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async update(opts, { model }) {
    const { params, data, files } = await this.wrapOptions(opts, { model });

    const modelDef = db.getModel(model);
    const existingEntry = await db.query(model).findOne(params);

    const isDraft = contentTypesUtils.isDraft(existingEntry, modelDef);

    const validData = await entityValidator.validateEntityUpdate(modelDef, data, {
      isDraft,
    });

    let entry = await db.query(model).update(params, validData);

    if (files && Object.keys(files).length > 0) {
      await this.uploadFiles(entry, files, { model });
      entry = await this.findOne({ params: { id: entry.id } }, { model });
    }

    eventHub.emit(ENTRY_UPDATE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Deletes & returns the entry that was deleted
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async delete(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    const entry = await db.query(model).delete(params);

    const modelDef = db.getModel(model);
    eventHub.emit(ENTRY_DELETE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Returns a list of matching entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async search(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).search(params, populate);
  },

  /**
   * Returns a list of matching entries with relations counters
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async searchWithRelationCounts(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).searchWithRelationCounts(params, populate);
  },

  /**
   * Returns a paginated list of matching entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async searchPage(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).searchPage(params, populate);
  },

  /**
   * Promise to count searched records
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async countSearch(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    return db.query(model).countSearch(params);
  },
});
<commit_msg>Fix some typos<commit_after>'use strict';

const _ = require('lodash');
const delegate = require('delegates');

const {
  sanitizeEntity,
  webhook: webhookUtils,
  contentTypes: contentTypesUtils,
} = require('strapi-utils');
const uploadFiles = require('./utils/upload-files');

// TODO: those should be strapi events used by the webhooks not the other way arround
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;

module.exports = ctx => {
  const implementation = createDefaultImplementation(ctx);

  const service = {
    implementation,
    decorate(decorator) {
      if (typeof decorator !== 'function') {
        throw new Error(`Decorator must be a function, received ${typeof decorator}`);
      }

      this.implementation = Object.assign({}, this.implementation, decorator(this.implementation));
      return this;
    },
  };

  const delegator = delegate(service, 'implementation');

  // delegate every method in implementation
  Object.keys(service.implementation).forEach(key => delegator.method(key));

  return service;
};

const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({
  /**
   * expose some utils so the end users can use them
   */
  uploadFiles,

  /**
   * Returns default opt
   * it is async so decorators can do async processing
   * @param {object} params - query params to extend
   * @param {object=} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async wrapOptions(options = {}) {
    return options;
  },

  /**
   * Returns a list of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async find(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    const { kind } = db.getModel(model);

    // return first element and ignore filters
    if (kind === 'singleType') {
      const results = await db.query(model).find({ ...params, _limit: 1 }, populate);
      return _.first(results) || null;
    }

    return db.query(model).find(params, populate);
  },

  /**
   * Returns a paginated list of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findPage(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findPage(params, populate);
  },

  /**
   * Returns a list of entries with relation counters
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findWithRelationCounts(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findWithRelationCounts(params, populate);
  },

  /**
   * Returns one entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async findOne(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).findOne(params, populate);
  },

  /**
   * Returns a count of entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async count(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    return db.query(model).count(params);
  },

  /**
   * Creates & returns a new entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async create(opts, { model }) {
    const { data, files } = await this.wrapOptions(opts, { model });

    const modelDef = db.getModel(model);

    if (modelDef.kind === 'singleType') {
      // check if there is already one entry and throw
      const count = await db.query(model).count();
      if (count >= 1) {
        throw new Error('Single type entry can only be created once');
      }
    }

    const isDraft = contentTypesUtils.isDraft(data, modelDef);

    const validData = await entityValidator.validateEntityCreation(modelDef, data, { isDraft });

    let entry = await db.query(model).create(validData);

    if (files && Object.keys(files).length > 0) {
      await this.uploadFiles(entry, files, { model });
      entry = await this.findOne({ params: { id: entry.id } }, { model });
    }

    eventHub.emit(ENTRY_CREATE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Updates & returns an existing entry
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async update(opts, { model }) {
    const { params, data, files } = await this.wrapOptions(opts, { model });

    const modelDef = db.getModel(model);
    const existingEntry = await db.query(model).findOne(params);

    const isDraft = contentTypesUtils.isDraft(existingEntry, modelDef);

    const validData = await entityValidator.validateEntityUpdate(modelDef, data, {
      isDraft,
    });

    let entry = await db.query(model).update(params, validData);

    if (files && Object.keys(files).length > 0) {
      await this.uploadFiles(entry, files, { model });
      entry = await this.findOne({ params: { id: entry.id } }, { model });
    }

    eventHub.emit(ENTRY_UPDATE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Deletes & returns the entry that was deleted
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async delete(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    const entry = await db.query(model).delete(params);

    const modelDef = db.getModel(model);
    eventHub.emit(ENTRY_DELETE, {
      model: modelDef.modelName,
      entry: sanitizeEntity(entry, { model: modelDef }),
    });

    return entry;
  },

  /**
   * Returns a list of matching entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async search(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).search(params, populate);
  },

  /**
   * Returns a list of matching entries with relations counters
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async searchWithRelationCounts(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).searchWithRelationCounts(params, populate);
  },

  /**
   * Returns a paginated list of matching entries
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async searchPage(opts, { model }) {
    const { params, populate } = await this.wrapOptions(opts, { model });

    return db.query(model).searchPage(params, populate);
  },

  /**
   * Promise to count searched records
   * @param {object} opts - Query options object (params, data, files, populate)
   * @param {object} ctx - Query context
   * @param {object} ctx.model - Model that is being used
   */
  async countSearch(opts, { model }) {
    const { params } = await this.wrapOptions(opts, { model });

    return db.query(model).countSearch(params);
  },
});
<|endoftext|>


<commit_before>const expect = require('chai').expect
const totalsRounder = require('../../../../app/services/helpers/totalsRounder')

describe('services/helpers/totals-rounder', function () {
  it('should round total contrcated hours to 1 decimal place', function () {
    var totals = { name: 'Total / Average', totalCapacityPercentage: 0, totalPoints: 0, totalAvailablePoints: 0, totalContractedHours: 118416.70000000001	, totalReduction: 77371.40000000001 }
    totals = totalsRounder(totals)
    expect(totals.totalContractedHours.to.eql(118416.7))
  })

  it('should round total reduction hours to 1 decimal place', function () {
    var totals = { name: 'Total / Average', totalCapacityPercentage: 0, totalPoints: 0, totalAvailablePoints: 0, totalContractedHours: 118416.70000000001	, totalReduction: 77371.40000000001 }
    totals = totalsRounder(totals)
    expect(totals.totalReduction.to.eql(77371.4))
  })
})
<commit_msg>Fixed broken reference to totals rounder<commit_after>const expect = require('chai').expect
const totalsRounder = require('../../../../app/services/helpers/totals-rounder')

describe('services/helpers/totals-rounder', function () {
  it('should round total contrcated hours to 1 decimal place', function () {
    var totals = { name: 'Total / Average', totalCapacityPercentage: 0, totalPoints: 0, totalAvailablePoints: 0, totalContractedHours: 118416.70000000001	, totalReduction: 77371.40000000001 }
    totals = totalsRounder(totals)
    expect(totals.totalContractedHours.to.eql(118416.7))
  })

  it('should round total reduction hours to 1 decimal place', function () {
    var totals = { name: 'Total / Average', totalCapacityPercentage: 0, totalPoints: 0, totalAvailablePoints: 0, totalContractedHours: 118416.70000000001	, totalReduction: 77371.40000000001 }
    totals = totalsRounder(totals)
    expect(totals.totalReduction.to.eql(77371.4))
  })
})
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Massachusetts Institute of Technology, Adobe Systems
 * Incorporated, and other contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */

/**
 * Your connection to instrumented node code.
 *
 * Provides these events:
 *
 *   - receivedScriptInfo (path):
 *       when information about functions and call sites has been received
 *   - scriptWentAway (path):
 *       when the connection to the instance for a particular path has closed
 */

define(function (require, exports, module) {
    var Agent   = require("Agent");
    var Dialogs = brackets.getModule("widgets/Dialogs");
    var semver  = require("./lib/semver");
    var Strings = require("strings");

    var dialogHTML     = require("text!InvalidVersion.html");
    var dialogTemplate = Mustache.render(dialogHTML, {Strings : Strings});

    var _conn;
    var _connected = false;
    var _nodes = {}; // id (string) -> {id: string, path: string, start: {line, column}, end: {line, column}, name: string (optional)}
    var _nodesByFilePath = {};
    var _hitsHandle, _exceptionsHandle;
    var _nodeHitCounts = {};
    var _nodeExceptionCounts = {};
    var $exports = $(exports);

    var REQUIRED_FONDUE_VERSION = JSON.parse(require("text!package.json")).dependencies.fondue;

    function Connection() {
        this.socket = new WebSocket("ws://localhost:8888/");
        this.socket.onerror = this._onerror.bind(this);
        this.socket.onopen = this._onopen.bind(this);
        this.socket.onmessage = this._onmessage.bind(this);
        this.socket.onclose = this._onclose.bind(this);
        this.connected = new $.Deferred();
        this.disconnected = new $.Deferred();
        this._nextRequestIndex = 0;
        this._requests = {};
    }
    Connection.prototype = {
        _onerror: function () {
            this.connected.reject();
            this.disconnected.resolve();
            this._rejectAllPending();
        },
        _onopen: function () {
            this.connected.resolve();
        },
        _onmessage: function (msg) {
            var resp;
            try {
                resp = JSON.parse(msg.data);
            } catch (e) {
                return;
            }

            if (resp.id in this._requests) {
                if ("data" in resp) {
                    this._requests[resp.id].resolve(resp.data);
                } else {
                    this._requests[resp.id].reject();
                }
                delete this._requests[resp.id];
            }
        },
        _onclose: function () {
            this.connected.reject();
            this.disconnected.resolve();
            this._rejectAllPending();
        },
        _rejectAllPending: function () {
            for (var i in this._requests) {
                this._requests[i].reject();
            }
            this._requests = {};
        },
        request: function (name, args) {
            var deferred = new $.Deferred();
            var idx = this._nextRequestIndex++;
            this.socket.send(JSON.stringify({
                name: name,
                arguments: args || [],
                id: idx
            }));
            this._requests[idx] = deferred;
            return deferred.promise();
        },
    };

    function _addNodes(nodes) {
        var indexByPath = function (obj, path, hash) {
            if (path in hash) {
                hash[path].push(obj);
            } else {
                hash[path] = [obj];
            }
        }

        for (var i in nodes) {
            var n = nodes[i];
            n.path = n.path.replace(/\\/g, "/"); // XXX: "Windows support"
            _nodes[n.id] = n;
            indexByPath(n, n.path, _nodesByFilePath);
        }

        // de-dup paths, then send receivedScriptInfo event for each one
        var pathsO = {};
        for (var i in nodes) { pathsO[nodes[i].path] = true; }
        for (var path in pathsO) {
            _triggerReceivedScriptInfo(path);
        }
    }

    function _triggerReceivedScriptInfo(path) {
        $exports.triggerHandler("receivedScriptInfo", [path]);
    }

    function _invoke(name, args, callback) {
        _conn.connected.done(function () {
            _conn.request(name, args).done(function (value) {
                callback && callback(value);
            }).fail(function () {
                callback && callback();
            });
        }).fail(function () {
            callback && callback();
        });
    }

    function _invokePromise(name, args) {
        return _conn.connected.pipe(function () {
            return _conn.request(name, args);
        });
    }

    function _reset() {
        _nodes = {};
        _nodesByFilePath = {};
        _hitsHandle = undefined;
        _exceptionsHandle = undefined;
        _nodeHitCounts = {};
        _nodeExceptionCounts = {};
    }

    function _connect() {
        _conn = new Connection();

        _conn.connected.done(_onConnect);

        _conn.disconnected.done(function () {
            if (_connected) $exports.triggerHandler("disconnect");

            _connected = false;

            var paths = [];
            for (var path in _nodesByFilePath) {
                paths.push(path);
            }

            _reset();

            paths.forEach(function (path) {
                $exports.triggerHandler("scriptWentAway", [path]);
            });

            setTimeout(_connect, 1000);
        });
    }

    function _onConnect() {
        _connected = true;

        // get the handle to use for tracking hits
        _invoke("version", [], function (version) {
            if (!semver.satisfies(version, REQUIRED_FONDUE_VERSION)) {
                var dialog = Dialogs.showModalDialogUsingTemplate(dialogTemplate);
                var $dialog = dialog.getElement();
                $dialog.find(".close").on("click", dialog.close.bind(dialog));
            }
        });

        $exports.triggerHandler("connect");

        // get the handle to use for tracking hits
        _invoke("trackHits", [], function (handle) {
            _hitsHandle = handle;
        });

        _invoke("trackExceptions", [], function (handle) {
            _exceptionsHandle = handle;
        });

        // poll for new nodes
        _invoke("trackNodes", [], function (handle) {
            var id = setInterval(function () {
                _invoke("newNodes", [handle], function (nodes) {
                    if (nodes) {
                        _addNodes(nodes);
                    }
                });
            }, 1000);

            _conn.disconnected.done(function () {
                clearInterval(id);
            });
        });
    }

    function init() {
        _connect();
    }

    function isReady() {
        return _connected;
    }

    function functionWithId(fid) {
        return _nodes[fid];
    }

    function functionsInFile(path) {
        for (var remotePath in _nodesByFilePath) {
            if (Agent.couldBeRemotePath(path, remotePath)) {
                var nodes = _nodesByFilePath[remotePath];
                if (nodes) {
                    return nodes.filter(function (n) { return n.type === "function" });
                }
            }
        }
        return [];
    }

    function refreshHitCounts(callback) {
        if (_hitsHandle === undefined) {
            callback && callback();
            return;
        }

        _invoke("hitCountDeltas", [_hitsHandle], function (deltas) {
            if (deltas) {
                for (var id in deltas) {
                    _nodeHitCounts[id] = (_nodeHitCounts[id] || 0) + deltas[id];
                }
                callback && callback(_nodeHitCounts, deltas);
            } else {
                callback && callback(_nodeHitCounts, {});
            }
        });
    }

    function refreshExceptionCounts(callback) {
        if (_exceptionsHandle === undefined) {
            callback && callback();
            return;
        }

        _invoke("newExceptions", [_exceptionsHandle], function (exceptions) {
            if (exceptions) {
                for (var id in exceptions.counts) {
                    _nodeExceptionCounts[id] = (_nodeExceptionCounts[id] || 0) + exceptions.counts[id];
                }
                callback && callback(_nodeExceptionCounts, exceptions.counts);
            } else {
                callback && callback(_nodeExceptionCounts, {});
            }
        });
    }

    function cachedHitCounts() {
        return _nodeHitCounts;
    }

    function trackLogs(query, callback) {
        _invoke("trackLogs", [query], callback);
    }

    function refreshLogs(handle, maxResults, callback) {
        _invoke("logDelta", [handle, maxResults], function (results) {
            if (results) {
                results.forEach(function (entry) {
                    entry.source = "node";
                });
            }
            callback(results);
        });
    }

    function backtrace(options, callback) {
        _invoke("backtrace", [options], function (backtrace) {
            if (backtrace) {
                backtrace.forEach(function (entry) {
                    entry.source = "node";
                });
                callback(backtrace);
            } else {
                callback();
            }
        });
    }

    function resetTrace() {
        if (_connected) {
            var realDisconnect = false;
            var detectRealDisconnect = function () {
                realDisconnect = true;
            };

            // make everyone think we've disconnected
            $exports.triggerHandler("disconnect"); // simulated

            // detect whether we *actually* get disconnected before we emit another "connected" event
            $exports.on("disconnect", detectRealDisconnect);

            // clear all the locally cached trace data
            _reset();

            // clear the remote trace data
            _invokePromise("resetTrace", []).then(function () {
                $exports.off("disconnect", detectRealDisconnect);

                // if there wasn't a real disconnection in the meantime, simulate a reconnection
                if (!realDisconnect) {
                    _onConnect();
                }
            });
        }
    }

    function wrapServerFunction(localName, remoteName) {
        exports[localName] = function () {
            return _invokePromise(remoteName, Array.prototype.slice.apply(arguments));
        };
    }

    var trackerFunctions = {
        trackNodes: "trackNodes",
        untrackNodes: "untrackNodes",
        nodeDelta: "newNodes",

        trackEpochs: "trackEpochs",
        untrackEpochs: "untrackEpochs",
        epochDelta: "epochDelta",

        trackExceptions: "trackExceptions",
        untrackExceptions: "untrackExceptions",
        exceptionDelta: "newExceptions",

        trackFileCallGraph: "trackFileCallGraph",
        untrackFileCallGraph: "untrackFileCallGraph",
        fileCallGraphDelta: "fileCallGraphDelta",

        logCount: "logCount",
    };
    for (var fname in trackerFunctions) {
        wrapServerFunction(fname, trackerFunctions[fname]);
    }

    exports.init = init;
    exports.isReady = isReady;
    exports.id = "agent-node";

    // satisfied from locally cached data (sync)
    exports.functionWithId = functionWithId;
    exports.functionsInFile = functionsInFile;
    exports.cachedHitCounts = cachedHitCounts;

    // fetch data from the instrumented app (async)
    exports.refreshHitCounts = refreshHitCounts;
    exports.refreshExceptionCounts = refreshExceptionCounts;
    exports.trackLogs = trackLogs;
    exports.refreshLogs = refreshLogs;

    exports.backtrace = backtrace;

    exports.resetTrace = resetTrace;
});
<commit_msg>after a trace reset, always 'reconnect'<commit_after>/*
 * Copyright (c) 2012 Massachusetts Institute of Technology, Adobe Systems
 * Incorporated, and other contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */

/**
 * Your connection to instrumented node code.
 *
 * Provides these events:
 *
 *   - receivedScriptInfo (path):
 *       when information about functions and call sites has been received
 *   - scriptWentAway (path):
 *       when the connection to the instance for a particular path has closed
 */

define(function (require, exports, module) {
    var Agent   = require("Agent");
    var Dialogs = brackets.getModule("widgets/Dialogs");
    var semver  = require("./lib/semver");
    var Strings = require("strings");

    var dialogHTML     = require("text!InvalidVersion.html");
    var dialogTemplate = Mustache.render(dialogHTML, {Strings : Strings});

    var _conn;
    var _connected = false;
    var _nodes = {}; // id (string) -> {id: string, path: string, start: {line, column}, end: {line, column}, name: string (optional)}
    var _nodesByFilePath = {};
    var _hitsHandle, _exceptionsHandle;
    var _nodeHitCounts = {};
    var _nodeExceptionCounts = {};
    var $exports = $(exports);

    var REQUIRED_FONDUE_VERSION = JSON.parse(require("text!package.json")).dependencies.fondue;

    function Connection() {
        this.socket = new WebSocket("ws://localhost:8888/");
        this.socket.onerror = this._onerror.bind(this);
        this.socket.onopen = this._onopen.bind(this);
        this.socket.onmessage = this._onmessage.bind(this);
        this.socket.onclose = this._onclose.bind(this);
        this.connected = new $.Deferred();
        this.disconnected = new $.Deferred();
        this._nextRequestIndex = 0;
        this._requests = {};
    }
    Connection.prototype = {
        _onerror: function () {
            this.connected.reject();
            this.disconnected.resolve();
            this._rejectAllPending();
        },
        _onopen: function () {
            this.connected.resolve();
        },
        _onmessage: function (msg) {
            var resp;
            try {
                resp = JSON.parse(msg.data);
            } catch (e) {
                return;
            }

            if (resp.id in this._requests) {
                if ("data" in resp) {
                    this._requests[resp.id].resolve(resp.data);
                } else {
                    this._requests[resp.id].reject();
                }
                delete this._requests[resp.id];
            }
        },
        _onclose: function () {
            this.connected.reject();
            this.disconnected.resolve();
            this._rejectAllPending();
        },
        _rejectAllPending: function () {
            for (var i in this._requests) {
                this._requests[i].reject();
            }
            this._requests = {};
        },
        request: function (name, args) {
            var deferred = new $.Deferred();
            var idx = this._nextRequestIndex++;
            this.socket.send(JSON.stringify({
                name: name,
                arguments: args || [],
                id: idx
            }));
            this._requests[idx] = deferred;
            return deferred.promise();
        },
    };

    function _addNodes(nodes) {
        var indexByPath = function (obj, path, hash) {
            if (path in hash) {
                hash[path].push(obj);
            } else {
                hash[path] = [obj];
            }
        }

        for (var i in nodes) {
            var n = nodes[i];
            n.path = n.path.replace(/\\/g, "/"); // XXX: "Windows support"
            _nodes[n.id] = n;
            indexByPath(n, n.path, _nodesByFilePath);
        }

        // de-dup paths, then send receivedScriptInfo event for each one
        var pathsO = {};
        for (var i in nodes) { pathsO[nodes[i].path] = true; }
        for (var path in pathsO) {
            _triggerReceivedScriptInfo(path);
        }
    }

    function _triggerReceivedScriptInfo(path) {
        $exports.triggerHandler("receivedScriptInfo", [path]);
    }

    function _invoke(name, args, callback) {
        _conn.connected.done(function () {
            _conn.request(name, args).done(function (value) {
                callback && callback(value);
            }).fail(function () {
                callback && callback();
            });
        }).fail(function () {
            callback && callback();
        });
    }

    function _invokePromise(name, args) {
        return _conn.connected.pipe(function () {
            return _conn.request(name, args);
        });
    }

    function _reset() {
        _nodes = {};
        _nodesByFilePath = {};
        _hitsHandle = undefined;
        _exceptionsHandle = undefined;
        _nodeHitCounts = {};
        _nodeExceptionCounts = {};
    }

    function _connect() {
        _conn = new Connection();

        _conn.connected.done(_onConnect);

        _conn.disconnected.done(function () {
            if (_connected) $exports.triggerHandler("disconnect");

            _connected = false;

            var paths = [];
            for (var path in _nodesByFilePath) {
                paths.push(path);
            }

            _reset();

            paths.forEach(function (path) {
                $exports.triggerHandler("scriptWentAway", [path]);
            });

            setTimeout(_connect, 1000);
        });
    }

    function _onConnect() {
        _connected = true;

        // get the handle to use for tracking hits
        _invoke("version", [], function (version) {
            if (!semver.satisfies(version, REQUIRED_FONDUE_VERSION)) {
                var dialog = Dialogs.showModalDialogUsingTemplate(dialogTemplate);
                var $dialog = dialog.getElement();
                $dialog.find(".close").on("click", dialog.close.bind(dialog));
            }
        });

        $exports.triggerHandler("connect");

        // get the handle to use for tracking hits
        _invoke("trackHits", [], function (handle) {
            _hitsHandle = handle;
        });

        _invoke("trackExceptions", [], function (handle) {
            _exceptionsHandle = handle;
        });

        // poll for new nodes
        _invoke("trackNodes", [], function (handle) {
            var id = setInterval(function () {
                _invoke("newNodes", [handle], function (nodes) {
                    if (nodes) {
                        _addNodes(nodes);
                    }
                });
            }, 1000);

            _conn.disconnected.done(function () {
                clearInterval(id);
            });
        });
    }

    function init() {
        _connect();
    }

    function isReady() {
        return _connected;
    }

    function functionWithId(fid) {
        return _nodes[fid];
    }

    function functionsInFile(path) {
        for (var remotePath in _nodesByFilePath) {
            if (Agent.couldBeRemotePath(path, remotePath)) {
                var nodes = _nodesByFilePath[remotePath];
                if (nodes) {
                    return nodes.filter(function (n) { return n.type === "function" });
                }
            }
        }
        return [];
    }

    function refreshHitCounts(callback) {
        if (_hitsHandle === undefined) {
            callback && callback();
            return;
        }

        _invoke("hitCountDeltas", [_hitsHandle], function (deltas) {
            if (deltas) {
                for (var id in deltas) {
                    _nodeHitCounts[id] = (_nodeHitCounts[id] || 0) + deltas[id];
                }
                callback && callback(_nodeHitCounts, deltas);
            } else {
                callback && callback(_nodeHitCounts, {});
            }
        });
    }

    function refreshExceptionCounts(callback) {
        if (_exceptionsHandle === undefined) {
            callback && callback();
            return;
        }

        _invoke("newExceptions", [_exceptionsHandle], function (exceptions) {
            if (exceptions) {
                for (var id in exceptions.counts) {
                    _nodeExceptionCounts[id] = (_nodeExceptionCounts[id] || 0) + exceptions.counts[id];
                }
                callback && callback(_nodeExceptionCounts, exceptions.counts);
            } else {
                callback && callback(_nodeExceptionCounts, {});
            }
        });
    }

    function cachedHitCounts() {
        return _nodeHitCounts;
    }

    function trackLogs(query, callback) {
        _invoke("trackLogs", [query], callback);
    }

    function refreshLogs(handle, maxResults, callback) {
        _invoke("logDelta", [handle, maxResults], function (results) {
            if (results) {
                results.forEach(function (entry) {
                    entry.source = "node";
                });
            }
            callback(results);
        });
    }

    function backtrace(options, callback) {
        _invoke("backtrace", [options], function (backtrace) {
            if (backtrace) {
                backtrace.forEach(function (entry) {
                    entry.source = "node";
                });
                callback(backtrace);
            } else {
                callback();
            }
        });
    }

    function resetTrace() {
        if (_connected) {
            var realDisconnect = false;
            var detectRealDisconnect = function () {
                realDisconnect = true;
            };

            // make everyone think we've disconnected
            $exports.triggerHandler("disconnect"); // simulated

            // detect whether we *actually* get disconnected before we emit another "connected" event
            $exports.on("disconnect", detectRealDisconnect);

            // clear all the locally cached trace data
            _reset();

            // clear the remote trace data
            _invokePromise("resetTrace", []).always(function () {
                $exports.off("disconnect", detectRealDisconnect);

                // if there wasn't a real disconnection in the meantime, simulate a reconnection
                if (!realDisconnect) {
                    _onConnect();
                }
            });
        }
    }

    function wrapServerFunction(localName, remoteName) {
        exports[localName] = function () {
            return _invokePromise(remoteName, Array.prototype.slice.apply(arguments));
        };
    }

    var trackerFunctions = {
        trackNodes: "trackNodes",
        untrackNodes: "untrackNodes",
        nodeDelta: "newNodes",

        trackEpochs: "trackEpochs",
        untrackEpochs: "untrackEpochs",
        epochDelta: "epochDelta",

        trackExceptions: "trackExceptions",
        untrackExceptions: "untrackExceptions",
        exceptionDelta: "newExceptions",

        trackFileCallGraph: "trackFileCallGraph",
        untrackFileCallGraph: "untrackFileCallGraph",
        fileCallGraphDelta: "fileCallGraphDelta",

        logCount: "logCount",
    };
    for (var fname in trackerFunctions) {
        wrapServerFunction(fname, trackerFunctions[fname]);
    }

    exports.init = init;
    exports.isReady = isReady;
    exports.id = "agent-node";

    // satisfied from locally cached data (sync)
    exports.functionWithId = functionWithId;
    exports.functionsInFile = functionsInFile;
    exports.cachedHitCounts = cachedHitCounts;

    // fetch data from the instrumented app (async)
    exports.refreshHitCounts = refreshHitCounts;
    exports.refreshExceptionCounts = refreshExceptionCounts;
    exports.trackLogs = trackLogs;
    exports.refreshLogs = refreshLogs;

    exports.backtrace = backtrace;

    exports.resetTrace = resetTrace;
});
<|endoftext|>


<commit_before>-My build DIGITAL Command Language:
-Muhammad Adi Nugroho,S.Adm.Neg.:
-Via My Notepad (HTML Editor):

-HAR:

-To be evaluated in console:

<commit_msg>Delete cookieControl-7.0.min.js<commit_after><|endoftext|>


<commit_before>import React from 'react'
import { findDOMNode } from 'react-dom'

import { changeEvent } from "../controllers/values";
import { resultsEvent } from "../controllers/pipeline";

const RIGHT_ARROW_KEYCODE = 39
const TAB_KEYCODE = 9
const RETURN_KEYCODE = 13

class AutocompleteInput extends React.Component {
  constructor(props) {
    super(props)
    this.setText = this.setText.bind(this);
    this.valuesUpdated = this.valuesUpdated.bind(this);
    this.getState = this.getState.bind(this);
    this.state = {
      ...this.getState(),
      qParam: props.qParam || "q",
      qOverrideParam: props.qOverrideParam || "q.override",
    };
  }

  getState() {
    const text = this.props.values.get()[this.state.qParam] || "";
    const responseValues = this.props.pipeline.getResponseValues();
    const completion = responseValues ? (responseValues[this.state.qParam] || "") : "";
    return { text, completion };
  }

  componentDidMount() {
    if (this.props.focus) {
      findDOMNode(this.refs.searchInput).focus()
    }

    this.removeValuesListener = this.props.values.listen(changeEvent, this.valuesUpdated);
    this.removeResultsListener = this.props.pipeline.listen(resultsEvent, this.valuesUpdated);
  }

  componentWillUnmount() {
    this.removeValuesListener();
    this.removeResultsListener();
  }

  valuesUpdated() {
    this.setState(this.getState())
  }

  setText(text, override = false) {
    const textValues = {
      [this.state.qParam] : text,
      [this.state.qOverrideParam]: override ? "true" : undefined,
    };
    this.props.values.set(textValues);
    if (textValues[this.state.qParam]) {
      this.props.pipeline.search();
    } else {
      this.props.pipeline.clearResults();
      this.props.pipeline.emitTrackingReset();
    }
  }

  render() {
    const { text, completion } = this.state
    const { placeHolder } = this.props

    return (

          <input
            type="text"
            id='sj-search-bar-completion'
            className='sj-search-bar-input-common'
            value={completion.indexOf(text) === 0 ? completion : text}
            readOnly
          />
          <input
            type="text"
            ref='searchInput'
            id='sj-search-bar-input'
            className='sj-search-bar-input-common'
            placeholder={placeHolder}
            value={text}
            onChange={e => this.setText(e.target.value)}
            onKeyDown={e => {
              if (e.keyCode === RETURN_KEYCODE) {
                e.preventDefault()
                this.setText(text, true)
              }
              if (!completion) { return }
              if (e.keyCode === TAB_KEYCODE || (e.keyCode === RIGHT_ARROW_KEYCODE && e.target.selectionStart === text.length)) {
                e.preventDefault()
                this.setText(completion)
              }
            }}
          />

    )
  }
}

export default AutocompleteInput;
<commit_msg>pipeline: allow specifying params on autocompleteinput<commit_after>import React from 'react'
import { findDOMNode } from 'react-dom'

import { changeEvent } from "../controllers/values";
import { resultsEvent } from "../controllers/pipeline";

const RIGHT_ARROW_KEYCODE = 39
const TAB_KEYCODE = 9
const RETURN_KEYCODE = 13

class AutocompleteInput extends React.Component {
  constructor(props) {
    super(props)
    this.setText = this.setText.bind(this);
    this.valuesUpdated = this.valuesUpdated.bind(this);
    this.getState = this.getState.bind(this);
    const qParam = props.qParam || "q";
    const qOverrideParam = props.qOverrideParam || "q.override";
    this.state = {
      ...this.getState(props.values, props.pipeline, qParam),
      qParam,
      qOverrideParam,
    };
  }

  getState(values, pipeline, qParam) {
    const text = values.get()[qParam] || "";
    const responseValues = pipeline.getResponseValues();
    const completion = responseValues ? (responseValues[qParam] || "") : "";
    return { text, completion };
  }

  componentDidMount() {
    if (this.props.focus) {
      findDOMNode(this.refs.searchInput).focus()
    }

    this.removeValuesListener = this.props.values.listen(changeEvent, this.valuesUpdated);
    this.removeResultsListener = this.props.pipeline.listen(resultsEvent, this.valuesUpdated);
  }

  componentWillUnmount() {
    this.removeValuesListener();
    this.removeResultsListener();
  }

  valuesUpdated() {
    this.setState(this.getState(this.props.values, this.props.pipeline, this.state.qParam));
  }

  setText(text, override = false) {
    const textValues = {
      [this.state.qParam] : text,
      [this.state.qOverrideParam]: override ? "true" : undefined,
    };
    this.props.values.set(textValues);
    if (textValues[this.state.qParam]) {
      this.props.pipeline.search();
    } else {
      this.props.pipeline.clearResults();
      this.props.pipeline.emitTrackingReset();
    }
  }

  render() {
    const { text, completion } = this.state
    const { placeHolder } = this.props

    return (

          <input
            type="text"
            id='sj-search-bar-completion'
            className='sj-search-bar-input-common'
            value={completion.indexOf(text) === 0 ? completion : text}
            readOnly
          />
          <input
            type="text"
            ref='searchInput'
            id='sj-search-bar-input'
            className='sj-search-bar-input-common'
            placeholder={placeHolder}
            value={text}
            onChange={e => this.setText(e.target.value)}
            onKeyDown={e => {
              if (e.keyCode === RETURN_KEYCODE) {
                e.preventDefault()
                this.setText(text, true)
              }
              if (!completion) { return }
              if (e.keyCode === TAB_KEYCODE || (e.keyCode === RIGHT_ARROW_KEYCODE && e.target.selectionStart === text.length)) {
                e.preventDefault()
                this.setText(completion)
              }
            }}
          />

    )
  }
}

export default AutocompleteInput;
<|endoftext|>


<commit_before>"use strict";

var environment = {
	"code": {
		"required": true,
		"type": "string",
		"format": "alphanumeric"
	},
	"description": {
		"required": true,
		"type": "string"
	},
	"domain": {
		"required": false,
		"type": "string",
		"format": "hostname"
	},
	"apiPrefix": {
		"required": false,
		"type": "string"
	},
	"sitePrefix": {
		"required": false,
		"type": "string"
	},
	"sensitive": {
		"required": false,
		"type": "boolean"
	},
	"tKeyPass": {
		"required": false,
		"type": "string"
	},
	"cookiesecret": {
		"required": false,
		"type": "string"
	},
	"sessionName": {
		"required": false,
		"type": "string"
	},
	"sessionSecret": {
		"required": false,
		"type": "string"
	},
	"deployPortal": {
		"required": false,
		"type": "boolean"
	},
	"deploy": {
		"required": false,
		"type": "object",
		"propeties": {

		}
	},
	"templateId": {
		"required": true,
		"type": "string"
	},
	"infraId": {
		"required": false,
		"type": "string"
	}
};

module.exports = environment;
<commit_msg>description is now optional<commit_after>"use strict";

var environment = {
	"code": {
		"required": true,
		"type": "string",
		"format": "alphanumeric"
	},
	"description": {
		"required": false,
		"type": "string"
	},
	"domain": {
		"required": false,
		"type": "string",
		"format": "hostname"
	},
	"apiPrefix": {
		"required": false,
		"type": "string"
	},
	"sitePrefix": {
		"required": false,
		"type": "string"
	},
	"sensitive": {
		"required": false,
		"type": "boolean"
	},
	"tKeyPass": {
		"required": false,
		"type": "string"
	},
	"cookiesecret": {
		"required": false,
		"type": "string"
	},
	"sessionName": {
		"required": false,
		"type": "string"
	},
	"sessionSecret": {
		"required": false,
		"type": "string"
	},
	"deployPortal": {
		"required": false,
		"type": "boolean"
	},
	"deploy": {
		"required": false,
		"type": "object",
		"propeties": {

		}
	},
	"templateId": {
		"required": true,
		"type": "string"
	},
	"infraId": {
		"required": false,
		"type": "string"
	}
};

module.exports = environment;
<|endoftext|>


<commit_before>// Constants
const NUMBER_CLASS = ['one', 'two', 'three', 'four', 'five', 'six']
const ALL_TABS = getElementsByClassName('hero-tab')
let ACTIVE_TAB_INDEX = 0
const ALL_REVIEWS = getElementsByClassName('reviews-review')
let ACTIVE_REVIEW_INDEX = 0

// Calculate Section.Hero height
const setHeroHeight = () => {
	const heroSection = getElementByClassName('section hero')
	if (
		deviceType() === 'desktop'
		|| (deviceType() === 'large tablet' && deviceOrientation() === 'landscape')
		|| deviceType() === 'tablet'
		|| deviceType() === 'mobile'
	) {
		// Set min height on mobile to 550px
		heroSection.style.height = deviceType() === 'mobile' && deviceOrientation() === 'landscape'
			? Math.max(window.innerHeight * 0.95 + 'px', '550px')
			: window.innerHeight * 0.95 + 'px'
	}
	else {
		heroSection.style.height = window.innerHeight * 0.5 + 'px'
	}
}

// Set width of boxes section for width that’s smaller than desktop yet larger than iPad portrait; not configurable in Webflow interface.
const setBoxSections = () => {
	const sectionBoxes = getElementsByClassName('section boxes')
	const boxContainer = getElementsByClassName('box-container')
	let sectionBoxesHeight, boxContainerWidth
	// Use desktop dimensions
	if (window.innerWidth >= 1240) {
		sectionBoxesHeight = '200px'
		boxContainerWidth = ''
	}
	// iPad portrait, and dimensions that are smaller than desktop, yet still larger than iPad portrait (such as iPad landscape)
	else if (window.innerWidth >= 768) {
		sectionBoxesHeight = '500px'
		boxContainerWidth = '768px'
	}
	// iPhone landscape and portrait
	else {
		sectionBoxesHeight = '1100px'
		boxContainerWidth = ''
	}
	// Do this for all box section and containers on homepage
	for (let i = 0; i < boxContainer.length; i++) {
		sectionBoxes[i].style.height = sectionBoxesHeight
		boxContainer[i].style.width = boxContainerWidth
	}
}

// Fade in tab
const fadeInTab = tabIndex => {
	// Make tab and clickable elements visible
	ALL_TABS[tabIndex].classList.add('display')
	const heroButton = getElementByClassName('hero-button', tabIndex)
	const heroArrows = getElementByClassName('hero-arrows', tabIndex)
	heroButton.classList.add('display')
	heroArrows.classList.add('display')
	// Fade in image
	setTimeout(() => ALL_TABS[tabIndex].classList.add('fade'), 100)
	// Slide in Hero Text
	setTimeout(() => {
		for (let i = 0; i < 3; i++) {
			const title = getElementByClassName('hero-title ' + NUMBER_CLASS[i], tabIndex)
			title.classList.add('fade-move')
		}
	}, 500)
	// Slide in Hero Button
	setTimeout(() => heroButton.classList.add('fade-move'), 800)
	// Slide in Hero Arrows
	setTimeout(() => heroArrows.classList.add('fade-move'), 1100)
}

const resetTab = tabIndex => {
	// Hide Hero Text
	for (let i = 0; i < 3; i++) {
		const title = getElementByClassName('hero-title ' + NUMBER_CLASS[i], tabIndex)
		title.classList.remove('fade-move')
	}
	// Hide Hero Button
	const heroButton = getElementByClassName('hero-button', tabIndex)
	heroButton.classList.remove('fade-move')
	heroButton.classList.remove('display')
	// Hide Hero Arrows
	const heroArrows = getElementByClassName('hero-arrows', tabIndex)
	heroArrows.classList.remove('fade-move')
	heroArrows.classList.remove('display')
	// Hide Tab
	ALL_TABS[tabIndex].classList.remove('fade')
	ALL_TABS[tabIndex].classList.remove('display')
}

const transitionTabs = ({ currentTab, nextTab }) => {
	// Fade out current tab
	ALL_TABS[currentTab].classList.remove('fade')
	// Fade in new slide
	fadeInTab(nextTab)
	// Reset current slide
	setTimeout(() => resetTab(currentTab), 500)
}

const fadeInReview = reviewIndex => {
	// Make review tab and clickable elements visible
	ALL_REVIEWS[reviewIndex].classList.add('display')
	ALL_REVIEWS[reviewIndex].classList.add('fade')
	// Fade in elements
	const reviewsQuoteMark = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuote = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuotees = getElementByClassName('reviews-quotee', reviewIndex)
	const reviewsArrows = getElementByClassName('reviews-arrows', reviewIndex)
	reviewsArrows.classList.add('display')
	setTimeout(() => reviewsQuoteMark.classList.add('fade-move'), 100)
	setTimeout(() => reviewsQuote.classList.add('fade-move'), 200)
	setTimeout(() => reviewsQuotees.classList.add('fade-move'), 400)
	setTimeout(() => reviewsArrows.classList.add('fade-move'), 800)
}

const resetReview = reviewIndex => {
	const reviewsQuoteMark = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuote = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuotees = getElementByClassName('reviews-quotee', reviewIndex)
	const reviewsArrows = getElementByClassName('reviews-arrows', reviewIndex)
	reviewsQuoteMark.classList.remove('fade-move')
	reviewsQuote.classList.remove('fade-move')
	reviewsQuotees.classList.remove('fade-move')
	reviewsArrows.classList.remove('fade-move')
	reviewsArrows.classList.remove('display')
	// Hide Review Tab
	ALL_REVIEWS[reviewIndex].classList.remove('fade')
	ALL_REVIEWS[reviewIndex].classList.remove('display')
}

const transitionReviews = ({ currentReview, nextReview }) => {
	// Fade out current tab
	ALL_REVIEWS[currentReview].classList.remove('fade')
	// Fade out current review tab
	resetReview(currentReview)
	// Fade in new slide
	setTimeout(() => fadeInReview(nextReview), 500)
}

// Begin
if (window.location.pathname === '/') {

	windowEventListener(['onload', 'orientationchange', 'resize'], () => {
		// Recalculate hero height on orientation change
		setHeroHeight()
		// As well as box section and container dimensions
		setBoxSections()
	})

	// Add event listener to cycle through all hero messages on arrow click
	const leftHeroArrows = getElementsByClassName('hero-arrow left')
	const rightHeroArrows = getElementsByClassName('hero-arrow right')
	for (let tabIndex = 0; tabIndex < rightHeroArrows.length; tabIndex++) {
		// Cycle forward
		rightHeroArrows[tabIndex].addEventListener('click', () => {
			ACTIVE_TAB_INDEX = tabIndex === ALL_TABS.length - 1 ? 0 : tabIndex + 1
			transitionTabs({ currentTab: tabIndex, nextTab: ACTIVE_TAB_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(tabInterval)
		})
		// Cycle backward
		leftHeroArrows[tabIndex].addEventListener('click', () => {
			ACTIVE_TAB_INDEX = tabIndex === 0 ? ALL_TABS.length - 1 : tabIndex - 1
			transitionTabs({ currentTab: tabIndex, nextTab: ACTIVE_TAB_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(tabInterval)
		})
	}

	// Add event listener to cycle through all reviews on arrow click
	const leftReviewsArrows = getElementsByClassName('hero-arrow left')
	const rightReviewsArrows = getElementsByClassName('hero-arrow right')
	for (let reviewIndex = 0; reviewIndex < rightReviewsArrows.length; reviewIndex++) {
		// Cycle forward
		rightReviewsArrows[reviewIndex].addEventListener('click', () => {
			ACTIVE_REVIEW_INDEX = reviewIndex === ALL_REVIEWS.length - 1 ? 0 : reviewIndex + 1
			transitionReviews({ currentReview: reviewIndex, nextReview: ACTIVE_REVIEW_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(reviewInterval)
		})
		// Cycle backward
		leftReviewsArrows[reviewIndex].addEventListener('click', () => {
			ACTIVE_REVIEW_INDEX = reviewIndex === 0 ? ALL_REVIEWS.length - 1 : reviewIndex - 1
			transitionReviews({ currentReview: reviewIndex, nextReview: ACTIVE_REVIEW_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(reviewInterval)
		})
	}

	// Remove display classes on init for hero tabs
	resetTab(0)

	// Remove display classes on init for reviews
	resetReview(0)

	// Set hero height
	setHeroHeight()

	// Fade in first slide
	fadeInTab(0)

	// Fade in first review
	fadeInReview(0)

	// Switch tabs periodically
	const tabInterval = setInterval(() => {
		const currentTab = ACTIVE_TAB_INDEX
		ACTIVE_TAB_INDEX = ACTIVE_TAB_INDEX === ALL_TABS.length - 1 ? 0 : ACTIVE_TAB_INDEX + 1
		transitionTabs({ currentTab, nextTab: ACTIVE_TAB_INDEX })
	}, 8000)
	// Switch reviews periodically
	const reviewInterval = setInterval(() => {
		const currentReview = ACTIVE_REVIEW_INDEX
		ACTIVE_REVIEW_INDEX = ACTIVE_REVIEW_INDEX === ALL_REVIEWS.length - 1 ? 0 : ACTIVE_REVIEW_INDEX + 1
		transitionReviews({ currentReview, nextReview: ACTIVE_REVIEW_INDEX })
	}, 8000)

}<commit_msg>Reviews.<commit_after>// Constants
const NUMBER_CLASS = ['one', 'two', 'three', 'four', 'five', 'six']
const ALL_TABS = getElementsByClassName('hero-tab')
let ACTIVE_TAB_INDEX = 0
const ALL_REVIEWS = getElementsByClassName('reviews-review')
let ACTIVE_REVIEW_INDEX = 0

// Calculate Section.Hero height
const setHeroHeight = () => {
	const heroSection = getElementByClassName('section hero')
	if (
		deviceType() === 'desktop'
		|| (deviceType() === 'large tablet' && deviceOrientation() === 'landscape')
		|| deviceType() === 'tablet'
		|| deviceType() === 'mobile'
	) {
		// Set min height on mobile to 550px
		heroSection.style.height = deviceType() === 'mobile' && deviceOrientation() === 'landscape'
			? Math.max(window.innerHeight * 0.95 + 'px', '550px')
			: window.innerHeight * 0.95 + 'px'
	}
	else {
		heroSection.style.height = window.innerHeight * 0.5 + 'px'
	}
}

// Set width of boxes section for width that’s smaller than desktop yet larger than iPad portrait; not configurable in Webflow interface.
const setBoxSections = () => {
	const sectionBoxes = getElementsByClassName('section boxes')
	const boxContainer = getElementsByClassName('box-container')
	let sectionBoxesHeight, boxContainerWidth
	// Use desktop dimensions
	if (window.innerWidth >= 1240) {
		sectionBoxesHeight = '200px'
		boxContainerWidth = ''
	}
	// iPad portrait, and dimensions that are smaller than desktop, yet still larger than iPad portrait (such as iPad landscape)
	else if (window.innerWidth >= 768) {
		sectionBoxesHeight = '500px'
		boxContainerWidth = '768px'
	}
	// iPhone landscape and portrait
	else {
		sectionBoxesHeight = '1100px'
		boxContainerWidth = ''
	}
	// Do this for all box section and containers on homepage
	for (let i = 0; i < boxContainer.length; i++) {
		sectionBoxes[i].style.height = sectionBoxesHeight
		boxContainer[i].style.width = boxContainerWidth
	}
}

// Fade in tab
const fadeInTab = tabIndex => {
	// Make tab and clickable elements visible
	ALL_TABS[tabIndex].classList.add('display')
	const heroButton = getElementByClassName('hero-button', tabIndex)
	const heroArrows = getElementByClassName('hero-arrows', tabIndex)
	heroButton.classList.add('display')
	heroArrows.classList.add('display')
	// Fade in image
	setTimeout(() => ALL_TABS[tabIndex].classList.add('fade'), 100)
	// Slide in Hero Text
	setTimeout(() => {
		for (let i = 0; i < 3; i++) {
			const title = getElementByClassName('hero-title ' + NUMBER_CLASS[i], tabIndex)
			title.classList.add('fade-move')
		}
	}, 500)
	// Slide in Hero Button
	setTimeout(() => heroButton.classList.add('fade-move'), 800)
	// Slide in Hero Arrows
	setTimeout(() => heroArrows.classList.add('fade-move'), 1100)
}

const resetTab = tabIndex => {
	// Hide Hero Text
	for (let i = 0; i < 3; i++) {
		const title = getElementByClassName('hero-title ' + NUMBER_CLASS[i], tabIndex)
		title.classList.remove('fade-move')
	}
	// Hide Hero Button
	const heroButton = getElementByClassName('hero-button', tabIndex)
	heroButton.classList.remove('fade-move')
	heroButton.classList.remove('display')
	// Hide Hero Arrows
	const heroArrows = getElementByClassName('hero-arrows', tabIndex)
	heroArrows.classList.remove('fade-move')
	heroArrows.classList.remove('display')
	// Hide Tab
	ALL_TABS[tabIndex].classList.remove('fade')
	ALL_TABS[tabIndex].classList.remove('display')
}

const transitionTabs = ({ currentTab, nextTab }) => {
	// Fade out current tab
	ALL_TABS[currentTab].classList.remove('fade')
	// Fade in new slide
	fadeInTab(nextTab)
	// Reset current slide
	setTimeout(() => resetTab(currentTab), 500)
}

const fadeInReview = reviewIndex => {
	// Make review tab and clickable elements visible
	ALL_REVIEWS[reviewIndex].classList.add('display')
	ALL_REVIEWS[reviewIndex].classList.add('fade')
	// Fade in elements
	const reviewsQuoteMark = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuote = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuotees = getElementByClassName('reviews-quotee', reviewIndex)
	const reviewsArrows = getElementByClassName('reviews-arrows', reviewIndex)
	reviewsArrows.classList.add('display')
	setTimeout(() => reviewsQuoteMark.classList.add('fade-move'), 100)
	setTimeout(() => reviewsQuote.classList.add('fade-move'), 200)
	setTimeout(() => reviewsQuotees.classList.add('fade-move'), 400)
	setTimeout(() => reviewsArrows.classList.add('fade-move'), 800)
}

const resetReview = reviewIndex => {
	const reviewsQuoteMark = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuote = getElementByClassName('reviews-quote', reviewIndex)
	const reviewsQuotees = getElementByClassName('reviews-quotee', reviewIndex)
	const reviewsArrows = getElementByClassName('reviews-arrows', reviewIndex)
	reviewsQuoteMark.classList.remove('fade-move')
	reviewsQuote.classList.remove('fade-move')
	reviewsQuotees.classList.remove('fade-move')
	reviewsArrows.classList.remove('fade-move')
	reviewsArrows.classList.remove('display')
	// Hide Review Tab
	ALL_REVIEWS[reviewIndex].classList.remove('fade')
	ALL_REVIEWS[reviewIndex].classList.remove('display')
}

const transitionReviews = ({ currentReview, nextReview }) => {
	// Fade out current tab
	ALL_REVIEWS[currentReview].classList.remove('fade')
	// Fade in new slide
	fadeInReview(nextReview)
	// Fade out current review tab
	setTimeout(() => resetReview(currentReview), 500)
}

// Begin
if (window.location.pathname === '/') {

	windowEventListener(['onload', 'orientationchange', 'resize'], () => {
		// Recalculate hero height on orientation change
		setHeroHeight()
		// As well as box section and container dimensions
		setBoxSections()
	})

	// Add event listener to cycle through all hero messages on arrow click
	const leftHeroArrows = getElementsByClassName('hero-arrow left')
	const rightHeroArrows = getElementsByClassName('hero-arrow right')
	for (let tabIndex = 0; tabIndex < rightHeroArrows.length; tabIndex++) {
		// Cycle forward
		rightHeroArrows[tabIndex].addEventListener('click', () => {
			ACTIVE_TAB_INDEX = tabIndex === ALL_TABS.length - 1 ? 0 : tabIndex + 1
			transitionTabs({ currentTab: tabIndex, nextTab: ACTIVE_TAB_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(tabInterval)
		})
		// Cycle backward
		leftHeroArrows[tabIndex].addEventListener('click', () => {
			ACTIVE_TAB_INDEX = tabIndex === 0 ? ALL_TABS.length - 1 : tabIndex - 1
			transitionTabs({ currentTab: tabIndex, nextTab: ACTIVE_TAB_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(tabInterval)
		})
	}

	// Add event listener to cycle through all reviews on arrow click
	const leftReviewsArrows = getElementsByClassName('hero-arrow left')
	const rightReviewsArrows = getElementsByClassName('hero-arrow right')
	for (let reviewIndex = 0; reviewIndex < rightReviewsArrows.length; reviewIndex++) {
		// Cycle forward
		rightReviewsArrows[reviewIndex].addEventListener('click', () => {
			ACTIVE_REVIEW_INDEX = reviewIndex === ALL_REVIEWS.length - 1 ? 0 : reviewIndex + 1
			transitionReviews({ currentReview: reviewIndex, nextReview: ACTIVE_REVIEW_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(reviewInterval)
		})
		// Cycle backward
		leftReviewsArrows[reviewIndex].addEventListener('click', () => {
			ACTIVE_REVIEW_INDEX = reviewIndex === 0 ? ALL_REVIEWS.length - 1 : reviewIndex - 1
			transitionReviews({ currentReview: reviewIndex, nextReview: ACTIVE_REVIEW_INDEX })
			// Interrupt auto-cycling if user manually clicked arrow
			clearInterval(reviewInterval)
		})
	}

	// Remove display classes on init for hero tabs
	resetTab(0)

	// Remove display classes on init for reviews
	resetReview(0)

	// Set hero height
	setHeroHeight()

	// Fade in first slide
	fadeInTab(0)

	// Fade in first review
	fadeInReview(0)

	// Switch tabs periodically
	const tabInterval = setInterval(() => {
		const currentTab = ACTIVE_TAB_INDEX
		ACTIVE_TAB_INDEX = ACTIVE_TAB_INDEX === ALL_TABS.length - 1 ? 0 : ACTIVE_TAB_INDEX + 1
		transitionTabs({ currentTab, nextTab: ACTIVE_TAB_INDEX })
	}, 8000)
	// Switch reviews periodically
	const reviewInterval = setInterval(() => {
		const currentReview = ACTIVE_REVIEW_INDEX
		ACTIVE_REVIEW_INDEX = ACTIVE_REVIEW_INDEX === ALL_REVIEWS.length - 1 ? 0 : ACTIVE_REVIEW_INDEX + 1
		transitionReviews({ currentReview, nextReview: ACTIVE_REVIEW_INDEX })
	}, 8000)

}<|endoftext|>


<commit_before>/**
 * intrabuild
 * (c) 2002-2008 siteartwork.de/MindPatterns
 * license@siteartwork.de
 *
 * $Author$
 * $Id$
 * $Date$
 * $Revision$
 * $LastChangedDate$
 * $LastChangedBy$
 * $URL$
 */

Ext.namespace('de.intrabuild.groupware.email');

de.intrabuild.groupware.email.EmailGrid = function(config, controller) {

    Ext.apply(this, config);

    this.controller = controller;
    this.enableHdMenu = false;

// ------------------------- set up buffered grid ------------------------------

    var reader = new Ext.ux.data.BufferedJsonReader({
        root            : 'items',
        totalProperty   : 'totalCount',
        versionProperty : 'version',
        id              : 'id'
    }, de.intrabuild.groupware.email.EmailItemRecord);

    reader.read = function(response){
        var json = response.responseText;
        var o = eval("("+json+")");
        if(!o) {
            throw {message: "JsonReader.read: Json object not found"};
        }

        var z = this.readRecords(o);
        if (o && (o.pendingItems === 0 || o.pendingItems > 0)) {
            z.pendingItems = parseInt(o.pendingItems);
        }

        return z;
    };

    // we receive the pendingItems count through a property
    // in the response object. We store it in the options
    var MyStore = Ext.extend(Ext.ux.grid.BufferedStore, {

        loadRecords: function(o, options, success)
        {
            if (o && (o.pendingItems === 0 || o.pendingItems > 0)) {
                this.pendingItems = o.pendingItems;
            } else {
                this.pendingItems = -1;
            }

            MyStore.superclass.loadRecords.call(this, o, options, success);
        }
    });

    this.store = new MyStore({
        storeId     : Ext.id(),
        bufferSize  : 300,
        autoLoad    : false,
        reader      : reader,
        sortInfo   : {field: 'date', direction: 'DESC'},
        pruneModifiedRecords : true,
        remoteSort  : true,
        listeners   : {
            remove : function (store, record, index) {
                Ext.ux.util.MessageBus.publish('de.intrabuild.groupware.email.EmailGrid.store.remove', {
                    item : record
                });
            },
            update : function (store, record, operation) {
                Ext.ux.util.MessageBus.publish('de.intrabuild.groupware.email.EmailGrid.store.update', {
                    item      : record,
                    operation : operation
                });
            }
        },
        url         : '/groupware/email/get.email.items/format/json'
    });

    this.view = new Ext.ux.grid.BufferedGridView({
        nearLimit   : 100,
        scrollDelay : 0,
        loadMask    : {
            msg : de.intrabuild.Gettext.gettext("Please wait...")
        },
        getRowClass : function(record, rowIndex, p, ds){
            if (record.data.isRead) {
                return 'de-intrabuild-groupware-email-EmailGrid-itemRead';
            } else {
                return 'de-intrabuild-groupware-email-EmailGrid-itemUnread';
            }
        }
    });

    this.selModel = new Ext.ux.grid.BufferedRowSelectionModel();

    /**
     * You can use an instance of BufferedGridToolbar for keeping track of the
     * current scroll position. It also gives you a refresh button and a loading
     * image that gets activated when the store buffers.
     * ...Yeah, I pretty much stole this one from the PagingToolbar!
     */
    this.bbar = new Ext.ux.BufferedGridToolbar({
        grid        : this,
        displayInfo : true,
        displayMsg  : de.intrabuild.Gettext.gettext("Emails {0} - {1} of {2}"),
        emptyMsg    : de.intrabuild.Gettext.gettext("No emails available")
    });

// ------------------------- ^^ EO set up buffered grid ------------------------

    this.columns = [{
        header: '

',
        width: 25,
        //align:'center',
        sortable: true,
        resizable : false,
        dataIndex: 'isAttachment',
        renderer : function(value, p, record){
               if (!value) {
                   return '&#160';
               }
               return '

';
           }
      },{
        header : '

',
        width: 25,
        //align:'center',
        sortable: true,
        resizable : false,
        dataIndex: 'isRead',
        renderer : function(value, p, record){
                      return '

';
                   }
      },{
        id : 'subject',
        header: de.intrabuild.Gettext.gettext("Subject"),
        width     : 315,
        sortable  : true,
        dataIndex : 'subject',
        renderer  : de.intrabuild.groupware.email.view.EmailGridRowRenderer.renderSubjectColumn
      },{
        header: de.intrabuild.Gettext.gettext("Sender"),
        width: 160,
        sortable: true,
        dataIndex: 'sender'
      },{
        header: de.intrabuild.Gettext.gettext("Recipients"),
        width: 160,
        sortable: true,
        dataIndex: 'recipients'
      },{
        header : '

',
        width: 25,
        //align:'center',
        resizable : false,
        sortable: true,
        dataIndex: 'isSpam',
        renderer : function(value, p, record){
                       return '

';
           }
      },{
        header: de.intrabuild.Gettext.gettext("Date"),
        width: 100,
        sortable: true,
        dataIndex: 'date',
        renderer: de.intrabuild.groupware.email.view.EmailGridRowRenderer.renderDateColumn
      }
    ];

    de.intrabuild.groupware.email.EmailGrid.superclass.constructor.call(this, {
        loadMask       : {msg: de.intrabuild.Gettext.gettext("Loading...")},
        autoScroll     : true,
        cls            : 'de-intrabuild-groupware-email-EmailGrid',
        trackMouseOver : false,
        enableDragDrop : true,
        ddGroup        : 'de.intrabuild.groupware-email-Email',
        ddText         : de.intrabuild.Gettext.gettext("{0} selected email(s)")
    });

    this.on('contextmenu',    this.onContextClick, this);
    this.on('rowcontextmenu', this.onRowContextClick, this);

    this.store.on('beforeselectionsload', this.onBeforeSelectionsLoad, this);
    this.store.on('selectionsload',       this.onSelectionsLoad,       this);

    this.store.proxy.on('loadexception', this.onLoadException, this);

};

Ext.extend(de.intrabuild.groupware.email.EmailGrid, Ext.grid.GridPanel, {

    onLoadException : function(proxy, options, response, jsError)
    {
        de.intrabuild.groupware.ResponseInspector.handleFailure(response, {
            onLogin: {
                fn : function(){
                    this.view.reset(true);
                },
                scope : this
            }
        });
    },

    onBeforeSelectionsLoad : function()
    {
        this.createContextMenu();

        this.menu.items.get(9).setDisabled(true);
        this.menu.items.get(9).setIconClass('de-intrabuild-groupware-selectionsLoading');

        var subItems  = this.menu.items.get(7).menu.items;
        subItems.get(0).setDisabled(true);
        subItems.get(0).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(1).setDisabled(true);
        subItems.get(1).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(3).setDisabled(true);
        subItems.get(3).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(4).setDisabled(true);
        subItems.get(4).setIconClass('de-intrabuild-groupware-selectionsLoading');

    },

    onSelectionsLoad : function()
    {
        this.createContextMenu();

        this.menu.items.get(9).setDisabled(false);
        this.menu.items.get(9).setIconClass('');

        var subItems  = this.menu.items.get(7).menu.items;
        subItems.get(0).setDisabled(false);
        subItems.get(0).setIconClass('');
        subItems.get(1).setDisabled(false);
        subItems.get(1).setIconClass('');
        subItems.get(3).setIconClass('');
        subItems.get(4).setIconClass('');

        if (this.controller.clkNodeId == this.controller.treePanel.folderSpam.id) {
            subItems.get(3).setDisabled(true);
            subItems.get(4).setDisabled(false);
        } else {
            subItems.get(3).setDisabled(false);
            subItems.get(4).setDisabled(false);
        }
    },

    onContextClick : function(e)
    {
        e.stopEvent();
    },

    onRowContextClick : function(grid, index, e)
    {
        var selModel = this.selModel;

        var nodeType = this.controller.treePanel.getSelectionModel().getSelectedNode();
        if (nodeType) {
            nodeType = nodeType.attributes.type;
        }

        this.createContextMenu();

        e.stopEvent();

        if (!selModel.isSelected(index)) {
            selModel.selectRow(index, false);
        }

        var menuItems = this.menu.items;
        var subItems  = menuItems.get(7).menu.items;

        // mark as spam has to be disabled in outbox, drafts and sent
        var tp = this.controller.treePanel;
        var clkNodeId = this.controller.clkNodeId;
        var isDrafts = clkNodeId == tp.folderDraft.id;
        var disableSpamItems = isDrafts || clkNodeId == tp.folderOutbox.id  ||
                                clkNodeId == tp.folderSent.id;
        var editDraft = menuItems.get(1);
        var openView = menuItems.get(0);
        editDraft.setVisible(isDrafts);

        if (selModel.getCount() == 1) {
            var ctxRecord = selModel.getSelected().data;
            openView.setDisabled(false);
            editDraft.setDisabled(false);
            subItems.get(0).setDisabled((ctxRecord.isRead == true));
            subItems.get(1).setDisabled(!(ctxRecord.isRead == true));
            if (nodeType === 'spam') {
                subItems.get(3).setDisabled(true);
                subItems.get(4).setDisabled(false);
            } else {
                subItems.get(3).setDisabled((ctxRecord.isSpam == true) || disableSpamItems);
                subItems.get(4).setDisabled((ctxRecord.isSpam == false) || disableSpamItems);
            }

        } else {
            openView.setDisabled(true);
            editDraft.setDisabled(true);
            var pendingRanges = this.selModel.getPendingSelections(true);

            if (pendingRanges.length == 0) {
                this.onSelectionsLoad();
            } else {
                this.store.loadSelections(pendingRanges);
            }
        }

        this.menu.showAt(e.getXY());
    },

    createContextMenu : function()
    {
        if(!this.menu){

            var decorateAccountRelatedClk = de.intrabuild.groupware.email.decorator.AccountActionComp.decorate;

            this.menu = new Ext.menu.Menu({
                items: [{
                    text  : de.intrabuild.Gettext.gettext("Open in new tab"),
                    handler : this.controller.openEmailView,
                    scope : this.controller
                  },

                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text    : de.intrabuild.Gettext.gettext("Edit draft"),
                    handler : function(){this.openEmailEditPanel(true, 'edit');},
                    scope   : this.controller
                  })),
                  '-' ,
                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Reply"),
                    handler : function(){this.openEmailEditPanel(true, 'reply');},
                    scope : this.controller
                  })),

                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Reply all"),
                    handler : function(){this.openEmailEditPanel(true, 'reply_all');},
                    scope : this.controller
                  })),
                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Forward"),
                    handler : function(){this.openEmailEditPanel(true, 'forward');},
                    scope : this.controller
                  })), '-', {
                      text : de.intrabuild.Gettext.gettext("Mark email"),
                      menu : {
                          items : [{
                                text    : de.intrabuild.Gettext.gettext("as read"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsRead(this.selModel.getSelections(), true);}
                              },{
                                text    : de.intrabuild.Gettext.gettext("as unread"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsRead(this.selModel.getSelections(), false);}
                              },
                              '-',{
                                text    : de.intrabuild.Gettext.gettext("as spam"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsSpam(this.selModel.getSelections(), true);}
                              },{
                                text    : de.intrabuild.Gettext.gettext("as \"no spam\""),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsSpam(this.selModel.getSelections(), false);}
                          }]
                      }
                  },
                  '-',{
                    text    : de.intrabuild.Gettext.gettext("Delete"),
                    scope   : this,
                    handler : function(){this.controller.deleteEmails(this.selModel.getSelections());}
                }]
            });
        }
    }

});<commit_msg> - enhancement: "stripeRows" property set to true to stripe rows in the email grid<commit_after>/**
 * intrabuild
 * (c) 2002-2008 siteartwork.de/MindPatterns
 * license@siteartwork.de
 *
 * $Author$
 * $Id$
 * $Date$
 * $Revision$
 * $LastChangedDate$
 * $LastChangedBy$
 * $URL$
 */

Ext.namespace('de.intrabuild.groupware.email');

de.intrabuild.groupware.email.EmailGrid = function(config, controller) {

    Ext.apply(this, config);

    this.controller = controller;
    this.enableHdMenu = false;

// ------------------------- set up buffered grid ------------------------------

    var reader = new Ext.ux.data.BufferedJsonReader({
        root            : 'items',
        totalProperty   : 'totalCount',
        versionProperty : 'version',
        id              : 'id'
    }, de.intrabuild.groupware.email.EmailItemRecord);

    reader.read = function(response){
        var json = response.responseText;
        var o = eval("("+json+")");
        if(!o) {
            throw {message: "JsonReader.read: Json object not found"};
        }

        var z = this.readRecords(o);
        if (o && (o.pendingItems === 0 || o.pendingItems > 0)) {
            z.pendingItems = parseInt(o.pendingItems);
        }

        return z;
    };

    // we receive the pendingItems count through a property
    // in the response object. We store it in the options
    var MyStore = Ext.extend(Ext.ux.grid.BufferedStore, {

        loadRecords: function(o, options, success)
        {
            if (o && (o.pendingItems === 0 || o.pendingItems > 0)) {
                this.pendingItems = o.pendingItems;
            } else {
                this.pendingItems = -1;
            }

            MyStore.superclass.loadRecords.call(this, o, options, success);
        }
    });

    this.store = new MyStore({
        storeId     : Ext.id(),
        bufferSize  : 300,
        autoLoad    : false,
        reader      : reader,
        sortInfo   : {field: 'date', direction: 'DESC'},
        pruneModifiedRecords : true,
        remoteSort  : true,
        listeners   : {
            remove : function (store, record, index) {
                Ext.ux.util.MessageBus.publish('de.intrabuild.groupware.email.EmailGrid.store.remove', {
                    item : record
                });
            },
            update : function (store, record, operation) {
                Ext.ux.util.MessageBus.publish('de.intrabuild.groupware.email.EmailGrid.store.update', {
                    item      : record,
                    operation : operation
                });
            }
        },
        url         : '/groupware/email/get.email.items/format/json'
    });

    this.view = new Ext.ux.grid.BufferedGridView({
        nearLimit   : 100,
        scrollDelay : 0,
        loadMask    : {
            msg : de.intrabuild.Gettext.gettext("Please wait...")
        },
        getRowClass : function(record, rowIndex, p, ds){
            if (record.data.isRead) {
                return 'de-intrabuild-groupware-email-EmailGrid-itemRead';
            } else {
                return 'de-intrabuild-groupware-email-EmailGrid-itemUnread';
            }
        }
    });

    this.selModel = new Ext.ux.grid.BufferedRowSelectionModel();

    /**
     * You can use an instance of BufferedGridToolbar for keeping track of the
     * current scroll position. It also gives you a refresh button and a loading
     * image that gets activated when the store buffers.
     * ...Yeah, I pretty much stole this one from the PagingToolbar!
     */
    this.bbar = new Ext.ux.BufferedGridToolbar({
        grid        : this,
        displayInfo : true,
        displayMsg  : de.intrabuild.Gettext.gettext("Emails {0} - {1} of {2}"),
        emptyMsg    : de.intrabuild.Gettext.gettext("No emails available")
    });

// ------------------------- ^^ EO set up buffered grid ------------------------

    this.columns = [{
        header: '

',
        width: 25,
        //align:'center',
        sortable: true,
        resizable : false,
        dataIndex: 'isAttachment',
        renderer : function(value, p, record){
               if (!value) {
                   return '&#160';
               }
               return '

';
           }
      },{
        header : '

',
        width: 25,
        //align:'center',
        sortable: true,
        resizable : false,
        dataIndex: 'isRead',
        renderer : function(value, p, record){
                      return '

';
                   }
      },{
        id : 'subject',
        header: de.intrabuild.Gettext.gettext("Subject"),
        width     : 315,
        sortable  : true,
        dataIndex : 'subject',
        renderer  : de.intrabuild.groupware.email.view.EmailGridRowRenderer.renderSubjectColumn
      },{
        header: de.intrabuild.Gettext.gettext("Sender"),
        width: 160,
        sortable: true,
        dataIndex: 'sender'
      },{
        header: de.intrabuild.Gettext.gettext("Recipients"),
        width: 160,
        sortable: true,
        dataIndex: 'recipients'
      },{
        header : '

',
        width: 25,
        //align:'center',
        resizable : false,
        sortable: true,
        dataIndex: 'isSpam',
        renderer : function(value, p, record){
                       return '

';
           }
      },{
        header: de.intrabuild.Gettext.gettext("Date"),
        width: 100,
        sortable: true,
        dataIndex: 'date',
        renderer: de.intrabuild.groupware.email.view.EmailGridRowRenderer.renderDateColumn
      }
    ];

    de.intrabuild.groupware.email.EmailGrid.superclass.constructor.call(this, {
        loadMask       : {msg: de.intrabuild.Gettext.gettext("Loading...")},
        autoScroll     : true,
        stripeRows     : true,
        cls            : 'de-intrabuild-groupware-email-EmailGrid',
        trackMouseOver : false,
        enableDragDrop : true,
        ddGroup        : 'de.intrabuild.groupware-email-Email',
        ddText         : de.intrabuild.Gettext.gettext("{0} selected email(s)")
    });

    this.on('contextmenu',    this.onContextClick, this);
    this.on('rowcontextmenu', this.onRowContextClick, this);

    this.store.on('beforeselectionsload', this.onBeforeSelectionsLoad, this);
    this.store.on('selectionsload',       this.onSelectionsLoad,       this);

    this.store.proxy.on('loadexception', this.onLoadException, this);

};

Ext.extend(de.intrabuild.groupware.email.EmailGrid, Ext.grid.GridPanel, {

    onLoadException : function(proxy, options, response, jsError)
    {
        de.intrabuild.groupware.ResponseInspector.handleFailure(response, {
            onLogin: {
                fn : function(){
                    this.view.reset(true);
                },
                scope : this
            }
        });
    },

    onBeforeSelectionsLoad : function()
    {
        this.createContextMenu();

        this.menu.items.get(9).setDisabled(true);
        this.menu.items.get(9).setIconClass('de-intrabuild-groupware-selectionsLoading');

        var subItems  = this.menu.items.get(7).menu.items;
        subItems.get(0).setDisabled(true);
        subItems.get(0).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(1).setDisabled(true);
        subItems.get(1).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(3).setDisabled(true);
        subItems.get(3).setIconClass('de-intrabuild-groupware-selectionsLoading');
        subItems.get(4).setDisabled(true);
        subItems.get(4).setIconClass('de-intrabuild-groupware-selectionsLoading');

    },

    onSelectionsLoad : function()
    {
        this.createContextMenu();

        this.menu.items.get(9).setDisabled(false);
        this.menu.items.get(9).setIconClass('');

        var subItems  = this.menu.items.get(7).menu.items;
        subItems.get(0).setDisabled(false);
        subItems.get(0).setIconClass('');
        subItems.get(1).setDisabled(false);
        subItems.get(1).setIconClass('');
        subItems.get(3).setIconClass('');
        subItems.get(4).setIconClass('');

        if (this.controller.clkNodeId == this.controller.treePanel.folderSpam.id) {
            subItems.get(3).setDisabled(true);
            subItems.get(4).setDisabled(false);
        } else {
            subItems.get(3).setDisabled(false);
            subItems.get(4).setDisabled(false);
        }
    },

    onContextClick : function(e)
    {
        e.stopEvent();
    },

    onRowContextClick : function(grid, index, e)
    {
        var selModel = this.selModel;

        var nodeType = this.controller.treePanel.getSelectionModel().getSelectedNode();
        if (nodeType) {
            nodeType = nodeType.attributes.type;
        }

        this.createContextMenu();

        e.stopEvent();

        if (!selModel.isSelected(index)) {
            selModel.selectRow(index, false);
        }

        var menuItems = this.menu.items;
        var subItems  = menuItems.get(7).menu.items;

        // mark as spam has to be disabled in outbox, drafts and sent
        var tp = this.controller.treePanel;
        var clkNodeId = this.controller.clkNodeId;
        var isDrafts = clkNodeId == tp.folderDraft.id;
        var disableSpamItems = isDrafts || clkNodeId == tp.folderOutbox.id  ||
                                clkNodeId == tp.folderSent.id;
        var editDraft = menuItems.get(1);
        var openView = menuItems.get(0);
        editDraft.setVisible(isDrafts);

        if (selModel.getCount() == 1) {
            var ctxRecord = selModel.getSelected().data;
            openView.setDisabled(false);
            editDraft.setDisabled(false);
            subItems.get(0).setDisabled((ctxRecord.isRead == true));
            subItems.get(1).setDisabled(!(ctxRecord.isRead == true));
            if (nodeType === 'spam') {
                subItems.get(3).setDisabled(true);
                subItems.get(4).setDisabled(false);
            } else {
                subItems.get(3).setDisabled((ctxRecord.isSpam == true) || disableSpamItems);
                subItems.get(4).setDisabled((ctxRecord.isSpam == false) || disableSpamItems);
            }

        } else {
            openView.setDisabled(true);
            editDraft.setDisabled(true);
            var pendingRanges = this.selModel.getPendingSelections(true);

            if (pendingRanges.length == 0) {
                this.onSelectionsLoad();
            } else {
                this.store.loadSelections(pendingRanges);
            }
        }

        this.menu.showAt(e.getXY());
    },

    createContextMenu : function()
    {
        if(!this.menu){

            var decorateAccountRelatedClk = de.intrabuild.groupware.email.decorator.AccountActionComp.decorate;

            this.menu = new Ext.menu.Menu({
                items: [{
                    text  : de.intrabuild.Gettext.gettext("Open in new tab"),
                    handler : this.controller.openEmailView,
                    scope : this.controller
                  },

                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text    : de.intrabuild.Gettext.gettext("Edit draft"),
                    handler : function(){this.openEmailEditPanel(true, 'edit');},
                    scope   : this.controller
                  })),
                  '-' ,
                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Reply"),
                    handler : function(){this.openEmailEditPanel(true, 'reply');},
                    scope : this.controller
                  })),

                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Reply all"),
                    handler : function(){this.openEmailEditPanel(true, 'reply_all');},
                    scope : this.controller
                  })),
                  decorateAccountRelatedClk(new Ext.menu.Item({
                    text  : de.intrabuild.Gettext.gettext("Forward"),
                    handler : function(){this.openEmailEditPanel(true, 'forward');},
                    scope : this.controller
                  })), '-', {
                      text : de.intrabuild.Gettext.gettext("Mark email"),
                      menu : {
                          items : [{
                                text    : de.intrabuild.Gettext.gettext("as read"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsRead(this.selModel.getSelections(), true);}
                              },{
                                text    : de.intrabuild.Gettext.gettext("as unread"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsRead(this.selModel.getSelections(), false);}
                              },
                              '-',{
                                text    : de.intrabuild.Gettext.gettext("as spam"),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsSpam(this.selModel.getSelections(), true);}
                              },{
                                text    : de.intrabuild.Gettext.gettext("as \"no spam\""),
                                scope   : this,
                                handler : function(){this.controller.setItemsAsSpam(this.selModel.getSelections(), false);}
                          }]
                      }
                  },
                  '-',{
                    text    : de.intrabuild.Gettext.gettext("Delete"),
                    scope   : this,
                    handler : function(){this.controller.deleteEmails(this.selModel.getSelections());}
                }]
            });
        }
    }

});<|endoftext|>


<commit_before><commit_msg>fix null content<commit_after><|endoftext|>