<commit_before>export class Envelope {
  /** Create an Envelope
   * @param {number} sessionId - identifies the session associated with this asset
   * @param {ApiClient} apiClient - the API client object to use for server API calls
   * @param {geoPosition} geoPosition -
   **/
  constructor(sessionId, apiClient, geoPosition, roundware) {
    this._envelopeId = "(unknown)";
    this._sessionId = sessionId;
    this._apiClient = apiClient;
    this._geoPosition = geoPosition;
    this._roundware = roundware;
  }

  /** @returns {String} human-readable representation of this asset **/
  toString() {
    return `Envelope ${this._assetId}`;
  }

  /** Create a new Envelope in the server to which we can attach audio recordings as assets
   * @returns {Promise} represents the pending API call **/
  async connect() {
    let data = {
      session_id: this._sessionId,
    };

    return this._apiClient.post("/envelopes/", data).then((data) => {
      this._envelopeId = data.id;
    });
  }

  /** Sends an audio file to the server
   * @param {blob} audioData
   * @param {string} fileName - name of the file
   * @return {Promise} - represents the API call */
  async upload(audioData, fileName, data = {}) {
    if (!this._envelopeId) {
      return Promise.reject(
        "cannot upload audio without first connecting this envelope to the server"
      );
    }

    let formData = new FormData();
    let coordinates = {};
    if (!data.latitude && !data.longitude) {
      coordinates = this._geoPosition.getLastCoords();
    } else {
      coordinates = {
        latitude: data.latitude,
        longitude: data.longitude,
      };
    }
    console.log(coordinates);

    formData.append("session_id", this._sessionId);
    formData.append("file", audioData);
    formData.append("latitude", coordinates.latitude);
    formData.append("longitude", coordinates.longitude);

    if ("tag_ids" in data) {
      formData.append("tag_ids", data.tag_ids);
    }
    if ("media_type" in data) {
      formData.append("media_type", data.media_type);
    }

    let path = `/envelopes/${this._envelopeId}/`;

    console.info(`Uploading ${fileName} to envelope ${path}`);

    let options = {
      contentType: "multipart/form-data",
    };

    const res = await this._apiClient.patch(path, formData, options);
    console.info("UPLOADDATA", res);
    if (res.detail) {
      throw new Error(res.detail);
    } else {
      // add the newly saved asset to the pool
      if (!data.media_type || data.media_type === "audio") {
        this._roundware._assetData.push(res);
      }
      return res;
    }
  }
}
<commit_msg>Update pool instead of manually adding new assets<commit_after>export class Envelope {
  /** Create an Envelope
   * @param {number} sessionId - identifies the session associated with this asset
   * @param {ApiClient} apiClient - the API client object to use for server API calls
   * @param {geoPosition} geoPosition -
   **/
  constructor(sessionId, apiClient, geoPosition, roundware) {
    this._envelopeId = "(unknown)";
    this._sessionId = sessionId;
    this._apiClient = apiClient;
    this._geoPosition = geoPosition;
    this._roundware = roundware;
  }

  /** @returns {String} human-readable representation of this asset **/
  toString() {
    return `Envelope ${this._assetId}`;
  }

  /** Create a new Envelope in the server to which we can attach audio recordings as assets
   * @returns {Promise} represents the pending API call **/
  async connect() {
    let data = {
      session_id: this._sessionId,
    };

    return this._apiClient.post("/envelopes/", data).then((data) => {
      this._envelopeId = data.id;
    });
  }

  /** Sends an audio file to the server
   * @param {blob} audioData
   * @param {string} fileName - name of the file
   * @return {Promise} - represents the API call */
  async upload(audioData, fileName, data = {}) {
    if (!this._envelopeId) {
      return Promise.reject(
        "cannot upload audio without first connecting this envelope to the server"
      );
    }

    let formData = new FormData();
    let coordinates = {};
    if (!data.latitude && !data.longitude) {
      coordinates = this._geoPosition.getLastCoords();
    } else {
      coordinates = {
        latitude: data.latitude,
        longitude: data.longitude,
      };
    }
    console.log(coordinates);

    formData.append("session_id", this._sessionId);
    formData.append("file", audioData);
    formData.append("latitude", coordinates.latitude);
    formData.append("longitude", coordinates.longitude);

    if ("tag_ids" in data) {
      formData.append("tag_ids", data.tag_ids);
    }
    if ("media_type" in data) {
      formData.append("media_type", data.media_type);
    }

    let path = `/envelopes/${this._envelopeId}/`;

    console.info(`Uploading ${fileName} to envelope ${path}`);

    let options = {
      contentType: "multipart/form-data",
    };

    const res = await this._apiClient.patch(path, formData, options);
    console.info("UPLOADDATA", res);
    if (res.detail) {
      throw new Error(res.detail);
    } else {
      // Update the asset pool to include the newly uploaded asset
      await this._roundware.updateAssetPool();
      return res;
    }
  }
}
<|endoftext|>


<commit_before>/*
 *  Waltz
 * Copyright (c) David Watkins. All rights reserved.
 * The use and distribution terms for this software are covered by the
 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 * which can be found in the file epl-v10.html at the root of this distribution.
 * By using this software in any fashion, you are agreeing to be bound by
 * the terms of this license.
 * You must not remove this notice, or any other, from this software.
 *
 */
import _ from "lodash";

function editController($state,
                        authSources,
                        flows,
                        orgUnits,
                        id,
                        authSourceStore,
                        notification,
                        displayNameService) {

    const dataTypes = displayNameService.toOptions('dataType');

    const authSourcesByType = _.groupBy(authSources, 'dataType');
    const orgUnitsById = _.keyBy(orgUnits, 'id');
    const orgUnit = orgUnitsById[id];
    const usedTypes = _.chain(flows).map('dataType').uniq().value();
    const unusedTypes = _.chain(dataTypes).map('code').without(...usedTypes).value();

    const wizard = {
        dataType: null,
        app: null,
        rating: null
    };

    function getSupplyingApps(dataType) {
        const apps = _.chain(flows)
            .filter(f => f.dataType === dataType)
            .filter(f => f.source.kind === 'APPLICATION')
            .map('source')
            .uniqBy('id')
            .value();

        return apps;
    }

    function isDisabled() {
        return !(wizard.dataType && wizard.app && wizard.rating);
    }

    function selectExisting(authSource) {
        wizard.dataType = authSource.dataType;
        wizard.app = authSource.applicationReference;
        wizard.rating = authSource.rating;
    }

    function resetWizard() {
        wizard.dataType = null;
        wizard.app = null;
        wizard.rating = null;
    }

    function refresh() {
        $state.reload();
    }

    function submit() {
        const existingAuthSource = _.find(authSources, a =>
            a.applicationReference.id === wizard.app.id &&
            a.dataType === wizard.dataType );

        if (existingAuthSource) {
            authSourceStore
                .update(existingAuthSource.id, wizard.rating)
                .then(() => existingAuthSource.rating = wizard.rating)
                .then(() => resetWizard());
        } else {
            const insertRequest = {
                kind: 'ORG_UNIT',
                id,
                dataType: wizard.dataType,
                appId: wizard.app.id,
                rating: wizard.rating
            };
            authSourceStore
                .insert(insertRequest)
                .then(
                    () => notification.success('Authoritative Sources updated'),
                    (e) => {
                        console.log('err', e);
                        notification.error('Update failed, ' + e.data.message || e.statusText);
                    }
                )
                .then(refresh);
        }
    }

    function remove(authSource) {
        authSourceStore
            .remove(authSource.id)
            .then(refresh)
            .then(() => notification.warning('Authoritative Source removed'));
    }

    const vm = this;
    Object.assign(vm, {
        authSources,
        flows,
        orgUnits,
        id,
        orgUnit,
        orgUnitsById,
        authSourcesByType,
        usedTypes,
        unusedTypes,

        getSupplyingApps,
        isDisabled,
        selectExisting,
        submit,
        remove,
        wizard
    });
}

editController.$inject = [
    '$state',
    'authSources',
    'flows',
    'orgUnits',
    'id',
    'AuthSourcesStore',
    'Notification',
    'WaltzDisplayNameService'
];

export default {
    template: require('./edit.html'),
    controller: editController,
    controllerAs: 'ctrl'
};
<commit_msg>Authoritative source button should not show if user does not have permission<commit_after>/*
 *  Waltz
 * Copyright (c) David Watkins. All rights reserved.
 * The use and distribution terms for this software are covered by the
 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 * which can be found in the file epl-v10.html at the root of this distribution.
 * By using this software in any fashion, you are agreeing to be bound by
 * the terms of this license.
 * You must not remove this notice, or any other, from this software.
 *
 */
import _ from "lodash";

function editController($state,
                        authSources,
                        flows,
                        orgUnits,
                        id,
                        authSourceStore,
                        notification,
                        displayNameService) {

    const dataTypes = displayNameService.toOptions('dataType');

    const authSourcesByType = _.groupBy(authSources, 'dataType');
    const orgUnitsById = _.keyBy(orgUnits, 'id');
    const orgUnit = orgUnitsById[id];
    const usedTypes = _.chain(flows).map('dataType').uniq().value();
    const unusedTypes = _.chain(dataTypes).map('code').without(...usedTypes).value();

    const wizard = {
        dataType: null,
        app: null,
        rating: null
    };

    function getSupplyingApps(dataType) {
        const apps = _.chain(flows)
            .filter(f => f.dataType === dataType)
            .filter(f => f.source.kind === 'APPLICATION')
            .map('source')
            .uniqBy('id')
            .value();

        return apps;
    }

    function isDisabled() {
        return !(wizard.dataType && wizard.app && wizard.rating);
    }

    function selectExisting(authSource) {
        wizard.dataType = authSource.dataType;
        wizard.app = authSource.applicationReference;
        wizard.rating = authSource.rating;
    }

    function resetWizard() {
        wizard.dataType = null;
        wizard.app = null;
        wizard.rating = null;
    }

    function refresh() {
        $state.reload();
    }

    function submit() {
        const existingAuthSource = _.find(authSources, a =>
            a.applicationReference.id === wizard.app.id &&
            a.dataType === wizard.dataType );

        if (existingAuthSource) {
            authSourceStore
                .update(existingAuthSource.id, wizard.rating)
                .then(() => existingAuthSource.rating = wizard.rating)
                .then(() => resetWizard());
        } else {
            const insertRequest = {
                kind: 'ORG_UNIT',
                id,
                dataType: wizard.dataType,
                appId: wizard.app.id,
                rating: wizard.rating
            };
            authSourceStore
                .insert(insertRequest)
                .then(
                    () => notification.success('Authoritative Sources updated'),
                    (e) => notification.error('Update failed, ' + e.data.message || e.statusText))
                .then(refresh);
        }
    }

    function remove(authSource) {
        authSourceStore
            .remove(authSource.id)
            .then(refresh)
            .then(() => notification.warning('Authoritative Source removed'));
    }

    const vm = this;
    Object.assign(vm, {
        authSources,
        flows,
        orgUnits,
        id,
        orgUnit,
        orgUnitsById,
        authSourcesByType,
        usedTypes,
        unusedTypes,

        getSupplyingApps,
        isDisabled,
        selectExisting,
        submit,
        remove,
        wizard
    });
}

editController.$inject = [
    '$state',
    'authSources',
    'flows',
    'orgUnits',
    'id',
    'AuthSourcesStore',
    'Notification',
    'WaltzDisplayNameService'
];

export default {
    template: require('./edit.html'),
    controller: editController,
    controllerAs: 'ctrl'
};
<|endoftext|>


<commit_before>/*
 * ESUI (Enterprise Simple UI)
 * Copyright 2010 Baidu Inc. All rights reserved.
 *
 * path:    ui/Tab.js
 * desc:    Tab标签控件
 * author:  zhaolei, erik, wanghuijun
 * date:    $Date: 2011-04-05 15:57:33 +0800 (二, 05  4 2011) $
 */

/**
 * Tab标签控件
 *
 * @param {Object} options 控件初始化参数
 */
ui.Tab = function (options) {
    this.__initOptions(options);
    this._type = "tab";

    this.activeIndex    = this.activeIndex || 0;
    this.allowEdit      = !!this.allowEdit;
	this.maxCount       = this.maxCount || ui.Tab.MAX_COUNT || 5;
};

ui.Tab.prototype = {
    /**
     * 渲染控件
     *
     * @protected
     * @param {HTMLElement} main 控件挂载的DOM
     */
    render: function (main) {
        var me = this;
        ui.Base.render.call(me, main, false);

        this.tabs = this.datasource || this.tabs || [];

        // 绘制内容部分
        me._main && me._renderTabs();
    },

    _tplItem: '
{0}{3}
',
    _tplAdd: '
+
',
    _tplClose: '',

    /**
     * 绘制标签区
     *
     * @private
     */
    _renderTabs: function () {
        var me        = this,
            main      = me._main,
            tabs      = me.tabs,
            len       = tabs.length,
            itemClass = me.__getClass('item'),
			html      = [],
            currClass,
            i,
			tab,
            title,
			closeHtml,
            clickHandler;

        if (len == 0) {
            main.innerHTML = '';
            return;
        } else if (len <= me.activeIndex) {
			me.activeIndex = 0;
		} else if (me.activeIndex < 0) {
            me.activeIndex = 0;
        }

        for (i = 0; i < len; i++) {
            tab = me.tabs[i];
            title = tab.title;
            currClass = itemClass;
            closeHtml = '';
            clickHandler = '';

            // 初始化关闭按钮
            if (me.allowEdit && !tab.stable) {
                closeHtml = ui._format(me.tplClose,
									   me.__getStrCall('_close', i));
            }

            // 首尾节点增加特殊class
            if (i == 0) {
                currClass += ' ' + me.__getClass('item-first');
            }
            if (i == len - 1) {
                currClass += ' ' + me.__getClass('item-last');
            }

            // 构建tab的样式与行为
            if (i == me.activeIndex) {
                currClass += ' ' + me.__getClass('item-active');
            } else {
                clickHandler = ' onclick="'
                                + me.__getStrCall('_select', i)
                                + '"';
            }

            // 构建tab项的html
            html.push(
                ui._format(me._tplItem,
                           title,
                           currClass,
                           clickHandler,
                           closeHtml));
        }

		// 填充tab的html
        main.innerHTML = '
' + html.join('') + '
';
		me._resetPanel();
    },

    /**
     * 重置tab对应的panel的显示隐藏状态
     *
     * @private
     */
    _resetPanel: function () {
        var tabs = this.tabs;
        var len  = tabs.length;
        var activeIndex = this.activeIndex;
        var i;
        var panel;

        for (i = 0; i < len; i++) {
            panel = tabs[i].panel;
            if (panel) {
                baidu.g(panel).style.display = (i == activeIndex ? '' : 'none');
            }
        }
    },

    onchange: new Function(),

    /**
     * 选择标签
     *
     * @private
     * @param {number} index 标签序号
     */
    _select: function (index) {
        if (this.onchange(index, this.tabs[index]) !== false) {
            this.select(index);
        }
    },

    /**
     * 选择标签
     *
     * @public
     * @param {number} index 标签序号
     */
    select: function (index) {
        this.activeIndex = index;
        this._renderTabs();
    },

	onclose: new Function(),

    /**
     * 关闭标签
     *
     * @private
     * @param {number} index 标签序号
     */
    _close: function (index) {
        if (this.onclose(index, this.tabs[index]) !== false) {
			this.close(index);
        }
    },

    /**
     * 关闭标签
     *
     * @private
     * @param {number} index 标签序号
     */
    close: function (index) {
        var tabs = this.tabs;
        tabs.splice(index, 1);

        // 重新设置activeIndex
        if (this.activeIndex >= tabs.length) {
            this.activeIndex--;
        }
        if (this.activeIndex < 0) {
            this.activeIndex = 0;
        }

        this._renderTabs();
    },

    /**
     * 添加标签
     *
     * @public
     * @param {Object} tab 标签数据
     */
    add: function (tab) {
        tab = tab || {title: '新建标签'}
        this.tabs.push(tab);
        this._renderTabs();
    }
};

ui.Base.derive(ui.Tab);<commit_msg>revise Tab 4 standard<commit_after>/*
 * ESUI (Enterprise Simple UI)
 * Copyright 2010 Baidu Inc. All rights reserved.
 *
 * path:    esui/Tab.js
 * desc:    Tab标签控件
 * author:  zhaolei, erik, wanghuijun
 */

///import esui.Control;
///import baidu.lang.inherits;

/**
 * Tab标签控件
 *
 * @param {Object} options 控件初始化参数
 */
esui.Tab = function ( options ) {
    // 类型声明，用于生成控件子dom的id和class
    this._type = 'tab';

    // 标识鼠标事件触发自动状态转换
    this._autoState = 0;

    esui.Control.call( this, options );

    this.activeIndex    = this.activeIndex || 0;
    this.allowEdit      = !!this.allowEdit;
	this.maxCount       = this.maxCount || esui.Tab.MAX_COUNT || 5;
};

esui.Tab.prototype = {
    /**
     * 渲染控件
     *
     * @public
     */
    render: function () {
        var me = this;
        esui.Control.prototype.render.call( me );

        this.tabs = this.datasource || this.tabs || [];

        // 绘制内容部分
        me._renderTabs();
    },

    _tplItem    : '
{0}{3}
',
    _tplAdd     : '
+
',
    _tplClose   : '',

    /**
     * 绘制标签区
     *
     * @private
     */
    _renderTabs: function () {
        var me        = this,
            main      = me.main,
            tabs      = me.tabs,
            len       = tabs.length,
            itemClass = me.__getClass( 'item' ),
			html      = [],
            currClass,
            i,
			tab,
            title,
			closeHtml,
            clickHandler;

        if ( len == 0 ) {
            main.innerHTML = '';
            return;
        } else if ( len <= me.activeIndex ) {
			me.activeIndex = 0;
		} else if ( me.activeIndex < 0 ) {
            me.activeIndex = 0;
        }

        for (i = 0; i < len; i++) {
            tab             = me.tabs[ i ];
            title           = tab.title;
            currClass       = itemClass;
            closeHtml       = '';
            clickHandler    = '';

            // 初始化关闭按钮
            if ( me.allowEdit && !tab.stable ) {
                closeHtml = esui.util.format(
                    me.tplClose,
					me.__getStrCall( '_close', i )
                );
            }

            // 首尾节点增加特殊class
            if ( i == 0 ) {
                currClass += ' ' + me.__getClass( 'item-first' );
            }
            if ( i == len - 1 ) {
                currClass += ' ' + me.__getClass( 'item-last' );
            }

            // 构建tab的样式与行为
            if ( i == me.activeIndex ) {
                currClass += ' ' + me.__getClass( 'item-active' );
            } else {
                clickHandler = ' onclick="'
                                + me.__getStrCall( '_select', i )
                                + '"';
            }

            // 构建tab项的html
            html.push(
                esui.util.format(
                    me._tplItem,
                    title,
                    currClass,
                    clickHandler,
                    closeHtml
                ) );
        }

		// 填充tab的html
        main.innerHTML = '
' + html.join('') + '
';
		me._resetPanel();
    },

    /**
     * 重置tab对应的panel的显示隐藏状态
     *
     * @private
     */
    _resetPanel: function () {
        var tabs        = this.tabs;
        var len         = tabs.length;
        var activeIndex = this.activeIndex;
        var i;
        var panel;

        for ( i = 0; i < len; i++ ) {
            panel = tabs[ i ].panel;
            if ( panel ) {
                baidu.g( panel ).style.display = (i == activeIndex ? '' : 'none');
            }
        }
    },

    onchange: new Function(),

    /**
     * 选择标签
     *
     * @private
     * @param {number} index 标签序号
     */
    _select: function ( index ) {
        if ( this.onchange( index, this.tabs[ index ] ) !== false ) {
            this.setActiveIndex( index );
        }
    },

    /**
     * 选择标签
     *
     * @public
     * @param {number} index 标签序号
     */
    setActiveIndex: function ( index ) {
        this.activeIndex = index;
        this._renderTabs();
    },

	onclose: new Function(),

    /**
     * 关闭标签
     *
     * @private
     * @param {number} index 标签序号
     */
    _close: function ( index ) {
        if ( this.onclose( index, this.tabs[ index ] ) !== false ) {
			this.remove( index );
        }
    },

    /**
     * 移除标签
     *
     * @private
     * @param {number} index 标签序号
     */
    remove: function ( index ) {
        var tabs = this.tabs;
        tabs.splice( index, 1 );

        // 重新设置activeIndex
        if ( this.activeIndex >= tabs.length ) {
            this.activeIndex--;
        }
        if ( this.activeIndex < 0 ) {
            this.activeIndex = 0;
        }

        this._renderTabs();
    },

    /**
     * 添加标签
     *
     * @public
     * @param {Object} tab 标签数据
     */
    add: function ( tab ) {
        tab = tab || { title: '新建标签' }
        this.tabs.push( tab );
        this._renderTabs();
    }
};

baidu.inherits( esui.Tab, esui.Control );
<|endoftext|>


<commit_before>/**
 * VclController
 *
 * @description :: Server-side logic for managing vcls
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */
var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var temp = require('temp');
var url = require('url');

function completeRun(err, fiddle, allRequests) {

  var completedData = { completedAt: new Date() };

  function writeCompletedData(err) {
    if (err instanceof Error) {
      completedData.error = err.message;
    } else if (err) {
      completedData.error = err.toString();
    }
    fs.writeFile(path.join(fiddle.path, 'completed'), JSON.stringify(completedData), { encoding: 'utf8' }, function (err) {
      if (err) sails.log.error(err);
    });
  }

  if (err) {
    sails.log.error('Run container error: ' + err);
    return writeCompletedData(err);
  }

  ContainerService.readOutputFiles(fiddle.path, function (err, output) {
    if (err) return writeCompletedData(err);
    if (output.runlog.length > 0) return writeCompletedData('Error: ' + output.runlog);

    var parsedNcsa = RequestMetadataService.parseVarnish4NCSA(output.varnishncsa);

    var results = RequestMetadataService.correlateResults(allRequests.includedRequests, output.responses, parsedNcsa, null);
    results = results.concat(allRequests.excludedRequests.map(function (r) { return { request: r }; }));

    completedData.log = output.varnishlog;
    completedData.results = results;
    writeCompletedData();
  });

}

module.exports = {
	index: function (req, res) {
    const defaultVcl = 'vcl 4.0; backend default { .host = "www.vclfiddle.net"; .port = "80"; }';

    var fiddleid = req.params.fiddleid || '';
    var runindex = req.params.runindex || '0';

    if (!fiddleid) {
      return res.view({
        fiddleid: '',
        vcl: defaultVcl,
        har: '',
        log: ''
      });
    }

    FiddlePersistenceService.getFiddleRun(fiddleid, runindex, function (err, fiddle) {

      if (err) return res.serverError(err);

      if (fiddle === null) return res.notFound();

      FiddlePersistenceService.loadViewState(fiddle, function (err, viewState) {
        if (err) return res.serverError(err);

        return res.view({
          fiddleid: fiddle.id,
          vcl: viewState.vcl,
          har: viewState.har,
          log: viewState.log,
          results: viewState.results
        })

      });

    });

  },

  result: function (req, res) {
    var fiddleid = req.query.fiddleid || '';
    var runindex = req.query.runindex || '0';
    sails.log(req.query);
    if (!fiddleid) return res.badRequest();

    FiddlePersistenceService.getFiddleRun(fiddleid, runindex, function (err, fiddle) {

      if (err) return res.serverError(err);

      if (fiddle === null) return res.notFound();

      ContainerService.getReplayResult(fiddle.path, function (err, completedData) {
        if (err) return res.serverError(err);

        if (!completedData.completedAt) {
          // not complete yet
          // TODO timeout if too long to complete
          // TODO instruct client to cache only briefly if at all
          return res.ok({});
        }

        FiddlePersistenceService.loadViewState(fiddle, function (err, viewState) {
          if (err) return res.serverError(err);

          viewState.log = completedData.error || completedData.log,
          viewState.results = completedData.results

          FiddlePersistenceService.saveViewState(fiddle, viewState, function (err) {
            if (err) return res.serverError(err);

            return res.ok({
              log: viewState.log,
              results: viewState.results
            });

          });
        });
      });

    });
  },

  run: function (req, res) {
    var fiddleid = req.body.fiddleid || '';
    var vcl = req.body.vcl;
    var rawRequests = req.body.har;

    if (typeof vcl !== 'string' || typeof rawRequests !== 'string') return res.badRequest();

    RequestMetadataService.parseInputRequests(rawRequests, function (err, _ignored, allRequests) {

      if (err) {
        return res.ok({
          fiddleid: fiddleid,
          vcl: vcl,
          har: rawRequests,
          log: err.toString()
        }, 'vcl/index');
      }

      if (allRequests.includedRequests.length == 0) {
        return res.ok({
          fiddleid: fiddleid,
          vcl: vcl,
          har: rawRequests,
          log: 'HAR does not contain any supported requests.'
        }, 'vcl/index');
      }

      if (!!req.body.dbl) {
        allRequests.includedRequests = allRequests.includedRequests.concat(allRequests.includedRequests);
      }

      FiddlePersistenceService.prepareFiddle(fiddleid, function (err, fiddle) {
        if (err) return res.serverError(err);

        // TODO persist state of 'replay requests twice' option

        ContainerService.beginReplay(fiddle.path, allRequests.includedRequests, vcl, function (err) {
          // started

          var viewState = {
            vcl: vcl,
            har: rawRequests
          };
          if (err) {
            viewState.log = 'Error: ' + err;
          }

          FiddlePersistenceService.saveViewState(fiddle, viewState, function (err) {
            if (err) return res.serverError(err);

            return res.ok({
              fiddleid: fiddle.id,
              runindex: fiddle.runIndex,
              vcl: viewState.vcl,
              har: viewState.har,
              log: viewState.log
            });

          });

        }, function (err) {
          // completed
          return completeRun(err, fiddle, allRequests);
        });

      });

    });

  }

};

<commit_msg>Remove noisy debug logging<commit_after>/**
 * VclController
 *
 * @description :: Server-side logic for managing vcls
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */
var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var temp = require('temp');
var url = require('url');

function completeRun(err, fiddle, allRequests) {

  var completedData = { completedAt: new Date() };

  function writeCompletedData(err) {
    if (err instanceof Error) {
      completedData.error = err.message;
    } else if (err) {
      completedData.error = err.toString();
    }
    fs.writeFile(path.join(fiddle.path, 'completed'), JSON.stringify(completedData), { encoding: 'utf8' }, function (err) {
      if (err) sails.log.error(err);
    });
  }

  if (err) {
    sails.log.error('Run container error: ' + err);
    return writeCompletedData(err);
  }

  ContainerService.readOutputFiles(fiddle.path, function (err, output) {
    if (err) return writeCompletedData(err);
    if (output.runlog.length > 0) return writeCompletedData('Error: ' + output.runlog);

    var parsedNcsa = RequestMetadataService.parseVarnish4NCSA(output.varnishncsa);

    var results = RequestMetadataService.correlateResults(allRequests.includedRequests, output.responses, parsedNcsa, null);
    results = results.concat(allRequests.excludedRequests.map(function (r) { return { request: r }; }));

    completedData.log = output.varnishlog;
    completedData.results = results;
    writeCompletedData();
  });

}

module.exports = {
	index: function (req, res) {
    const defaultVcl = 'vcl 4.0; backend default { .host = "www.vclfiddle.net"; .port = "80"; }';

    var fiddleid = req.params.fiddleid || '';
    var runindex = req.params.runindex || '0';

    if (!fiddleid) {
      return res.view({
        fiddleid: '',
        vcl: defaultVcl,
        har: '',
        log: ''
      });
    }

    FiddlePersistenceService.getFiddleRun(fiddleid, runindex, function (err, fiddle) {

      if (err) return res.serverError(err);

      if (fiddle === null) return res.notFound();

      FiddlePersistenceService.loadViewState(fiddle, function (err, viewState) {
        if (err) return res.serverError(err);

        return res.view({
          fiddleid: fiddle.id,
          vcl: viewState.vcl,
          har: viewState.har,
          log: viewState.log,
          results: viewState.results
        })

      });

    });

  },

  result: function (req, res) {
    var fiddleid = req.query.fiddleid || '';
    var runindex = req.query.runindex || '0';
    if (!fiddleid) return res.badRequest();

    FiddlePersistenceService.getFiddleRun(fiddleid, runindex, function (err, fiddle) {

      if (err) return res.serverError(err);

      if (fiddle === null) return res.notFound();

      ContainerService.getReplayResult(fiddle.path, function (err, completedData) {
        if (err) return res.serverError(err);

        if (!completedData.completedAt) {
          // not complete yet
          // TODO timeout if too long to complete
          // TODO instruct client to cache only briefly if at all
          return res.ok({});
        }

        FiddlePersistenceService.loadViewState(fiddle, function (err, viewState) {
          if (err) return res.serverError(err);

          viewState.log = completedData.error || completedData.log,
          viewState.results = completedData.results

          FiddlePersistenceService.saveViewState(fiddle, viewState, function (err) {
            if (err) return res.serverError(err);

            return res.ok({
              log: viewState.log,
              results: viewState.results
            });

          });
        });
      });

    });
  },

  run: function (req, res) {
    var fiddleid = req.body.fiddleid || '';
    var vcl = req.body.vcl;
    var rawRequests = req.body.har;

    if (typeof vcl !== 'string' || typeof rawRequests !== 'string') return res.badRequest();

    RequestMetadataService.parseInputRequests(rawRequests, function (err, _ignored, allRequests) {

      if (err) {
        return res.ok({
          fiddleid: fiddleid,
          vcl: vcl,
          har: rawRequests,
          log: err.toString()
        }, 'vcl/index');
      }

      if (allRequests.includedRequests.length == 0) {
        return res.ok({
          fiddleid: fiddleid,
          vcl: vcl,
          har: rawRequests,
          log: 'HAR does not contain any supported requests.'
        }, 'vcl/index');
      }

      if (!!req.body.dbl) {
        allRequests.includedRequests = allRequests.includedRequests.concat(allRequests.includedRequests);
      }

      FiddlePersistenceService.prepareFiddle(fiddleid, function (err, fiddle) {
        if (err) return res.serverError(err);

        // TODO persist state of 'replay requests twice' option

        ContainerService.beginReplay(fiddle.path, allRequests.includedRequests, vcl, function (err) {
          // started

          var viewState = {
            vcl: vcl,
            har: rawRequests
          };
          if (err) {
            viewState.log = 'Error: ' + err;
          }

          FiddlePersistenceService.saveViewState(fiddle, viewState, function (err) {
            if (err) return res.serverError(err);

            return res.ok({
              fiddleid: fiddle.id,
              runindex: fiddle.runIndex,
              vcl: viewState.vcl,
              har: viewState.har,
              log: viewState.log
            });

          });

        }, function (err) {
          // completed
          return completeRun(err, fiddle, allRequests);
        });

      });

    });

  }

};

<|endoftext|>


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

    window.graphView = {
    	el: '.graph',
        div: null,
        d: {
            w: $(window).height()*.66,
            h: $(window).height()*.66,
            rx: $(window).height()*.33,
            ry: $(window).height()*.33
        },
        d3: null,
        lastEvent: [],
        bundle: null,
        cluster: null,
        line: null,
        svg: null,
        timer: null,
        colors:[ '#210050','#46007a','#7f00de','#018b88','#01b8b1','#01ead6','#1667af','#01aeff','#6ed1ff','#73115b','#af007c','#d03593' ],
        fakeData: 'javascripts/data/dummy.json',

        init: function(data){
            var _this = this;

            console.log('started graphview')

            _this.setupGraph();
            _this.parseToD3(data);
            _this.render();

        },

        render: function(){

        },

        update: function(eventData) {
            var _this = this;
            var d = this.parseEventData(eventData);
            console.log(d);

            _.each(d,function(val,key){
                var select = _this.svg.selectAll("path.link.source-" + val)
                  .each(_this.updateNodes("source", true))
                  .transition()
                    .style("opacity", 1)
                    .style('stroke-width','5px')
                    .duration(400);

                select.transition()
                    .style("opacity", .4)
                    .style('stroke-width','2px')
                    .duration(400)
                    .delay(400);
              })
        },

        setupGraph: function() {
            var _this = this;

            // Setup the data clustering
            this.cluster = d3.layout.cluster()
                .size([360, this.d.ry-120]) // Degrees, radius
                .sort( function(a,b){
                    return d3.ascending(a.key, b.key);
                });

            // Setup the layout bundle
            this.bundle = d3.layout.bundle();

            // Setup radial line generator
            this.line = d3.svg.line.radial()
                .interpolate('bundle')
                .tension(.85)
                .radius( function(d) {
                    return d.y;
                    })
                .angle( function(d) {
                    return d.x/180 * Math.PI;
                    });

            // Generates the containing div
            this.div = d3.select('.graph')
                .insert("div", "h2")
                .style("width", "100%")//this.d.w + "px")
                .style("height", this.d.h + "px")
                .style("position", "absolute")
                .style("left","0px")
                .style("-webkit-backface-visibility", "hidden");

            // Binds svg to the containing div
            this.svg = this.div.append('svg:svg')
                .style('margin','0 auto')
                .attr('width',this.d.w)
                .attr('height',this.d.h)
                .append('svg:g')
                .attr('transform','translate(' + this.d.rx + ',' + this.d.ry + ')' );

            // Line generator
            this.line = d3.svg.line.radial()
                .interpolate("bundle")
                .tension(.85)
                .radius(function(d) { return d.y; })
                .angle(function(d) { return d.x / 180 * Math.PI; });

        },

        parseToD3: function(collectionData) {
            var _this = this;
            // d3.json(collectionData, function(data) {

                // Main elements
            var d = _this.mapToNodes(collectionData),
                nodes = _this.cluster.nodes(_this.mapHierarchy(d)),
                links = _this.getConnections(nodes),
                splines = _this.bundle(links);

            console.log('mappedData',d);
            console.log('nodes',nodes);
            console.log('links',links);
            console.log('splines',splines);

            _this.drawCategory(nodes);

             // Establish paths from links
            var path = _this.svg.selectAll("path.link")
                .data(links)
                .enter().append("svg:path")
                .attr("class", function(d) { return "link name-" + d.source.name.split('.')[1] + " source-" + d.source.name.split('.')[1] + " target-" + d.target.name.split('.')[1]; })
                .attr("d", function(d, i) { return _this.line(splines[i]); });

                // Establish nodes for text display
            _this.svg.selectAll("g.node")
                  .data(nodes.filter(function(d) { return !d.children; }))
                .enter().append("svg:g")
                  .attr("class", "node")
                  .attr("id", function(d) { return "node-" + d.key; })
                  .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
                .append("svg:text")
                  .attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
                  .attr("dy", ".31em")
                  .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
                  .attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
                  .text(function(d) { return d.name.split('participant.')[1]; })
                  .on("mouseover", _this.mouseon)
                  .on("mouseout", _this.mouseoff);
          // });
        },

        drawCategory: function(nodes) {
            var _this = this;

            // Arc Generator
            var groupArc = d3.svg.arc()
                .innerRadius(this.d.ry-120)
                .outerRadius(this.d.ry-160)
                .startAngle(function(d){ var r=_this.getAngles(d); return r.min; })
                .endAngle(function(d){ var r=_this.getAngles(d); return r.max; });

          this.svg.selectAll("g.arc")
            .data(nodes.filter(function(d){
                return d.name !== 'participant';
            }))
            .enter().append("svg:path")
            .attr("d", groupArc)
            .attr("class", function(d) {
                return "groupArc " + d.name.split('.')[1];
            })
            .style("fill", function(d) {
                return _this.colors[Math.floor(Math.random() * _this.colors.length)]
            })
            .style("fill-opacity", 0.5)
            .on("mouseover", _this.mouseon)
            .on("mouseout", _this.mouseoff);

        },

        getAngles: function(data) {
            var min,max = 0;
            // console.log('getAnglesInput',data.x)
            if(!data.children) {
                console.log("Noe kids")
                return { min: 0, max:0 };
            }
            min = max = data.children[0].x;
           data.children.forEach(function(d){
               if(d.x < min) min = d.x;
               if(d.x > max) max = d.x;
           });
            var pi = Math.PI;
            // console.log('minmax return',{ min: min-2 * (pi/180), max: max+2 * (pi/180)});
            return { min: (min-10) * (pi/180), max: (max+10) * (pi/180)};
        },

        // For sanity
        mapHierarchy: function(data) {
            var map = {};

            function find(name, data) {
                var node = map[name], i;
                if (!node) {
                  node = map[name] = data || {name: name, children: []};
                  if (name.length) {
                    node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
                    node.parent.children.push(node);
                    node.key = name.substring(i + 1);
                  }
                }
                // console.log(node)
                return node;
            }

            _.each( data, function(d, i) {
                find(d.name, d);
            });

            // console.log('classes',classes)

            return map[""];
        },

        // For connectyness
        getConnections: function(nodes) {
            var map = {},
              imports = [];

            // Compute a map from name to node.
            nodes.forEach(function(d) {
                map[d.name] = d;
            });

            // For each import, construct a link from the source to target node.
            nodes.forEach(function(d) {
                if (d.imports) d.imports.forEach(function(i) {
                  imports.push({source: map[d.name], target: map[i]});
                });
            });
            console.log(imports)
            return imports;
        },

        mapToNodes: function(data) {
            var map=[];
            var _this = this;
            console.log('debugdata',data)
            _.each(data.players, function(val,iter) {
                var i={}, o={};
                i.name = 'participant.' + val.spalla_id + '.hears';
                i.size = Math.random() * 5000;
                i.color = _this.colors[Math.floor(Math.random() * _this.colors.length)]
                i.imports = [];
                _.each(val.hears, function(dat,jter) {
                    i.imports.push('participant.' + dat.split('sentFrom')[1] + '.plays');
                });

                o.name = 'participant.' + val.spalla_id + '.plays';
                o.color = _this.colors[Math.floor(Math.random() * _this.colors.length)]
                o.size = Math.random() * 5000;
                o.imports = [];
                // Commented this out, causes Lemmas that talk to eachother
                // _.each(val.plays, function(dat,jter) {
                //     o.imports.push('participant.' + dat.split('sentFrom')[1] + '.in');
                // });

                map.push(i);
                map.push(o);
            })
            return map;
        },

        parseEventData: function(data) {
            var map=[];
            var _this = this;

            _.each(data, function(val,key) {
                _this.lastEvent[key] = _this.lastEvent[key] || {};
                console.log(_this.lastEvent[key].timestamp,val.timestamp)
                if(_this.lastEvent[key].timestamp !== val.timestamp) {
                    map.push(key.split('sentFrom')[1]);
                    _this.lastEvent[key].timestamp = val.timestamp;
                }
            });
            console.log(map)
            return map;
        },

        mouseon: function(d) {
            var _this = this;
            //console.log('mouseon',d.name)
            _this.svg.selectAll("path.link.target-" + d.name.split('.')[1])
              .classed("target", true)
              .each(_this.updateNodes("source", true));

            _this.svg.selectAll("path.link.source-" + d.name.split('.')[1])
              .classed("source", true)
              .each(_this.updateNodes("target", true));

          // _this.svg.selectAll("groupArc." + d.__data__.name.split('.')[1])
          this.select('path')
              .classed("target", true)
              .attr("fill-opacity",1);
            },

        mouseoff:function(d) {
            var _this = this;
            _this.svg.selectAll("path.link.source-" + d.name.split('.')[1])
              .classed("source", false)
              .each(_this.updateNodes("target", false));

            _this.svg.selectAll("path.link.target-" + d.name.split('.')[1])
              .classed("target", false)
              .each(_this.updateNodes("source", false));
        },

        updateNodes: function(name, value) {
          var _this = this;
          return function(d) {
            //console.log('updateNotdes',this)
            if (value) this.parentNode.appendChild(this);
            _this.svg.select("#node-" + d[name].key).classed(name, value);
          };
        }

    };

})();
<commit_msg>Fixed undefined category bug<commit_after>(function () {
    'use strict';

    window.graphView = {
    	el: '.graph',
        div: null,
        d: {
            w: $(window).height()*.66,
            h: $(window).height()*.66,
            rx: $(window).height()*.33,
            ry: $(window).height()*.33
        },
        d3: null,
        lastEvent: [],
        bundle: null,
        cluster: null,
        line: null,
        svg: null,
        timer: null,
        colors:[ '#210050','#46007a','#7f00de','#018b88','#01b8b1','#01ead6','#1667af','#01aeff','#6ed1ff','#73115b','#af007c','#d03593' ],
        fakeData: 'javascripts/data/dummy.json',

        init: function(data){
            var _this = this;

            console.log('started graphview')

            _this.setupGraph();
            _this.parseToD3(data);
            _this.render();

        },

        render: function(){

        },

        update: function(eventData) {
            var _this = this;
            var d = this.parseEventData(eventData);
            console.log(d);

            _.each(d,function(val,key){
                var select = _this.svg.selectAll("path.link.source-" + val)
                  .each(_this.updateNodes("source", true))
                  .transition()
                    .style("opacity", 1)
                    .style('stroke-width','5px')
                    .duration(400);

                select.transition()
                    .style("opacity", .4)
                    .style('stroke-width','2px')
                    .duration(400)
                    .delay(400);
              })
        },

        setupGraph: function() {
            var _this = this;

            // Setup the data clustering
            this.cluster = d3.layout.cluster()
                .size([360, this.d.ry-120]) // Degrees, radius
                .sort( function(a,b){
                    return d3.ascending(a.key, b.key);
                });

            // Setup the layout bundle
            this.bundle = d3.layout.bundle();

            // Setup radial line generator
            this.line = d3.svg.line.radial()
                .interpolate('bundle')
                .tension(.85)
                .radius( function(d) {
                    return d.y;
                    })
                .angle( function(d) {
                    return d.x/180 * Math.PI;
                    });

            // Generates the containing div
            this.div = d3.select('.graph')
                .insert("div", "h2")
                .style("width", "100%")//this.d.w + "px")
                .style("height", this.d.h + "px")
                .style("position", "absolute")
                .style("left","0px")
                .style("-webkit-backface-visibility", "hidden");

            // Binds svg to the containing div
            this.svg = this.div.append('svg:svg')
                .style('margin','0 auto')
                .attr('width',this.d.w)
                .attr('height',this.d.h)
                .append('svg:g')
                .attr('transform','translate(' + this.d.rx + ',' + this.d.ry + ')' );

            // Line generator
            this.line = d3.svg.line.radial()
                .interpolate("bundle")
                .tension(.85)
                .radius(function(d) { return d.y; })
                .angle(function(d) { return d.x / 180 * Math.PI; });

        },

        parseToD3: function(collectionData) {
            var _this = this;
            // d3.json(collectionData, function(data) {

                // Main elements
            var d = _this.mapToNodes(collectionData),
                nodes = _this.cluster.nodes(_this.mapHierarchy(d)),
                links = _this.getConnections(nodes),
                splines = _this.bundle(links);

            console.log('mappedData',d);
            console.log('nodes',nodes);
            console.log('links',links);
            console.log('splines',splines);

            _this.drawCategory(nodes);

             // Establish paths from links
            var path = _this.svg.selectAll("path.link")
                .data(links)
                .enter().append("svg:path")
                .attr("class", function(d) { return "link name-" + d.source.name.split('.')[1] + " source-" + d.source.name.split('.')[1] + " target-" + d.target.name.split('.')[1]; })
                .attr("d", function(d, i) { return _this.line(splines[i]); });

                // Establish nodes for text display
            _this.svg.selectAll("g.node")
                  .data(nodes.filter(function(d) { return !d.children; }))
                .enter().append("svg:g")
                  .attr("class", "node")
                  .attr("id", function(d) { return "node-" + d.key; })
                  .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
                .append("svg:text")
                  .attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
                  .attr("dy", ".31em")
                  .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
                  .attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
                  .text(function(d) { return d.name.split('participant.')[1]; })
                  .on("mouseover", _this.mouseon)
                  .on("mouseout", _this.mouseoff);
          // });
        },

        drawCategory: function(nodes) {
            var _this = this;

            // Arc Generator
            var groupArc = d3.svg.arc()
                .innerRadius(this.d.ry-120)
                .outerRadius(this.d.ry-160)
                .startAngle(function(d){ var r=_this.getAngles(d); return r.min; })
                .endAngle(function(d){ var r=_this.getAngles(d); return r.max; });

          this.svg.selectAll("g.arc")
            .data(nodes.filter(function(d){
                return d.name !== 'participant' && d.name;
            }))
            .enter().append("svg:path")
            .attr("d", groupArc)
            .attr("class", function(d) {
                return "groupArc " + d.name.split('.')[1];
            })
            .style("fill", function(d) {
                return _this.colors[Math.floor(Math.random() * _this.colors.length)]
            })
            .style("fill-opacity", 0.5)
            .on("mouseover", _this.mouseon)
            .on("mouseout", _this.mouseoff);

        },

        getAngles: function(data) {
            var min,max = 0;
            // console.log('getAnglesInput',data.x)
            if(!data.children) {
                console.log("Noe kids")
                return { min: 0, max:0 };
            }
            min = max = data.children[0].x;
           data.children.forEach(function(d){
               if(d.x < min) min = d.x;
               if(d.x > max) max = d.x;
           });
            var pi = Math.PI;
            // console.log('minmax return',{ min: min-2 * (pi/180), max: max+2 * (pi/180)});
            return { min: (min-10) * (pi/180), max: (max+10) * (pi/180)};
        },

        // For sanity
        mapHierarchy: function(data) {
            var map = {};

            function find(name, data) {
                var node = map[name], i;
                if (!node) {
                  node = map[name] = data || {name: name, children: []};
                  if (name.length) {
                    node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
                    node.parent.children.push(node);
                    node.key = name.substring(i + 1);
                  }
                }
                // console.log(node)
                return node;
            }

            _.each( data, function(d, i) {
                find(d.name, d);
            });

            // console.log('classes',classes)

            return map[""];
        },

        // For connectyness
        getConnections: function(nodes) {
            var map = {},
              imports = [];

            // Compute a map from name to node.
            nodes.forEach(function(d) {
                map[d.name] = d;
            });

            // For each import, construct a link from the source to target node.
            nodes.forEach(function(d) {
                if (d.imports) d.imports.forEach(function(i) {
                  imports.push({source: map[d.name], target: map[i]});
                });
            });
            console.log(imports)
            return imports;
        },

        mapToNodes: function(data) {
            var map=[];
            var _this = this;
            console.log('debugdata',data)
            _.each(data.players, function(val,iter) {
                var i={}, o={};
                i.name = 'participant.' + val.spalla_id + '.hears';
                i.size = Math.random() * 5000;
                i.color = _this.colors[Math.floor(Math.random() * _this.colors.length)]
                i.imports = [];
                _.each(val.hears, function(dat,jter) {
                    i.imports.push('participant.' + dat.split('sentFrom')[1] + '.plays');
                });

                o.name = 'participant.' + val.spalla_id + '.plays';
                o.color = _this.colors[Math.floor(Math.random() * _this.colors.length)]
                o.size = Math.random() * 5000;
                o.imports = [];
                // Commented this out, causes Lemmas that talk to eachother
                 _.each(val.plays, function(dat,jter) {
                     o.imports.push('participant.' + dat.split('sentFrom')[1] + '.hears');
                 });

                map.push(i);
                map.push(o);
            })
            return map;
        },

        parseEventData: function(data) {
            var map=[];
            var _this = this;

            _.each(data, function(val,key) {
                _this.lastEvent[key] = _this.lastEvent[key] || {};
                console.log(_this.lastEvent[key].timestamp,val.timestamp)
                if(_this.lastEvent[key].timestamp !== val.timestamp) {
                    map.push(key.split('sentFrom')[1]);
                    _this.lastEvent[key].timestamp = val.timestamp;
                }
            });
            console.log(map)
            return map;
        },

        mouseon: function(d) {
            var _this = this;
            //console.log('mouseon',d.name)
            _this.svg.selectAll("path.link.target-" + d.name.split('.')[1])
              .classed("target", true)
              .each(_this.updateNodes("source", true));

            _this.svg.selectAll("path.link.source-" + d.name.split('.')[1])
              .classed("source", true)
              .each(_this.updateNodes("target", true));

          // _this.svg.selectAll("groupArc." + d.__data__.name.split('.')[1])
          this.select('path')
              .classed("target", true)
              .attr("fill-opacity",1);
            },

        mouseoff:function(d) {
            var _this = this;
            _this.svg.selectAll("path.link.source-" + d.name.split('.')[1])
              .classed("source", false)
              .each(_this.updateNodes("target", false));

            _this.svg.selectAll("path.link.target-" + d.name.split('.')[1])
              .classed("target", false)
              .each(_this.updateNodes("source", false));
        },

        updateNodes: function(name, value) {
          var _this = this;
          return function(d) {
            //console.log('updateNotdes',this)
            if (value) this.parentNode.appendChild(this);
            _this.svg.select("#node-" + d[name].key).classed(name, value);
          };
        }

    };

})();
<|endoftext|>


<commit_before>$(function(){
  gorgon.image_size = 'small';

  timelineAllLoad();

  $('.basic-mode').remove();
  $('.timeline-mode').show();

  if ((navigator.userAgent.indexOf('iPhone') > 0 && navigator.userAgent.indexOf('iPad') == -1) || navigator.userAgent.indexOf('iPod') > 0) {
    $('#timeline-upload-photo-button').hide();
    $('#timeline-public-flag').css('margin', '0 0 0 0');
  }

  $('#timeline_postform_submit').click( function() {
    $('#timeline-submit-error').hide();
    $('.photo-info').hide();
    $('#timeline_postform_submit').attr('disabled', 'disabled');

    var body = $.trim($('#tosaka_postform_body').val());
    body = body.replace(/"/g, '"')

    var faceName = $('.face-name').text();
    var faceImg = $('#face').children('.span2').children('img').attr('src');
    var flashTimelineDom =
          '
'
          + '
'
            + ''
          + '
'
          + '
'
            + '
'
              + '' + faceName + ''
              + '
1分前
'
            + '
'
            + '
' + body + '
'
            + ''
              + ''
            + ''
          + '
'
          + '
'
            + ''
          + '
'
        + '
';

    if (0 < jQuery.trim(body).length)
    {
      $('#timeline-list').prepend(flashTimelineDom);
    }

    if (gorgon)
    {
      var publicFlag = 1;
      if ('community' != gorgon.target)
      {
        publicFlag = $('#timeline-public-flag option:selected').val()
      }

      var data = {
        body: body,
        target: gorgon.post.foreign,
        target_id: gorgon.post.foreignId,
        apiKey: openpne.apiKey,
        public_flag: publicFlag
      };
    }
    else
    {
      var data = {
        body: body,
        apiKey: openpne.apiKey
      };
    }
    tweetByData(data);
  });

  $('#timeline-upload-photo-button').click(function() {
    $('#timeline-submit-upload').click();
    $('#photo-remove').show();
  });

  $('#timeline-submit-upload').change(function() {

      $('#timeline-submit-error').hide();
      $('#timeline-submit-error').text('');

      var size = this.files[0].size;

      if (size >= fileMaxSizeInfo['size']) {
        $('#timeline-submit-error').show();

        var errorMessage = 'ファイルは' + fileMaxSizeInfo['format'] + '以上はアップロードできません';
        $('#timeline-submit-error').text(errorMessage);

        $('#timeline-submit-upload').val('');
        $('#photo-file-name').text('');

      }

  });

  $('#gorgon-loadmore').click( function() {
    $('#timeline-list-loader').show();
    $('#gorgon-loadmore').hide();
    timelineLoadmore();
    $('#timeline-list-loader').hide();
    $('#gorgon-loadmore').show();
  });

  $(document).on('click', '.timeline-comment-loadmore', function() {
    var timelineId = $(this).attr('data-timeline-id');
    var commentlist = $('#commentlist-' + timelineId);
    var commentLength = commentlist.children('.timeline-post-comment').length;
    $('#timeline-comment-loader-' + timelineId).show();

    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/commentSearch.json?apiKey=' + openpne.apiKey,
      data: {
        'timeline_id': timelineId,
        'count': commentLength + 20,
      },
      success: function(json){
        commentlist.children('.timeline-post-comment').remove();
        $('#timelineCommentTemplate').tmpl(json.data.reverse()).prependTo('#commentlist-' + timelineId);
        $('#timeline-comment-loader-' + timelineId).hide();

        if (json.data.length < commentLength + 20)
        {
          $('#timeline-comment-loadmore-' + timelineId).hide();
        }
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#commentlist-' + timelineId).hide();
      }
    });
  });

  $('#tosaka_postform_body').keyup( function() {
    lengthCheck($(this), $('#timeline_postform_submit'));
  });

  $(document).on('keyup', '.timeline-post-comment-form-input', function() {
    lengthCheck($(this), $('button[data-timeline-id=' + $(this).attr('data-timeline-id') + ']'));
  });

  $('#timeline-submit-upload').change(function() {
    var fileName = $('#timeline-submit-upload').val();
    var dom = $('#photo-file-name');
    if (20 > fileName.length)
    {
      dom.text(fileName);
    }
    else
    {
      dom.text(fileName.substring(0, 19) + '…');
    }
    $('.photo-info').show();
  });

  $('#photo-remove').click( function() {
    $('#timeline-submit-upload').val('');
    $('#photo-file-name').text('');
    $(this).hide();
  });
});

function timelineAllLoad() {
  if (gorgon)
  {
    gorgon.apiKey = openpne.apiKey;
    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/search.json',
      data: gorgon,
      success: function (json){
        renderJSON(json, 'all');
        $('#timeline-list-loader').hide();
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#timeline-list-loader').hide();
        $('#timeline-list').text('タイムラインは投稿されていません。');
        $('#timeline-list').show();
        $('.flashTimelineDom').remove();
      }
    });

  }
  else
  {
    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/search.json?apiKey=' + openpne.apiKey,
      success: function (json){
        renderJSON(json, 'all');
        $('#timeline-list-loader').hide();
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#timeline-list-loader').hide();
        $('#timeline-list').text('タイムラインは投稿されていません。');
        $('#timeline-list').show();
        $('.flashTimelineDom').remove();
      }
    });
  }
}

function timelineLoadmore() {
  var loadmoreId = $('#timeline-list').attr('data-loadmore-id');
  loadmoreId = loadmoreId - 1;
  if (gorgon)
  {
    gorgon.apiKey = openpne.apiKey;
  }
  else
  {
    gorgon = {apiKey: openpne.apiKey,}
  }
  gorgon.max_id = loadmoreId;

  $.ajax({
    type: 'GET',
    url: openpne.apiBase + 'activity/search.json',
    data: gorgon,
    success: function(json){
      renderJSON(json, 'more');
      $('#timeline-loadmore-loading').hide();
    },
    error: function(XMLHttpRequest, textStatus, errorThrown){
      $('#timeline-loadmore-loading').hide();
    }
  });
}

function renderJSON(json, mode) {
  if (undefined == mode)
  {
    mode = 'all';
  }
  if ('all' == mode)
  {
    $('#timeline-list').empty();
  }
  if(json.data && 0 < viewPhoto)
  {
    for(i=0;i<json.data.length;i++)
    {
      if (!json.data[i].body_html.match(/img.*src=/))
      {
        if (json.data[i].body.match(/\.(jpg|jpeg|bmg|png|gif)/gi))
        {
          json.data[i].body_html = json.data[i].body.replace(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+.(jpg|jpeg|bmg|png|gif))/gi, '

');
        }
        else if (json.data[i].body.match(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+)/gi))
        {
          json.data[i].body_html = json.data[i].body.replace(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+)/gi, '

$1
');
        }
      }
      json.data[i].body_html = json.data[i].body_html.replace(/<br \/>/g, '
');
    }
  }

  $timelineData = $('#timelineTemplate').tmpl(json.data);
  $('.timeline-comment-button', $timelineData).timelineComment();
  $('.timeline-comment-link', $timelineData).click(function(){
    $commentBoxArea = $(this).parent().siblings().find('.timeline-post-comment-form');
    $commentBoxArea.show();
    $commentBoxArea.children('.timeline-post-comment-form-input').focus();
  });
  if ('diff' == mode)
  {
    $timelineData.prependTo('#timeline-list');
  }
  else
  {
    $timelineData.appendTo('#timeline-list');
  }
  if ('all' == mode || 'diff' == mode)
  {
    if(json.data[0])
    {
      $('#timeline-list').attr('data-last-id', json.data[0].id);
    }
  }
  if ('all' == mode || 'more' == mode)
  {
    var max = json.data.length - 1;
    if (json.data[max])
    {
      $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
    }
  }
  if(json.data)
  {
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].replies)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].replies.reverse()).prependTo('#commentlist-' +json.data[i].id);
        $('#timeline-post-comment-form-' + json.data[i].id, $timelineData).show();
      }
      if(10 < parseInt(json.data[i].replies_count))
      {
        $('#timeline-comment-loadmore-' + json.data[i].id).show();
      }
    }
  }
  if ('all' == mode)
  {
    $('#timeline-loading').hide();
  }
  if ('more' == mode)
  {
    $('#timeline-loadmore').show();
    $('#timeline-loadmore-loading').hide();
  }
  $('.timeago').timeago();
}

function tweetByData(data)
{
  //reference　http://lagoscript.org/jquery/upload/documentation
  $('#timeline-submit-upload').upload(
    openpne.apiBase + 'activity/post.json', data,
    function (res) {
      var resCheck = responceCheck(res);
      if (false !== resCheck)
      {
        $('#timeline-submit-error').text(resCheck);
        $('#timeline-submit-error').show();
        $('.flashTimelineDom').remove();
        return;
      }

      res = res.replace(/,\"body.*\"/g, '');
      returnData = JSON.parse(res);

      if (returnData.status === "error") {

        var errorMessages = {
          file_size: 'ファイルサイズは' + fileMaxSizeInfo['format'] + 'までです',
          upload: 'アップロードに失敗しました',
          not_image: '画像をアップロードしてください',
          tweet: '投稿に失敗しました'
        };

        var errorType = returnData.type;

        $('#timeline-submit-error').text(errorMessages[errorType]);
        $('#timeline-submit-error').show();
        $('.flashTimelineDom').remove();

      } else {
        $(".postform").toggle();
        timelineAllLoad();
      }

      $('#timeline-submit-upload').val('');
      $('#tosaka_postform_body').val('');
      $('#timeline-submit-loader').hide();
      $('#counter').text(MAXLENGTH);

    },
    'text'
    );
}

function lengthCheck(obj, target)
{
  var objLength = obj.val().length;
  if (obj.val().match(/\n/gm))
  {
    objLength = objLength + obj.val().match(/\n/gm).length;
  }

  if (0 < objLength && MAXLENGTH >= objLength)
  {
    target.removeAttr('disabled');
  }
  else
  {
    target.attr('disabled', 'disabled');
  }
}

function responceCheck(res)
{
  if (0 <= res.indexOf('\<pre'))
  {
    return 'エラーが発生しました。再度読み込んで下さい。';
  }
  return false;
}
<commit_msg>(refs #1138) 投稿後追加するDOMをエスケープした<commit_after>$(function(){
  gorgon.image_size = 'small';

  timelineAllLoad();

  $('.basic-mode').remove();
  $('.timeline-mode').show();

  if ((navigator.userAgent.indexOf('iPhone') > 0 && navigator.userAgent.indexOf('iPad') == -1) || navigator.userAgent.indexOf('iPod') > 0) {
    $('#timeline-upload-photo-button').hide();
    $('#timeline-public-flag').css('margin', '0 0 0 0');
  }

  $('#timeline_postform_submit').click( function() {
    $('#timeline-submit-error').hide();
    $('.photo-info').hide();
    $('#timeline_postform_submit').attr('disabled', 'disabled');

    var body = $.trim($('#tosaka_postform_body').val());
    body = body.replace(/"/g, '"')
    var a = $('
');
    body = a.text(body).html();

    var faceName = $('.face-name').text();
    var faceImg = $('#face').children('.span2').children('img').attr('src');
    var flashTimelineDom =
          '
'
          + '
'
            + ''
          + '
'
          + '
'
            + '
'
              + '' + faceName + ''
              + '
1分前
'
            + '
'
            + '
' + body + '
'
            + ''
              + ''
            + ''
          + '
'
          + '
'
            + ''
          + '
'
        + '
';

    if (0 < jQuery.trim(body).length)
    {
      $('#timeline-list').prepend(flashTimelineDom);
    }

    if (gorgon)
    {
      var publicFlag = 1;
      if ('community' != gorgon.target)
      {
        publicFlag = $('#timeline-public-flag option:selected').val()
      }

      var data = {
        body: body,
        target: gorgon.post.foreign,
        target_id: gorgon.post.foreignId,
        apiKey: openpne.apiKey,
        public_flag: publicFlag
      };
    }
    else
    {
      var data = {
        body: body,
        apiKey: openpne.apiKey
      };
    }
    tweetByData(data);
  });

  $('#timeline-upload-photo-button').click(function() {
    $('#timeline-submit-upload').click();
    $('#photo-remove').show();
  });

  $('#timeline-submit-upload').change(function() {

      $('#timeline-submit-error').hide();
      $('#timeline-submit-error').text('');

      var size = this.files[0].size;

      if (size >= fileMaxSizeInfo['size']) {
        $('#timeline-submit-error').show();

        var errorMessage = 'ファイルは' + fileMaxSizeInfo['format'] + '以上はアップロードできません';
        $('#timeline-submit-error').text(errorMessage);

        $('#timeline-submit-upload').val('');
        $('#photo-file-name').text('');

      }

  });

  $('#gorgon-loadmore').click( function() {
    $('#timeline-list-loader').show();
    $('#gorgon-loadmore').hide();
    timelineLoadmore();
    $('#timeline-list-loader').hide();
    $('#gorgon-loadmore').show();
  });

  $(document).on('click', '.timeline-comment-loadmore', function() {
    var timelineId = $(this).attr('data-timeline-id');
    var commentlist = $('#commentlist-' + timelineId);
    var commentLength = commentlist.children('.timeline-post-comment').length;
    $('#timeline-comment-loader-' + timelineId).show();

    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/commentSearch.json?apiKey=' + openpne.apiKey,
      data: {
        'timeline_id': timelineId,
        'count': commentLength + 20,
      },
      success: function(json){
        commentlist.children('.timeline-post-comment').remove();
        $('#timelineCommentTemplate').tmpl(json.data.reverse()).prependTo('#commentlist-' + timelineId);
        $('#timeline-comment-loader-' + timelineId).hide();

        if (json.data.length < commentLength + 20)
        {
          $('#timeline-comment-loadmore-' + timelineId).hide();
        }
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#commentlist-' + timelineId).hide();
      }
    });
  });

  $('#tosaka_postform_body').keyup( function() {
    lengthCheck($(this), $('#timeline_postform_submit'));
  });

  $(document).on('keyup', '.timeline-post-comment-form-input', function() {
    lengthCheck($(this), $('button[data-timeline-id=' + $(this).attr('data-timeline-id') + ']'));
  });

  $('#timeline-submit-upload').change(function() {
    var fileName = $('#timeline-submit-upload').val();
    var dom = $('#photo-file-name');
    if (20 > fileName.length)
    {
      dom.text(fileName);
    }
    else
    {
      dom.text(fileName.substring(0, 19) + '…');
    }
    $('.photo-info').show();
  });

  $('#photo-remove').click( function() {
    $('#timeline-submit-upload').val('');
    $('#photo-file-name').text('');
    $(this).hide();
  });
});

function timelineAllLoad() {
  if (gorgon)
  {
    gorgon.apiKey = openpne.apiKey;
    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/search.json',
      data: gorgon,
      success: function (json){
        renderJSON(json, 'all');
        $('#timeline-list-loader').hide();
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#timeline-list-loader').hide();
        $('#timeline-list').text('タイムラインは投稿されていません。');
        $('#timeline-list').show();
        $('.flashTimelineDom').remove();
      }
    });

  }
  else
  {
    $.ajax({
      type: 'GET',
      url: openpne.apiBase + 'activity/search.json?apiKey=' + openpne.apiKey,
      success: function (json){
        renderJSON(json, 'all');
        $('#timeline-list-loader').hide();
      },
      error: function(XMLHttpRequest, textStatus, errorThrown){
        $('#timeline-list-loader').hide();
        $('#timeline-list').text('タイムラインは投稿されていません。');
        $('#timeline-list').show();
        $('.flashTimelineDom').remove();
      }
    });
  }
}

function timelineLoadmore() {
  var loadmoreId = $('#timeline-list').attr('data-loadmore-id');
  loadmoreId = loadmoreId - 1;
  if (gorgon)
  {
    gorgon.apiKey = openpne.apiKey;
  }
  else
  {
    gorgon = {apiKey: openpne.apiKey,}
  }
  gorgon.max_id = loadmoreId;

  $.ajax({
    type: 'GET',
    url: openpne.apiBase + 'activity/search.json',
    data: gorgon,
    success: function(json){
      renderJSON(json, 'more');
      $('#timeline-loadmore-loading').hide();
    },
    error: function(XMLHttpRequest, textStatus, errorThrown){
      $('#timeline-loadmore-loading').hide();
    }
  });
}

function renderJSON(json, mode) {
  if (undefined == mode)
  {
    mode = 'all';
  }
  if ('all' == mode)
  {
    $('#timeline-list').empty();
  }
  if(json.data && 0 < viewPhoto)
  {
    for(i=0;i<json.data.length;i++)
    {
      if (!json.data[i].body_html.match(/img.*src=/))
      {
        if (json.data[i].body.match(/\.(jpg|jpeg|bmg|png|gif)/gi))
        {
          json.data[i].body_html = json.data[i].body.replace(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+.(jpg|jpeg|bmg|png|gif))/gi, '

');
        }
        else if (json.data[i].body.match(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+)/gi))
        {
          json.data[i].body_html = json.data[i].body.replace(/((http:|https:)\/\/[\x21-\x26\x28-\x7e]+)/gi, '

$1
');
        }
      }
      json.data[i].body_html = json.data[i].body_html.replace(/<br \/>/g, '
');
    }
  }

  $timelineData = $('#timelineTemplate').tmpl(json.data);
  $('.timeline-comment-button', $timelineData).timelineComment();
  $('.timeline-comment-link', $timelineData).click(function(){
    $commentBoxArea = $(this).parent().siblings().find('.timeline-post-comment-form');
    $commentBoxArea.show();
    $commentBoxArea.children('.timeline-post-comment-form-input').focus();
  });
  if ('diff' == mode)
  {
    $timelineData.prependTo('#timeline-list');
  }
  else
  {
    $timelineData.appendTo('#timeline-list');
  }
  if ('all' == mode || 'diff' == mode)
  {
    if(json.data[0])
    {
      $('#timeline-list').attr('data-last-id', json.data[0].id);
    }
  }
  if ('all' == mode || 'more' == mode)
  {
    var max = json.data.length - 1;
    if (json.data[max])
    {
      $('#timeline-list').attr('data-loadmore-id', json.data[max].id);
    }
  }
  if(json.data)
  {
    for(i=0;i<json.data.length;i++)
    {
      if(json.data[i].replies)
      {
        $('#timelineCommentTemplate').tmpl(json.data[i].replies.reverse()).prependTo('#commentlist-' +json.data[i].id);
        $('#timeline-post-comment-form-' + json.data[i].id, $timelineData).show();
      }
      if(10 < parseInt(json.data[i].replies_count))
      {
        $('#timeline-comment-loadmore-' + json.data[i].id).show();
      }
    }
  }
  if ('all' == mode)
  {
    $('#timeline-loading').hide();
  }
  if ('more' == mode)
  {
    $('#timeline-loadmore').show();
    $('#timeline-loadmore-loading').hide();
  }
  $('.timeago').timeago();
}

function tweetByData(data)
{
  //reference　http://lagoscript.org/jquery/upload/documentation
  $('#timeline-submit-upload').upload(
    openpne.apiBase + 'activity/post.json', data,
    function (res) {
      var resCheck = responceCheck(res);
      if (false !== resCheck)
      {
        $('#timeline-submit-error').text(resCheck);
        $('#timeline-submit-error').show();
        $('.flashTimelineDom').remove();
        return;
      }

      res = res.replace(/,\"body.*\"/g, '');
      returnData = JSON.parse(res);

      if (returnData.status === "error") {

        var errorMessages = {
          file_size: 'ファイルサイズは' + fileMaxSizeInfo['format'] + 'までです',
          upload: 'アップロードに失敗しました',
          not_image: '画像をアップロードしてください',
          tweet: '投稿に失敗しました'
        };

        var errorType = returnData.type;

        $('#timeline-submit-error').text(errorMessages[errorType]);
        $('#timeline-submit-error').show();
        $('.flashTimelineDom').remove();

      } else {
        $(".postform").toggle();
        timelineAllLoad();
      }

      $('#timeline-submit-upload').val('');
      $('#tosaka_postform_body').val('');
      $('#timeline-submit-loader').hide();
      $('#counter').text(MAXLENGTH);

    },
    'text'
    );
}

function lengthCheck(obj, target)
{
  var objLength = obj.val().length;
  if (obj.val().match(/\n/gm))
  {
    objLength = objLength + obj.val().match(/\n/gm).length;
  }

  if (0 < objLength && MAXLENGTH >= objLength)
  {
    target.removeAttr('disabled');
  }
  else
  {
    target.attr('disabled', 'disabled');
  }
}

function responceCheck(res)
{
  if (0 <= res.indexOf('\<pre'))
  {
    return 'エラーが発生しました。再度読み込んで下さい。';
  }
  return false;
}
<|endoftext|>


<commit_before>function Grammar(rules) {
    this.lhsToRhsList = {};
    for (var i in rules) {
        var rule = rules[i];
        // "A -> B C | D" -> ["A ", " B C | D"]
        var parts = rule.split('->');
        // "A"
        var lhs = parts[0].trim();
        // "B C | D"
        var rhss = parts[1].trim();
        // "B C | D" -> ["B C", "D"]
        var rhssParts = rhss.split('|');
        if (!this.lhsToRhsList[lhs]) {
            this.lhsToRhsList[lhs] = [];
        }
        for (var j in rhssParts) {
            this.lhsToRhsList[lhs].push(rhssParts[j].trim().split(' '));
        }
        // now this.lhsToRhsList contains list of these rules:
        // {... "A": [["B C"], ["D"]] ...}
    }
}
Grammar.prototype.terminalSymbols = function(token) {
    return [];
}
Grammar.prototype.getRightHandSides = function(leftHandSide) {
        var rhss = this.lhsToRhsList[leftHandSide];
        if (rhss) {
            return rhss;
        }
        return null;
    }

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

function Chart(tokens) {
    this.idToState = {};
    this.currentId = 0;
    this.chart = [];
    for (var i = 0; i < tokens.length + 1; i++) {
        this.chart[i] = [];
    }
}
Chart.prototype.addToChart = function(newState, position) {
    newState.setId(this.currentId);
    // TODO: use HashSet + LinkedList
    var chartColumn = this.chart[position];
    for (var x in chartColumn) {
        var chartState = chartColumn[x];
        if (newState.equals(chartState)) {
            chartState.appendRefsToChidStates(newState.getRefsToChidStates());
            return;
        }
    }
    chartColumn.push(newState);
    this.idToState[this.currentId] = newState;
    this.currentId++;
}
Chart.prototype.getStatesInColumn = function(index) {
    return this.chart[index];
}
Chart.prototype.countStatesInColumn = function(index) {
    return this.chart[index].length;
}
Chart.prototype.getState = function(id) {
    return this.idToState[id];
}
Chart.prototype.getFinishedRoot = function( rootRule ) {
    var lastColumn = this.chart[this.chart.length - 1];
    for(var i in lastColumn) {
        var state = lastColumn[i];
        if(state.complete() && state.getLeftHandSide() == rootRule ) {
            return state;
        }
    }
    return null;
}
Chart.prototype.log = function(column) {
    console.log('-------------------')
    console.log('Column: ' + column)
    console.log('-------------------')
    for (var j in this.chart[column]) {
        console.log(this.chart[column][j].toString())
    }
}

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

function State(lhs, rhs, dot, left, right) {
    this.lhs = lhs;
    this.rhs = rhs;
    this.dot = dot;
    this.left = left;
    this.right = right;
    this.id = -1;
    this.ref = [];
    for (var i = 0; i < rhs.length; i++) {
        this.ref[i] = {};
    }
}
State.prototype.complete = function() {
    return this.dot >= this.rhs.length;
}
State.prototype.toString = function() {
    var builder = [];
    builder.push('(id: ' + this.id + ')');
    builder.push(this.lhs);
    builder.push('→');
    for (var i = 0; i < this.rhs.length; i++) {
        if (i == this.dot) {
            builder.push('•');
        }
        builder.push(this.rhs[i]);
    }
    if (this.complete()) {
        builder.push('•');
    }
    builder.push('[' + this.left + ', ' + this.right + ']');
    builder.push(JSON.stringify(this.ref))
    return builder.join(' ');
}
State.prototype.expectedNonTerminal = function(grammar) {
    var expected = this.rhs[this.dot];
    var rhss = grammar.getRightHandSides(expected);
    if (rhss !== null) {
        return true;
    }
    return false;
}
State.prototype.setId = function(id) {
    this.id = id;
}
State.prototype.getId = function() {
    return this.id;
}
State.prototype.equals = function(otherState) {
    if (this.lhs === otherState.lhs && this.dot === otherState.dot && this.left === otherState.left && this.right === otherState.right && JSON.stringify(this.rhs) === JSON.stringify(otherState.rhs)) {
        return true;
    }
    return false;
}
State.prototype.getRefsToChidStates = function() {
    return this.ref;
}
State.prototype.appendRefsToChidStates = function(refs) {
    for (var i = 0; i < refs.length; i++) {
        if (refs[i]) {
            for (var j in refs[i]) {
                this.ref[i][j] = refs[i][j];
            }
        }
    }
}
State.prototype.predictor = function(grammar, chart) {
    var nonTerm = this.rhs[this.dot];
    var rhss = grammar.getRightHandSides(nonTerm);
    for (var i in rhss) {
        var rhs = rhss[i];
        var newState = new State(nonTerm, rhs, 0, this.right, this.right);
        chart.addToChart(newState, this.right);
    }
}
State.prototype.scanner = function(grammar, chart, token) {
    var term = this.rhs[this.dot];
    var tokenTerminals = grammar.terminalSymbols(token);
    tokenTerminals.push(token);
    for (var i in tokenTerminals) {
        if (term == tokenTerminals[i]) {
            var newState = new State(term, [token], 1, this.right, this.right + 1);
            chart.addToChart(newState, this.right + 1);
            break;
        }
    }
}
State.prototype.completer = function(chart) {
    var statesInColumn = chart.getStatesInColumn(this.left);
    for (var i in statesInColumn) {
        var existingState = statesInColumn[i];
        if (existingState.rhs[existingState.dot] == this.lhs) {
            var newState = new State(existingState.lhs, existingState.rhs, existingState.dot + 1, existingState.left, this.right);
            // copy existing refs to new state
            newState.appendRefsToChidStates(existingState.ref);
            // add ref to current state
            var rf = new Array(existingState.rhs.length);
            rf[existingState.dot] = {};
            rf[existingState.dot][this.id] = this;
            newState.appendRefsToChidStates(rf)
            chart.addToChart(newState, this.right);
        }
    }
}

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

// Returning all possible correct parse trees
// Possible exponential complexity and memory consumption!
// Take care of your grammar!
// TODO: instead of returning all possible parse trees - provide iterator + callback
State.prototype.traverse = function() {
    if (this.ref.length == 1 && Object.keys(this.ref[0]).length == 0) {
        // This is last production in parse tree (leaf)
        var subtrees = [];
        if (this.lhs != this.rhs) {
            // prettify leafs of parse tree
            subtrees.push({
                root: this.rhs
            });
        }
        return [{
            root: this.lhs,
            subtrees: subtrees
        }];
    }
    var rhsSubTrees = [];
    for (var i = 0; i < this.ref.length; i++) {
        rhsSubTrees[i] = [];
        for (var j in this.ref[i]) {
            rhsSubTrees[i] = rhsSubTrees[i].concat(this.ref[i][j].traverse());
        }
    }
    var possibleSubTrees = [];
    combinations(rhsSubTrees, 0, [], possibleSubTrees);
    var result = [];
    for (var i in possibleSubTrees) {
        result.push({
            root: this.lhs,
            subtrees: possibleSubTrees[i]
        })
    }
    return result;
}

// Generating array of all possible combinations, e.g.:
// input: [[1, 2, 3], [4, 5]]
// output: [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
function combinations(arrOfArr, i, stack, result) {
    if (i == arrOfArr.length) {
        result.push(stack.slice());
        return;
    }
    for (var j in arrOfArr[i]) {
        stack.push(arrOfArr[i][j]);
        combinations(arrOfArr, i + 1, stack, result);
        stack.pop();
    }
}

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

State.prototype.getLeftHandSide = function() {
    return this.lhs;
}

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

function parse(tokens, grammar, rootRule) {
    var chart = new Chart(tokens);
    var rootRuleRhss = grammar.getRightHandSides(rootRule);
    for (var i in rootRuleRhss) {
        var rhs = rootRuleRhss[i];
        var initialState = new State(rootRule, rhs, 0, 0, 0);
        chart.addToChart(initialState, 0);
    }
    for (var i = 0; i < tokens.length + 1; i++) {
        j = 0;
        while (j < chart.countStatesInColumn(i)) {
            var state = chart.getStatesInColumn(i)[j];
            if (!state.complete()) {
                if (state.expectedNonTerminal(grammar)) {
                    state.predictor(grammar, chart);
                } else {
                    state.scanner(grammar, chart, tokens[i]);
                }
            } else {
                state.completer(chart);
            }
            j++;
        }
        chart.log(i)
    }
    return chart;
}

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

var grammar = new Grammar([
    'R -> S',
    'S -> S add_sub M | M | num',
    'M -> M mul_div T | T | num',
    'T -> num',
    ///*
    'num -> 2 | 3 | 4',
    'add_sub -> + | -',
    'mul_div -> * | /'
    //*/
]);
/*
grammar.terminalSymbols = function( token ) {
if( '+' === token || '-' === token ) return ['add_sub'];
if( '*' === token || '/' === token ) return ['mul_div'];
return ['num'];
}
*/
var chart = parse('2 + 3 * 4'.split(' '), grammar, 'R');
console.log('\n')
var state = chart.getFinishedRoot('R');
var trees = state.traverse();
for (var i in trees) {
    console.log(JSON.stringify(trees[i]))
    document.body.innerHTML += '

' + displayTree(trees[i]) + '

'
}

function displayTree(tree) {
    if (tree.subtrees.length == 0) {
        return '
' + tree.root + '
';
    }
    var builder = [];
    builder.push('
');
    builder.push(tree.root);
    builder.push('')
    builder.push('
')
    for (var i in tree.subtrees) {
        builder.push(displayTree(tree.subtrees[i]))
    }
    builder.push('
')
    builder.push('
')
    return builder.join('');
}
<commit_msg>Encapsulated Earley-OOP stuff to separate 'namespace' (tinynlp)<commit_after>var tinynlp = (function(){

    function Grammar(rules) {
        this.lhsToRhsList = {};
        for (var i in rules) {
            var rule = rules[i];
            // "A -> B C | D" -> ["A ", " B C | D"]
            var parts = rule.split('->');
            // "A"
            var lhs = parts[0].trim();
            // "B C | D"
            var rhss = parts[1].trim();
            // "B C | D" -> ["B C", "D"]
            var rhssParts = rhss.split('|');
            if (!this.lhsToRhsList[lhs]) {
                this.lhsToRhsList[lhs] = [];
            }
            for (var j in rhssParts) {
                this.lhsToRhsList[lhs].push(rhssParts[j].trim().split(' '));
            }
            // now this.lhsToRhsList contains list of these rules:
            // {... "A": [["B C"], ["D"]] ...}
        }
    }
    Grammar.prototype.terminalSymbols = function(token) {
        return [];
    }
    Grammar.prototype.getRightHandSides = function(leftHandSide) {
            var rhss = this.lhsToRhsList[leftHandSide];
            if (rhss) {
                return rhss;
            }
            return null;
        }

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

    function Chart(tokens) {
        this.idToState = {};
        this.currentId = 0;
        this.chart = [];
        for (var i = 0; i < tokens.length + 1; i++) {
            this.chart[i] = [];
        }
    }
    Chart.prototype.addToChart = function(newState, position) {
        newState.setId(this.currentId);
        // TODO: use HashSet + LinkedList
        var chartColumn = this.chart[position];
        for (var x in chartColumn) {
            var chartState = chartColumn[x];
            if (newState.equals(chartState)) {
                chartState.appendRefsToChidStates(newState.getRefsToChidStates());
                return;
            }
        }
        chartColumn.push(newState);
        this.idToState[this.currentId] = newState;
        this.currentId++;
    }
    Chart.prototype.getStatesInColumn = function(index) {
        return this.chart[index];
    }
    Chart.prototype.countStatesInColumn = function(index) {
        return this.chart[index].length;
    }
    Chart.prototype.getState = function(id) {
        return this.idToState[id];
    }
    Chart.prototype.getFinishedRoot = function( rootRule ) {
        var lastColumn = this.chart[this.chart.length - 1];
        for(var i in lastColumn) {
            var state = lastColumn[i];
            if(state.complete() && state.getLeftHandSide() == rootRule ) {
                return state;
            }
        }
        return null;
    }
    Chart.prototype.log = function(column) {
        console.log('-------------------')
        console.log('Column: ' + column)
        console.log('-------------------')
        for (var j in this.chart[column]) {
            console.log(this.chart[column][j].toString())
        }
    }

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

    function State(lhs, rhs, dot, left, right) {
        this.lhs = lhs;
        this.rhs = rhs;
        this.dot = dot;
        this.left = left;
        this.right = right;
        this.id = -1;
        this.ref = [];
        for (var i = 0; i < rhs.length; i++) {
            this.ref[i] = {};
        }
    }
    State.prototype.complete = function() {
        return this.dot >= this.rhs.length;
    }
    State.prototype.toString = function() {
        var builder = [];
        builder.push('(id: ' + this.id + ')');
        builder.push(this.lhs);
        builder.push('→');
        for (var i = 0; i < this.rhs.length; i++) {
            if (i == this.dot) {
                builder.push('•');
            }
            builder.push(this.rhs[i]);
        }
        if (this.complete()) {
            builder.push('•');
        }
        builder.push('[' + this.left + ', ' + this.right + ']');
        builder.push(JSON.stringify(this.ref))
        return builder.join(' ');
    }
    State.prototype.expectedNonTerminal = function(grammar) {
        var expected = this.rhs[this.dot];
        var rhss = grammar.getRightHandSides(expected);
        if (rhss !== null) {
            return true;
        }
        return false;
    }
    State.prototype.setId = function(id) {
        this.id = id;
    }
    State.prototype.getId = function() {
        return this.id;
    }
    State.prototype.equals = function(otherState) {
        if (this.lhs === otherState.lhs && this.dot === otherState.dot && this.left === otherState.left && this.right === otherState.right && JSON.stringify(this.rhs) === JSON.stringify(otherState.rhs)) {
            return true;
        }
        return false;
    }
    State.prototype.getRefsToChidStates = function() {
        return this.ref;
    }
    State.prototype.appendRefsToChidStates = function(refs) {
        for (var i = 0; i < refs.length; i++) {
            if (refs[i]) {
                for (var j in refs[i]) {
                    this.ref[i][j] = refs[i][j];
                }
            }
        }
    }
    State.prototype.predictor = function(grammar, chart) {
        var nonTerm = this.rhs[this.dot];
        var rhss = grammar.getRightHandSides(nonTerm);
        for (var i in rhss) {
            var rhs = rhss[i];
            var newState = new State(nonTerm, rhs, 0, this.right, this.right);
            chart.addToChart(newState, this.right);
        }
    }
    State.prototype.scanner = function(grammar, chart, token) {
        var term = this.rhs[this.dot];
        var tokenTerminals = grammar.terminalSymbols(token);
        tokenTerminals.push(token);
        for (var i in tokenTerminals) {
            if (term == tokenTerminals[i]) {
                var newState = new State(term, [token], 1, this.right, this.right + 1);
                chart.addToChart(newState, this.right + 1);
                break;
            }
        }
    }
    State.prototype.completer = function(chart) {
        var statesInColumn = chart.getStatesInColumn(this.left);
        for (var i in statesInColumn) {
            var existingState = statesInColumn[i];
            if (existingState.rhs[existingState.dot] == this.lhs) {
                var newState = new State(existingState.lhs, existingState.rhs, existingState.dot + 1, existingState.left, this.right);
                // copy existing refs to new state
                newState.appendRefsToChidStates(existingState.ref);
                // add ref to current state
                var rf = new Array(existingState.rhs.length);
                rf[existingState.dot] = {};
                rf[existingState.dot][this.id] = this;
                newState.appendRefsToChidStates(rf)
                chart.addToChart(newState, this.right);
            }
        }
    }

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

    // Returning all possible correct parse trees
    // Possible exponential complexity and memory consumption!
    // Take care of your grammar!
    // TODO: instead of returning all possible parse trees - provide iterator + callback
    State.prototype.traverse = function() {
        if (this.ref.length == 1 && Object.keys(this.ref[0]).length == 0) {
            // This is last production in parse tree (leaf)
            var subtrees = [];
            if (this.lhs != this.rhs) {
                // prettify leafs of parse tree
                subtrees.push({
                    root: this.rhs
                });
            }
            return [{
                root: this.lhs,
                subtrees: subtrees
            }];
        }
        var rhsSubTrees = [];
        for (var i = 0; i < this.ref.length; i++) {
            rhsSubTrees[i] = [];
            for (var j in this.ref[i]) {
                rhsSubTrees[i] = rhsSubTrees[i].concat(this.ref[i][j].traverse());
            }
        }
        var possibleSubTrees = [];
        combinations(rhsSubTrees, 0, [], possibleSubTrees);
        var result = [];
        for (var i in possibleSubTrees) {
            result.push({
                root: this.lhs,
                subtrees: possibleSubTrees[i]
            })
        }
        return result;
    }

    // Generating array of all possible combinations, e.g.:
    // input: [[1, 2, 3], [4, 5]]
    // output: [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
    function combinations(arrOfArr, i, stack, result) {
        if (i == arrOfArr.length) {
            result.push(stack.slice());
            return;
        }
        for (var j in arrOfArr[i]) {
            stack.push(arrOfArr[i][j]);
            combinations(arrOfArr, i + 1, stack, result);
            stack.pop();
        }
    }

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

    State.prototype.getLeftHandSide = function() {
        return this.lhs;
    }

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

    function parse(tokens, grammar, rootRule) {
        var chart = new Chart(tokens);
        var rootRuleRhss = grammar.getRightHandSides(rootRule);
        for (var i in rootRuleRhss) {
            var rhs = rootRuleRhss[i];
            var initialState = new State(rootRule, rhs, 0, 0, 0);
            chart.addToChart(initialState, 0);
        }
        for (var i = 0; i < tokens.length + 1; i++) {
            j = 0;
            while (j < chart.countStatesInColumn(i)) {
                var state = chart.getStatesInColumn(i)[j];
                if (!state.complete()) {
                    if (state.expectedNonTerminal(grammar)) {
                        state.predictor(grammar, chart);
                    } else {
                        state.scanner(grammar, chart, tokens[i]);
                    }
                } else {
                    state.completer(chart);
                }
                j++;
            }
            chart.log(i)
        }
        return chart;
    }

    var exports = {};
    exports.Grammar = Grammar;
    exports.State = State;
    exports.Chart = Chart;
    exports.parse = parse;
    return exports;
})();
//------------------------------------------------------------------------------------

var grammar = new tinynlp.Grammar([
    'R -> S',
    'S -> S add_sub M | M | num',
    'M -> M mul_div T | T | num',
    'T -> num',
    ///*
    'num -> 2 | 3 | 4',
    'add_sub -> + | -',
    'mul_div -> * | /'
    //*/
]);
/*
grammar.terminalSymbols = function( token ) {
if( '+' === token || '-' === token ) return ['add_sub'];
if( '*' === token || '/' === token ) return ['mul_div'];
return ['num'];
}
*/
var chart = tinynlp.parse('2 + 3 * 4'.split(' '), grammar, 'R');
console.log('\n')
var state = chart.getFinishedRoot('R');
var trees = state.traverse();
for (var i in trees) {
    console.log(JSON.stringify(trees[i]))
    document.body.innerHTML += '

' + displayTree(trees[i]) + '

'
}

function displayTree(tree) {
    if (tree.subtrees.length == 0) {
        return '
' + tree.root + '
';
    }
    var builder = [];
    builder.push('
');
    builder.push(tree.root);
    builder.push('')
    builder.push('
')
    for (var i in tree.subtrees) {
        builder.push(displayTree(tree.subtrees[i]))
    }
    builder.push('
')
    builder.push('
')
    return builder.join('');
}
<|endoftext|>