<commit_before>(function () {
	var encodeFunction = encodeURIComponent, i = 0, j = 0, isready, request_queue2 = [], doc = document;

	window.addEventListener("beforeunload", function (e) {
		ajax('x/' + mt.actionid);
	});

	function ajax(url, data, callback) {
		var script = doc.createElement('script');
		// script.type = 'text/javascript'; comment this because we dont need to excute the script
		script.src = '//api.meotrics.com/' + mt.appid + '/' + url + (data ? '?' + serialize(data) : '');
		script.style.display = 'none';
		script.onreadystatechange = script.onload = callback;//for IE
		doc.body === undefined ? doc.head.appendChild(script) : doc.body.appendChild(script);
	}

	function serialize(obj, prefix) {
		var str = [];
		for (var p in obj)/* if (obj.hasOwnProperty(p)) */{
			var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
			str.push(v instanceof Object ? serialize(v, k) : encodeFunction(k) + "=" + encodeFunction(v));
		}
		return str.join("&");
	}

	mt.info = function (data, callback, callback2, callback3) {
		if(typeof data == 'string') data = {userid: data};
		if(isready)
			ajax('info', data, callback || callback3)
		else{
			request_queue2.push(['info', data]);
			(callback || callback3)();
		}
	};

	mt.clear = function (callback, callback2, callback3, callback4) {
		return isready ?: request_queue2.push(['clear']);
		if(isready)
			 ajax('clear', callback3 /*alway undefined, use callback3 for better minify*/, callback || callback4);
		else{
			request_queue2.push(['clear']);
			(callback || callback4)();
		}
	};

	mt.track = function (event, data, time, callback) {
		data._deltat = (new Date() - time) / 1000;
		data._typeid = event;
		if(isready) ajax('track', addVisitorPlatform(data), callback)
		else
		{
			request_queue2.push(['track', event, data, new Date()]);
			callback();
		}
	};

	function addVisitorPlatform(data) {
		data._ref = doc.referrer;
		data._scr = screen.width + "x" + screen.height;
		data._url = location.href;
		data._link = mt.actionid;
		return data;
	}

	// clean request queue
	function cleanRequest() {
		// clean queue number 2 when out of element in queue number 1
		if (i >= mt.rq.length) return cleanRequest2();
		var rq = mt.rq[i++];
		mt[rq[0]](rq[1], rq[2], rq[3], cleanRequest);
	}

	// clean request queue step 2
	function cleanRequest2() {
		if (j >= request_queue2.length) // clean the state when done
			return isready = 1;
		var rq = request_queue2[j++];
		mt[rq[0]](rq[1], rq[2], rq[3], cleanRequest2);
	}

	mt.excute = function(event){
		var data = JSON.parse(event.data);
		mt.actionid = data.actionid;
		if(data.lastactionid !== undefined)
		{
			var o1 = {
				actionid : data.actionid,
				lastactionid: data.lastactionid
			}
			addVisitorPlatform(o1);
			ajax('fix',o1);
		}
		else
		{
			var o2 = {actionid : data.actionid};
			addVisitorPlatform(o2);
			ajax('fix', o2);
		}
		cleanRequest();// excute delayed request in queue
	}

	var ifm = doc.createElement('iframe');
	ifm.style.display="none";
	ifm.src = "//meotrics.com/iframe.html?x=" + mt.appid + '-' + mt.actionid;
	if(doc.body === undefined) doc.head.appendChild(ifm);
	else doc.body.appendChild(ifm);

	mt.onready();
})();<commit_msg>fix syntax error<commit_after>(function () {
	var encodeFunction = encodeURIComponent, i = 0, j = 0, isready, request_queue2 = [], doc = document;

	window.addEventListener("beforeunload", function (e) {
		ajax('x/' + mt.actionid);
	});

	function ajax(url, data, callback) {
		var script = doc.createElement('script');
		// script.type = 'text/javascript'; comment this because we dont need to excute the script
		script.src = '//api.meotrics.com/' + mt.appid + '/' + url + (data ? '?' + serialize(data) : '');
		script.style.display = 'none';
		script.onreadystatechange = script.onload = callback;//for IE
		doc.body === undefined ? doc.head.appendChild(script) : doc.body.appendChild(script);
	}

	function serialize(obj, prefix) {
		var str = [];
		for (var p in obj)/* if (obj.hasOwnProperty(p)) */{
			var k = prefix ? prefix + "[" + p + "]" : p, v = obj[p];
			str.push(v instanceof Object ? serialize(v, k) : encodeFunction(k) + "=" + encodeFunction(v));
		}
		return str.join("&");
	}

	mt.info = function (data, callback, callback2, callback3) {
		if(typeof data == 'string') data = {userid: data};
		if(isready)
			ajax('info', data, callback || callback3)
		else{
			request_queue2.push(['info', data]);
			(callback || callback3)();
		}
	};

	mt.clear = function (callback, callback2, callback3, callback4) {
		if(isready)
			 ajax('clear', callback3 /*alway undefined, use callback3 for better minify*/, callback || callback4);
		else{
			request_queue2.push(['clear']);
			(callback || callback4)();
		}
	};

	mt.track = function (event, data, time, callback) {
		data._deltat = (new Date() - time) / 1000;
		data._typeid = event;
		if(isready) ajax('track', addVisitorPlatform(data), callback)
		else
		{
			request_queue2.push(['track', event, data, new Date()]);
			callback();
		}
	};

	function addVisitorPlatform(data) {
		data._ref = doc.referrer;
		data._scr = screen.width + "x" + screen.height;
		data._url = location.href;
		data._link = mt.actionid;
		return data;
	}

	// clean request queue
	function cleanRequest() {
		// clean queue number 2 when out of element in queue number 1
		if (i >= mt.rq.length) return cleanRequest2();
		var rq = mt.rq[i++];
		mt[rq[0]](rq[1], rq[2], rq[3], cleanRequest);
	}

	// clean request queue step 2
	function cleanRequest2() {
		if (j >= request_queue2.length) // clean the state when done
			return isready = 1;
		var rq = request_queue2[j++];
		mt[rq[0]](rq[1], rq[2], rq[3], cleanRequest2);
	}

	mt.excute = function(event){
		var data = JSON.parse(event.data);
		mt.actionid = data.actionid;
		if(data.lastactionid !== undefined)
		{
			var o1 = {
				actionid : data.actionid,
				lastactionid: data.lastactionid
			}
			addVisitorPlatform(o1);
			ajax('fix',o1);
		}
		else
		{
			var o2 = {actionid : data.actionid};
			addVisitorPlatform(o2);
			ajax('fix', o2);
		}
		cleanRequest();// excute delayed request in queue
	}

	var ifm = doc.createElement('iframe');
	ifm.style.display="none";
	ifm.src = "//meotrics.com/iframe.html?x=" + mt.appid + '-' + mt.actionid;
	if(doc.body === undefined) doc.head.appendChild(ifm);
	else doc.body.appendChild(ifm);

	mt.onready();
})();<|endoftext|>


<commit_before>const resolve = require('resolve-pathname')
const $ = window.jQuery

const npmurl = require('./javascript').npmurl
const composerurl = require('./php').composerurl
const createLink = require('../helpers').createLink

module.exports.process = function process () {
  switch (window.pathdata.last) {
    case 'composer.json':
      composerjson()
      break
    case 'package.json':
      packagejson()
      break
    case 'elm-package.json':
      elmpackagejson()
      break
  }
}

function composerjson () {
  var depsOpen = false

  $('.blob-code-inner').each((_, elem) => {
    elem = $(elem)
    let line = elem.text().trim()

    if (line.match(/"require"/) || line.match(/"require-dev"/) ||
        line.match(/"suggest"/) || line.match(/"replace"/) || line.match(/"conflict"/)) {
      depsOpen = true
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(elem, composerurl)
    }
  })
}

function packagejson () {
  var depsOpen = false
  var contributorsOpen = false
  var authorOpen = false
  var binOpen = false

  $('.blob-code-inner').each((_, rawelem) => {
    let elem = $(rawelem)
    let line = elem.text().trim()

    if (line.match(/"dependencies"/) || line.match(/"devDependencies"/) ||
        line.match(/"peerDependencies"/) || line.match(/"optionalDependencies"/)) {
      depsOpen = true
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (line.match(/"contributors"/) || line.match(/"authors"/)) {
      contributorsOpen = true
    }

    if (line.match(']')) {
      contributorsOpen = false
    }

    if (line.match(/"author"/) && line.match('{')) {
      authorOpen = true
    }

    if (line.match('}')) {
      authorOpen = false
    }

    if (line.match(/"bin"/) && line.match('{')) {
      binOpen = true
    }

    if (line.match('}')) {
      binOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(elem, npmurl)
    }

    if (!contributorsOpen && !authorOpen && line.match(/"name":/)) {
      let name = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = 'https://npmjs.com/package/' + name
      createLink(rawelem, name, {url, kind: 'maybe'})
    }

    if (binOpen && line.split(':').length == 2) {
      let main = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = resolve(main, location.pathname)
      createLink(rawelem, main, url)
    }

    if (line.match(/"main":/) || line.match(/"module":/) || line.match(/"es2015":/) || line.match(/"esnext":/) || // Files for different Node.js versions
        (line.match(/"browser":/) && !line.match(/"browser": {/)) || line.match(/"web":/) || // Files for web browsers
        line.match(/"unpkg":/) || line.match(/"jsdelivr":/) || line.match(/"runkitExampleFilename":/) || // Files for popular CDNs and examples
        line.match(/"source":/) || line.match(/"src":/) || line.match(/"typings":/) || line.match(/"types":/) || // Files for sources and typings
        line.match(/"node":/) || line.match(/"deno":/) || // Files for different runtimes
        (line.match(/"bin":/) && !line.match(/"bin": {/)) // Executable file
    ) {
      let main = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = resolve(main, location.pathname)
      createLink(rawelem, main, url)
    }
  })
}

function lineWithUrlFetcher (elem, urlfetcher) {
  let link = elem.find('.pl-s').eq(0)
  let moduleName = link.text().trim().slice(1, -1)

  urlfetcher(moduleName)
    .then(url => {
      createLink(link.get(0), moduleName, url)
    })
    .catch(() => {})
}

function elmpackagejson () {
  var depsOpen = false
  var srcDirectoriesOpen = false

  $('.blob-code-inner').each((_, elem) => {
    elem = $(elem)
    let line = elem.text().trim()

    if (line.match(/"dependencies"/)) {
      depsOpen = true
      return
    }

    if (line.match(/"source-directories"/)) {
      srcDirectoriesOpen = true
      return
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (line.match(']')) {
      srcDirectoriesOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(
        elem,
        (moduleName) =>
          Promise.resolve({
            url: `http://package.elm-lang.org/packages/${moduleName}/latest`,
            kind: 'external'
          })
      )
    }

    if (srcDirectoriesOpen && elem.find('.pl-s').length === 1) {
      let path = elem.find('.pl-s').text().slice(1, -1)
      if (path && path.length > 1) {
        createLink(elem.get(0), path, path)
      }
    }
  })
}
<commit_msg>Allow multiple spaces between key and colon for Node.js<commit_after>const resolve = require('resolve-pathname')
const $ = window.jQuery

const npmurl = require('./javascript').npmurl
const composerurl = require('./php').composerurl
const createLink = require('../helpers').createLink

module.exports.process = function process () {
  switch (window.pathdata.last) {
    case 'composer.json':
      composerjson()
      break
    case 'package.json':
      packagejson()
      break
    case 'elm-package.json':
      elmpackagejson()
      break
  }
}

function composerjson () {
  var depsOpen = false

  $('.blob-code-inner').each((_, elem) => {
    elem = $(elem)
    let line = elem.text().trim()

    if (line.match(/"require"/) || line.match(/"require-dev"/) ||
        line.match(/"suggest"/) || line.match(/"replace"/) || line.match(/"conflict"/)) {
      depsOpen = true
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(elem, composerurl)
    }
  })
}

function packagejson () {
  var depsOpen = false
  var contributorsOpen = false
  var authorOpen = false
  var binOpen = false

  $('.blob-code-inner').each((_, rawelem) => {
    let elem = $(rawelem)
    let line = elem.text().trim()

    if (line.match(/"dependencies"/) || line.match(/"devDependencies"/) ||
        line.match(/"peerDependencies"/) || line.match(/"optionalDependencies"/)) {
      depsOpen = true
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (line.match(/"contributors"/) || line.match(/"authors"/)) {
      contributorsOpen = true
    }

    if (line.match(']')) {
      contributorsOpen = false
    }

    if (line.match(/"author"/) && line.match('{')) {
      authorOpen = true
    }

    if (line.match('}')) {
      authorOpen = false
    }

    if (line.match(/"bin"/) && line.match('{')) {
      binOpen = true
    }

    if (line.match('}')) {
      binOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(elem, npmurl)
    }

    if (!contributorsOpen && !authorOpen && line.match(/"name"\s*:/)) {
      let name = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = 'https://npmjs.com/package/' + name
      createLink(rawelem, name, {url, kind: 'maybe'})
    }

    if (binOpen && line.split(':').length == 2) {
      let main = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = resolve(main, location.pathname)
      createLink(rawelem, main, url)
    }

    if (line.match(/"main"\s*:/) || line.match(/"module"\s*:/) || line.match(/"es2015"\s*:/) || line.match(/"esnext"\s*:/) || // Files for different Node.js versions
        (line.match(/"browser"\s*:/) && !line.match(/"browser"\s*:\s*{/)) || line.match(/"web"\s*:/) || // Files for web browsers
        line.match(/"unpkg"\s*:/) || line.match(/"jsdelivr"\s*:/) || line.match(/"runkitExampleFilename"\s*:/) || // Files for popular CDNs and examples
        line.match(/"source"\s*:/) || line.match(/"src"\s*:/) || line.match(/"typings"\s*:/) || line.match(/"types"\s*:/) || // Files for sources and typings
        line.match(/"node"\s*:/) || line.match(/"deno"\s*:/) || // Files for different runtimes
        (line.match(/"bin"\s*:/) && !line.match(/"bin"\s*:\s*{/)) // Executable file
    ) {
      let main = elem.find('.pl-s').eq(1).text().trim().slice(1, -1)
      let url = resolve(main, location.pathname)
      createLink(rawelem, main, url)
    }
  })
}

function lineWithUrlFetcher (elem, urlfetcher) {
  let link = elem.find('.pl-s').eq(0)
  let moduleName = link.text().trim().slice(1, -1)

  urlfetcher(moduleName)
    .then(url => {
      createLink(link.get(0), moduleName, url)
    })
    .catch(() => {})
}

function elmpackagejson () {
  var depsOpen = false
  var srcDirectoriesOpen = false

  $('.blob-code-inner').each((_, elem) => {
    elem = $(elem)
    let line = elem.text().trim()

    if (line.match(/"dependencies"/)) {
      depsOpen = true
      return
    }

    if (line.match(/"source-directories"/)) {
      srcDirectoriesOpen = true
      return
    }

    if (line.match('}')) {
      depsOpen = false
    }

    if (line.match(']')) {
      srcDirectoriesOpen = false
    }

    if (depsOpen && elem.find('.pl-s').length === 2) {
      lineWithUrlFetcher(
        elem,
        (moduleName) =>
          Promise.resolve({
            url: `http://package.elm-lang.org/packages/${moduleName}/latest`,
            kind: 'external'
          })
      )
    }

    if (srcDirectoriesOpen && elem.find('.pl-s').length === 1) {
      let path = elem.find('.pl-s').text().slice(1, -1)
      if (path && path.length > 1) {
        createLink(elem.get(0), path, path)
      }
    }
  })
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Sebastian Schaffert (wastl@wastl.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Created by wastl on 24.03.14.
 */
var rdfdotApp = angular.module('rdfdotApp', ['ui.bootstrap','colorpicker.module']);

rdfdotApp.controller("ConfigurationCtrl", function($scope,$http) {

    $scope.inputs = ['turtle','xml'];
    $scope.outputs = ['png','jpg'];

    $scope.data = "";

    $scope.configuration = {
        'input': 'turtle',
        'output': 'png',
        'base64': 'true',

        uri_shape: 'OVAL',
        uri_fill:  "#ffffff",
        uri_style: 'SOLID',

        bnode_shape: 'POINT',
        bnode_fill:  '#ffffff',
        bnode_style: 'SOLID',

        literal_shape: 'BOX',
        literal_fill:  '#B24CFF',
        literal_style: 'FILLED',

        arrow_shape: 'VEE',
        arrow_style: 'SOLID',
        arrow_color: '#000000'
    };

    $scope.image = null;
    $scope.alerts = [];

    $scope.submit = function() {

        $http.post("render",$scope.data,{ 'params': $scope.configuration }).
            success(function(img) {
                console.log(img);
                $scope.image = img;
            }).
            error(function(data,status) {
                $scope.addAlert(data);
            });
    };

    $scope.addAlert = function(msg) {
        $scope.alerts.push({ type: 'danger', 'msg': msg});
    };

    $scope.closeAlert = function(idx) {
        $scope.alerts.splice(idx, 1);
    };

    // show configuration panel
    $scope.show_configuration = false;

    $scope.node_shapes = ['OVAL','ELLIPSE','EGG','CIRCLE', 'BOX','SQUARE', 'POINT', 'OCTAGON', 'PLAINTEXT'];
    $scope.node_styles = ['SOLID', 'DASHED', 'DOTTED', 'FILLED', 'BOLD'];

    $scope.arrow_shapes = ['NORMAL', 'VEE', 'TEE', 'CURVE', 'DOT', 'DIAMOND'];
    $scope.arrow_styles = ['SOLID', 'DASHED', 'DOTTED', 'BOLD'];

    // color configuration
    $scope.uri_css = {
        'background-color': $scope.configuration.uri_fill
    };

    $scope.$watch('configuration.uri_fill', function() {
        $scope.uri_css['background-color'] = $scope.configuration.uri_fill;
    });

    $scope.bnode_css = {
        'background-color': $scope.configuration.bnode_fill
    };

    $scope.$watch('configuration.bnode_fill', function() {
        $scope.bnode_css['background-color'] = $scope.configuration.bnode_fill;
    });

    $scope.literal_css = {
        'background-color': $scope.configuration.literal_fill
    };

    $scope.$watch('configuration.literal_fill', function() {
        $scope.literal_css['background-color'] = $scope.configuration.literal_fill;
    });

    $scope.edge_css = {
        'background-color': $scope.configuration.arrow_color
    };

    $scope.$watch('configuration.arrow_color', function() {
        $scope.edge_css['background-color'] = $scope.configuration.arrow_color;
    });

    $scope.examples = {
        'FOAF': '@prefix ex: <http://example.com/> .\n@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n\nex:Alice a foaf:Person ;\n    foaf:firstName "Alice" ;\n    foaf:lastName  "Smith" ;\n    foaf:knows ex:Bob .\n        \nex:Bob a foaf:Person ;\n    foaf:firstName "Bob" ;\n    foaf:lastName  "Cook" ;\n    foaf:knows ex:Alice.\n',
        'Students': '@prefix ex: <http://example.com/> .\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n\nex:Student rdfs:subClassOf ex:Person.\n\nex:Professor rdfs:subClassOf ex:Person.\n\nex:Algorithms_and_Datastructures a ex:Lecture .\n\nex:Alice a ex:Professor ;\n    rdfs:label "Alice Smith" ;\n    ex:teaches ex:Algorithms_and_Datastructures .\n\nex:Bob a ex:Student ;\n    rdfs:label "Bob Cook" ;\n    ex:attends ex:Algorithms_and_Datastructures .\n'
    };

    $scope.selectExample = function(name) {
        $scope.data = $scope.examples[name];
    };
});

<commit_msg>remove all open alerts on new submit<commit_after>/*
 * Copyright 2014 Sebastian Schaffert (wastl@wastl.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Created by wastl on 24.03.14.
 */
var rdfdotApp = angular.module('rdfdotApp', ['ui.bootstrap','colorpicker.module']);

rdfdotApp.controller("ConfigurationCtrl", function($scope,$http) {

    $scope.inputs = ['turtle','xml'];
    $scope.outputs = ['png','jpg'];

    $scope.data = "";

    $scope.configuration = {
        'input': 'turtle',
        'output': 'png',
        'base64': 'true',

        uri_shape: 'OVAL',
        uri_fill:  "#ffffff",
        uri_style: 'SOLID',

        bnode_shape: 'POINT',
        bnode_fill:  '#ffffff',
        bnode_style: 'SOLID',

        literal_shape: 'BOX',
        literal_fill:  '#B24CFF',
        literal_style: 'FILLED',

        arrow_shape: 'VEE',
        arrow_style: 'SOLID',
        arrow_color: '#000000'
    };

    $scope.image = null;
    $scope.alerts = [];

    $scope.submit = function() {

        $scope.alerts = [];

        $http.post("render",$scope.data,{ 'params': $scope.configuration }).
            success(function(img) {
                console.log(img);
                $scope.image = img;
            }).
            error(function(data,status) {
                $scope.addAlert(data);
            });
    };

    $scope.addAlert = function(msg) {
        $scope.alerts.push({ type: 'danger', 'msg': msg});
    };

    $scope.closeAlert = function(idx) {
        $scope.alerts.splice(idx, 1);
    };

    // show configuration panel
    $scope.show_configuration = false;

    $scope.node_shapes = ['OVAL','ELLIPSE','EGG','CIRCLE', 'BOX','SQUARE', 'POINT', 'OCTAGON', 'PLAINTEXT'];
    $scope.node_styles = ['SOLID', 'DASHED', 'DOTTED', 'FILLED', 'BOLD'];

    $scope.arrow_shapes = ['NORMAL', 'VEE', 'TEE', 'CURVE', 'DOT', 'DIAMOND'];
    $scope.arrow_styles = ['SOLID', 'DASHED', 'DOTTED', 'BOLD'];

    // color configuration
    $scope.uri_css = {
        'background-color': $scope.configuration.uri_fill
    };

    $scope.$watch('configuration.uri_fill', function() {
        $scope.uri_css['background-color'] = $scope.configuration.uri_fill;
    });

    $scope.bnode_css = {
        'background-color': $scope.configuration.bnode_fill
    };

    $scope.$watch('configuration.bnode_fill', function() {
        $scope.bnode_css['background-color'] = $scope.configuration.bnode_fill;
    });

    $scope.literal_css = {
        'background-color': $scope.configuration.literal_fill
    };

    $scope.$watch('configuration.literal_fill', function() {
        $scope.literal_css['background-color'] = $scope.configuration.literal_fill;
    });

    $scope.edge_css = {
        'background-color': $scope.configuration.arrow_color
    };

    $scope.$watch('configuration.arrow_color', function() {
        $scope.edge_css['background-color'] = $scope.configuration.arrow_color;
    });

    $scope.examples = {
        'FOAF': '@prefix ex: <http://example.com/> .\n@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n\nex:Alice a foaf:Person ;\n    foaf:firstName "Alice" ;\n    foaf:lastName  "Smith" ;\n    foaf:knows ex:Bob .\n        \nex:Bob a foaf:Person ;\n    foaf:firstName "Bob" ;\n    foaf:lastName  "Cook" ;\n    foaf:knows ex:Alice.\n',
        'Students': '@prefix ex: <http://example.com/> .\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n\nex:Student rdfs:subClassOf ex:Person.\n\nex:Professor rdfs:subClassOf ex:Person.\n\nex:Algorithms_and_Datastructures a ex:Lecture .\n\nex:Alice a ex:Professor ;\n    rdfs:label "Alice Smith" ;\n    ex:teaches ex:Algorithms_and_Datastructures .\n\nex:Bob a ex:Student ;\n    rdfs:label "Bob Cook" ;\n    ex:attends ex:Algorithms_and_Datastructures .\n'
    };

    $scope.selectExample = function(name) {
        $scope.data = $scope.examples[name];
    };
});

<|endoftext|>


<commit_before>var request = require('request')
var Endpoint = require('./Endpoint')
var HttpErrors = require('http-errors')

/******************************************************************************
 * @class Collection
 * @constructor
 *
 * A Collection is a wrapper around an Endpoint that exposes a higher-level
 * set of methods that abstracts the endpoint as a resource collection.
 *
 */
function Collection(endpoint) {
  this.endpoint = endpoint
}

/**********************************************************************
 * find
 *
 * @param {Object} query (optional)
 * @param {Object} options (optional)
 *
 * Supported calling forms:
 *   find()
 *   find(query)
 *   find(query, options)
 *
 * @return CollectionCursor
 */
Collection.prototype.find = function() {
  return new CollectionCursor(this, arguments)
}

/**********************************************************************
 * _doFind
 *
 * @param {Object} query (optional)
 * @param {Object} options (optional)
 * @param {Function} cb
 *
 * Supported calling forms:
 *   find(cb)
 *   find(query, cb)
 *   find(query, options, cb)
 */
Collection.prototype._doFind = function() {
  var query = undefined
  var options = {}
  var cb = undefined

  if (arguments.length === 1) {
    cb = arguments[0]
  } else if (arguments.length === 2) {
    query = arguments[0]
    cb = arguments[1]
  } else if (arguments.length === 3) {
    query = arguments[0]
    options = arguments[1]
    cb = arguments[2]
  }

  // Create proper get query parameters
  var params = {}
  if (query) {
    params.query = query
  }
  if (options.sort) {
    params.sort = options.sort
  }
  if (options.fields) {
    params.fields = options.fields
  }
  if (options.skip) {
    params.skip = options.skip
  }
  if (options.limit) {
    params.limit = options.limit
  }

  return this.endpoint.get({ params: params }, function(err, res) {
//  return this.endpoint.get({}, function(err, res) {
//  return this.endpoint.get(function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res ? res.body : null)
  })
}

/**********************************************************************
 * insert
 *
 * @param {Object} obj // XXX do we support arrays?
 * @param {Function} cb
 *
 * Supported calling forms:
 *   insert(obj, cb)
 */
Collection.prototype.insert = function(obj, cb) {
  return this.endpoint.post(obj, {}, function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/**********************************************************************
 * update
 *
 * @param {Object} query
 * @param {Object} obj
 * @param {Object} options // XXX see leafnode
 * @param {Function} cb
 *
 * Supported calling forms:
 *   update(query, obj, cb)
 *   update(query, obj, options, cb)
 */
Collection.prototype.update = function() {
  var query = {}
  var obj = undefined
  var options = {}
  var cb = undefined

  if (arguments.length === 3) {
    query = arguments[0]
    obj = arguments[1]
    cb = arguments[2]
  } else if (arguments.length === 3) {
    query = arguments[0]
    obj = arguments[1]
    options = arguments[2]
    cb = arguments[3]
  } else {
    throw new Error("Must supply a query and an obj.")
  }

  return this.endpoint.put(query, {}, function(err, res) { // XXX put or patch depending on obj? Hmmm?
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/**********************************************************************
 * remove
 *
 * @param {Object} query
 * @param {Object} options // XXX see leafnode
 * @param {Function} cb
 *
 * Supported calling forms:
 *   remove(query, cb)
 *   remove(query, options, cb)
 */
Collection.prototype.remove = function() {
  var query = {}
  var options = {}
  var cb = undefined

  if (arguments.length === 2) {
    query = arguments[0]
    cb = arguments[1]
  } else if (arguments.length === 3) {
    query = arguments[0]
    options = arguments[1]
    cb = arguments[2]
  } else {
    throw new Error("Must supply a query and a cb.")
  }

  return this.endpoint.delete(query, {}, function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/******************************************************************************
 * @class CollectionCursor
 * @constructor
 *
 * A CollectionCursor is a cursor object returned by Collection.find() method and used for iterating over
 * results from find()
 *
 */
function CollectionCursor(collection, findArguments) {
  this.collection = collection
  this.findArguments = parseFindArgs(findArguments)

  this.limit = 100
  this.skip = 0
  this.items = null
  this.nextItemPos = null
  this.done = false
}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.each = function(cb) {
  if (this.done) {
    // NOOP
    return
  }

  var self = this

  function callForeachItem(e) {
    if (self.done) {
      // NOOP
    } else if (e) {
      cb(e)
    } else {
      while (self.nextItemPos in self.items) {
        self._nextObject(cb)
      }

      if (self._needToGetMore()){
        self.getMore(callForeachItem)
      }
    }

  }

  if (this._needToGetMore()) {
    this.getMore(callForeachItem)
  } else {
    callForeachItem()
  }
}

CollectionCursor.prototype.toArray = function(cb) {
  if (this.done) {
    cb()
    return
  }
  this.limit = null
  this.skip = null

  var self = this
  this.getMore(function(e) {
    cb(e, self.items)
    self.done = true
  })

}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.next = function(cb) {

  if (this.done) {
    cb(null, null)
    return
  }

  var self = this

  function nextCallback(e) {
    if (self.done) {
      cb(null, null)
    } else if (e) {
      cb(e)
    } else {
      self._nextObject(cb)
    }

  }

  if (this._needToGetMore()) {
    this.getMore(nextCallback)
  } else {
    this._nextObject(nextCallback)
  }

}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.getMore = function(cb) {

  if ( this.done ) {
    cb()
  }
  var options = {
    skip: this.skip * this.limit,
    limit: this.limit
  }

  var self = this
  this.collection._doFind(this.findArguments.query, options, function(e, data) {
    if (data && data.length > 0) {
      self.skip++
      self.items = data
      self.nextItemPos = 0
      cb()
    } else if (e) {
      cb(e)
    } else {
      self.done = true
      cb()
    }
  })

}

/**********************************************************************
 *
 * @param cb
 * @private
 */
CollectionCursor.prototype._nextObject = function(cb) {
  if (this.items && this.nextItemPos in this.items) {
    var item = this.items[this.nextItemPos]
    this.nextItemPos++
    cb(null, item)
  }
}

/**********************************************************************
 *
 * @returns {boolean}
 * @private
 */
CollectionCursor.prototype._needToGetMore = function() {
  return !this.done && (this.items == null || !(this.nextItemPos in this.items))
}

/**********************************************************************
 *
 * @param cb
 * @private
 */
CollectionCursor.prototype._doMoveEach = function(cb) {
  if (this.items) {
    while (this.nextItemPos in this.items) {
      var item = this.items[this.nextItemPos]
      this.nextItemPos++
      cb(null, item)
    }
      this.items = null
      this.getMore(cb)
  }

}

/**********************************************************************
 *
 * @param findArgs
 * @returns {{query: undefined, options: {}, cb: undefined}}
 */
function parseFindArgs(findArgs) {
  var query = undefined
  var options = {}

  if (arguments.length === 1) {
    query = arguments[0]
  } else if (arguments.length === 2) {
    query = arguments[0]
    options = arguments[1]
  }

  return {
    query: query,
    options: options
  }
}

Collection.collectionCursorClass = CollectionCursor

/**********************************************************************
 * exports
 */
module.exports = Collection

<commit_msg>make toArray() continue from current position<commit_after>var request = require('request')
var Endpoint = require('./Endpoint')
var HttpErrors = require('http-errors')

/******************************************************************************
 * @class Collection
 * @constructor
 *
 * A Collection is a wrapper around an Endpoint that exposes a higher-level
 * set of methods that abstracts the endpoint as a resource collection.
 *
 */
function Collection(endpoint) {
  this.endpoint = endpoint
}

/**********************************************************************
 * find
 *
 * @param {Object} query (optional)
 * @param {Object} options (optional)
 *
 * Supported calling forms:
 *   find()
 *   find(query)
 *   find(query, options)
 *
 * @return CollectionCursor
 */
Collection.prototype.find = function() {
  return new CollectionCursor(this, arguments)
}

/**********************************************************************
 * _doFind
 *
 * @param {Object} query (optional)
 * @param {Object} options (optional)
 * @param {Function} cb
 *
 * Supported calling forms:
 *   find(cb)
 *   find(query, cb)
 *   find(query, options, cb)
 */
Collection.prototype._doFind = function() {
  var query = undefined
  var options = {}
  var cb = undefined

  if (arguments.length === 1) {
    cb = arguments[0]
  } else if (arguments.length === 2) {
    query = arguments[0]
    cb = arguments[1]
  } else if (arguments.length === 3) {
    query = arguments[0]
    options = arguments[1]
    cb = arguments[2]
  }

  // Create proper get query parameters
  var params = {}
  if (query) {
    params.query = query
  }
  if (options.sort) {
    params.sort = options.sort
  }
  if (options.fields) {
    params.fields = options.fields
  }
  if (options.skip) {
    params.skip = options.skip
  }
  if (options.limit) {
    params.limit = options.limit
  }

  return this.endpoint.get({ params: params }, function(err, res) {
//  return this.endpoint.get({}, function(err, res) {
//  return this.endpoint.get(function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res ? res.body : null)
  })
}

/**********************************************************************
 * insert
 *
 * @param {Object} obj // XXX do we support arrays?
 * @param {Function} cb
 *
 * Supported calling forms:
 *   insert(obj, cb)
 */
Collection.prototype.insert = function(obj, cb) {
  return this.endpoint.post(obj, {}, function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/**********************************************************************
 * update
 *
 * @param {Object} query
 * @param {Object} obj
 * @param {Object} options // XXX see leafnode
 * @param {Function} cb
 *
 * Supported calling forms:
 *   update(query, obj, cb)
 *   update(query, obj, options, cb)
 */
Collection.prototype.update = function() {
  var query = {}
  var obj = undefined
  var options = {}
  var cb = undefined

  if (arguments.length === 3) {
    query = arguments[0]
    obj = arguments[1]
    cb = arguments[2]
  } else if (arguments.length === 3) {
    query = arguments[0]
    obj = arguments[1]
    options = arguments[2]
    cb = arguments[3]
  } else {
    throw new Error("Must supply a query and an obj.")
  }

  return this.endpoint.put(query, {}, function(err, res) { // XXX put or patch depending on obj? Hmmm?
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/**********************************************************************
 * remove
 *
 * @param {Object} query
 * @param {Object} options // XXX see leafnode
 * @param {Function} cb
 *
 * Supported calling forms:
 *   remove(query, cb)
 *   remove(query, options, cb)
 */
Collection.prototype.remove = function() {
  var query = {}
  var options = {}
  var cb = undefined

  if (arguments.length === 2) {
    query = arguments[0]
    cb = arguments[1]
  } else if (arguments.length === 3) {
    query = arguments[0]
    options = arguments[1]
    cb = arguments[2]
  } else {
    throw new Error("Must supply a query and a cb.")
  }

  return this.endpoint.delete(query, {}, function(err, res) {
    // XXX using same error for now but may want to abstract away HttpError
    cb(err, res.body)
  })
}

/******************************************************************************
 * @class CollectionCursor
 * @constructor
 *
 * A CollectionCursor is a cursor object returned by Collection.find() method and used for iterating over
 * results from find()
 *
 */
function CollectionCursor(collection, findArguments) {
  this.collection = collection
  this.findArguments = parseFindArgs(findArguments)

  this.limit = 100
  this.skip = 0
  this.items = null
  this.nextItemPos = null
  this.done = false
}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.each = function(cb) {
  if (this.done) {
    // NOOP
    return
  }

  var self = this

  function callForeachItem(e) {
    if (self.done) {
      // NOOP
    } else if (e) {
      cb(e)
    } else {
      while (self.nextItemPos in self.items) {
        self._nextObject(cb)
      }

      if (self._needToGetMore()){
        self.getMore(callForeachItem)
      }
    }

  }

  if (this._needToGetMore()) {
    this.getMore(callForeachItem)
  } else {
    callForeachItem()
  }
}

CollectionCursor.prototype.toArray = function(cb) {
  if (this.done) {
    cb()
    return
  }
  this.limit = null

  var self = this
  this.getMore(function(e) {
    cb(e, self.items)
    self.done = true
  })

}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.next = function(cb) {

  if (this.done) {
    cb(null, null)
    return
  }

  var self = this

  function nextCallback(e) {
    if (self.done) {
      cb(null, null)
    } else if (e) {
      cb(e)
    } else {
      self._nextObject(cb)
    }

  }

  if (this._needToGetMore()) {
    this.getMore(nextCallback)
  } else {
    this._nextObject(nextCallback)
  }

}

/**********************************************************************
 *
 * @param cb
 */
CollectionCursor.prototype.getMore = function(cb) {

  if ( this.done ) {
    cb()
  }
  var options = {
    skip: this.skip * this.limit,
    limit: this.limit
  }

  var self = this
  this.collection._doFind(this.findArguments.query, options, function(e, data) {
    if (data && data.length > 0) {
      self.skip++
      self.items = data
      self.nextItemPos = 0
      cb()
    } else if (e) {
      cb(e)
    } else {
      self.done = true
      cb()
    }
  })

}

/**********************************************************************
 *
 * @param cb
 * @private
 */
CollectionCursor.prototype._nextObject = function(cb) {
  if (this.items && this.nextItemPos in this.items) {
    var item = this.items[this.nextItemPos]
    this.nextItemPos++
    cb(null, item)
  }
}

/**********************************************************************
 *
 * @returns {boolean}
 * @private
 */
CollectionCursor.prototype._needToGetMore = function() {
  return !this.done && (this.items == null || !(this.nextItemPos in this.items))
}

/**********************************************************************
 *
 * @param cb
 * @private
 */
CollectionCursor.prototype._doMoveEach = function(cb) {
  if (this.items) {
    while (this.nextItemPos in this.items) {
      var item = this.items[this.nextItemPos]
      this.nextItemPos++
      cb(null, item)
    }
      this.items = null
      this.getMore(cb)
  }

}

/**********************************************************************
 *
 * @param findArgs
 * @returns {{query: undefined, options: {}, cb: undefined}}
 */
function parseFindArgs(findArgs) {
  var query = undefined
  var options = {}

  if (arguments.length === 1) {
    query = arguments[0]
  } else if (arguments.length === 2) {
    query = arguments[0]
    options = arguments[1]
  }

  return {
    query: query,
    options: options
  }
}

Collection.collectionCursorClass = CollectionCursor

/**********************************************************************
 * exports
 */
module.exports = Collection

<|endoftext|>


<commit_before>const LINE_CHANNEL_ACCESS_TOKEN = '{your token}';

var request = require('request');

//--------

var Reply = function(replyToken) {
    this.replyToken = replyToken;
    this.messages = [];
};

Reply.prototype.url = 'https://api.line.me/v2/bot/message/reply';

Reply.prototype.method = 'POST';

Reply.prototype.headers = {
    'Content-type': 'application/json',
    'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
};

Reply.prototype.addMessage = function(message) {
    this.messages.push(message);
}

Reply.prototype.execute = function() {
    // TODO: エラーハンドリング
    request({
        url: this.url,
        method: this.method,
        headers: this.headers,
        body: {
            replyToken: this.replyToken,
            messages: this.messages
        },
        json: true
    });
};

module.exports = class bbbot {
    static date(replyToken) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: 'こんにちは！会議室を取るよ！'
        });
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: 'いつ？',
                actions: [
                    {
                        type: 'postback',
                        label: '今日',
                        text: '今日',
                        data: "{ \"date\": \"today\" }"
                    },
                    {
                        type: 'postback',
                        label: '明日',
                        text: '明日',
                        data: "{ \"date\": \"tomorrow\" }"
                    },
                    {
                        type: 'postback',
                        label: 'あさって',
                        text: 'あさって',
                        data: "{ \"date\": \"after_tomorrow\" }"
                    },
                    {
                        type: 'postback',
                        label: 'それ以外',
                        text: 'それ以外',
                        data: "{ \"date\": \"none_of_them\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static time(replyToken, botMemory) {
        var date;
        if (botMemory.date == 'today') {
            date = '今日';
        } else if (botMemory.date == 'tomorrow') {
            date = 'あした';
        } else if (botMemory.date == 'after_tomorrow') {
            date = 'あさって';
        }
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: date + 'だね！'
        });
        ask.addMessage({
            type: 'text',
            text: 'なんじから？'
        });
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'carousel',
                text: '何時？',
                columns: [
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/morning.jpg',
                        text: '朝',
                        actions: [
                            {
                                type: 'postback',
                                text: '9時',
                                label: '9時',
                                data: "{ \"time\": 9 }"
                            },
                            {
                                type: 'postback',
                                text: '10時',
                                label: '10時',
                                data: "{ \"time\": 10 }"
                            },
                            {
                                type: 'postback',
                                text: '11時',
                                label: '11時',
                                data: "{ \"time\": 11 }"
                            }
                        ]
                    },
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/daytime.jpg',
                        text: '昼',
                        actions: [
                            {
                                type: 'postback',
                                text: '13時',
                                label: '13時',
                                data: "{ \"time\": 13 }"
                            },
                            {
                                type: 'postback',
                                text: '14時',
                                label: '14時',
                                data: "{ \"time\": 14 }"
                            },
                            {
                                type: 'postback',
                                text: '15時',
                                label: '15時',
                                data: "{ \"time\": 15 }"
                            }
                        ]
                    },
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/evening.jpg',
                        text: '夜',
                        actions: [
                            {
                                type: 'postback',
                                text: '16時',
                                label: '16時',
                                data: "{ \"time\": 16 }"
                            },
                            {
                                type: 'postback',
                                text: '17時',
                                label: '17時',
                                data: "{ \"time\": 17 }"
                            },
                            {
                                type: 'postback',
                                text: 'それ以外',
                                label: 'それ以外',
                                data: "{ \"time\": \"none_of_them\" }"
                            }
                        ]
                    }
                ]
            }
        });
        ask.execute();
    }

    static turnout(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: botMemory.time + '時だね！'
        });
        ask.addMessage({
            type: 'text',
            text: 'なんにん？'
        });
        ask.execute();
    }

    static duration(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: botMemory.turnout + '人だね！'
        });
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: 'なんぷん？',
                actions: [
                    {
                        type: 'postback',
                        label: '30分',
                        text: '30分',
                        data: "{ \"duration\": 30 }"
                    },
                    {
                        type: 'postback',
                        label: '60分',
                        text: '60分',
                        data: "{ \"duration\": 60 }"
                    },
                    {
                        type: 'postback',
                        label: 'それ以外',
                        text: 'それ以外',
                        data: "{ \"duration\": \"none_of_them\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static room(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: botMemory.duration + '分だね！'
        });
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: '開いている部屋はこれだよ。どれがいい？',
                actions: [
                    {
                        type: 'postback',
                        label: '15M1',
                        text: '15M1',
                        data: "{ \"room\": \"15M1\" }"
                    },
                    {
                        type: 'postback',
                        label: '17M2',
                        text: '17M2',
                        data: "{ \"room\": \"17M2\" }"
                    },
                    {
                        type: 'postback',
                        label: '19M3',
                        text: '19M3',
                        data: "{ \"room\": \"19M3\" }"
                    },
                    {
                        type: 'postback',
                        label: '20M6',
                        text: '20M6',
                        data: "{ \"room\": \"20M6\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static title(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: 'OK! 予約しちゃうよ。'
        });
        ask.addMessage({
            type: 'text',
            text: 'なんて名前で予約する？'
        });
        ask.execute();
    }

    static result(replyToken, botMemory) {
        var date;
        if (botMemory.date == 'today') {
            date = new Date();
        } else if (botMemory.date == 'tomorrow') {
            date = new Date((new Date().getTime()) + (60 * 60 * 24 * 1000));
        } else if (botMemory.date == 'after_tomorrow') {
            date = new Date((new Date().getTime()) + (60 * 60 * 24 * 1000 * 2));
        }
        var dateString = (date.getMonth() + 1) + '/' + date.getDate();

        var begin = botMemory.time + ':00';
        var end;
        if (botMemory.duration == 30) {
            end = botMemory.time + ':30';
        } else if (botMemory.duration == 60) {
            end = botMemory.time + 1 + ':00';
        }
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: 'OK! 予約できたよ！'
        });
        ask.addMessage({
            type: 'text',
            text: '名前: ' + botMemory.title + '\n' + '日時: ' + dateString + ' ' + begin + '～' + end + '\n' + '場所: ' + botMemory.room
        });
        ask.execute();
    }

    static abandon(replyToken) {
        var ask = new Reply(replyToken);
        ask.addMessage({
            type: 'text',
            text: 'ちょっと僕にはむずかしいな・・・'
        });
        ask.addMessage({
            type: 'text',
            text: '近くのアドミさんに相談してみてね！'
        });
        ask.execute();
    }
}
<commit_msg>Minor refactoring<commit_after>const LINE_CHANNEL_ACCESS_TOKEN = '{your token}';

var request = require('request');

//--------

var Reply = function(replyToken) {
    this.replyToken = replyToken;
    this.messages = [];
};

Reply.prototype.url = 'https://api.line.me/v2/bot/message/reply';

Reply.prototype.method = 'POST';

Reply.prototype.headers = {
    'Content-type': 'application/json',
    'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
};

Reply.prototype.addMessage = function(message) {
    this.messages.push(message);
}

Reply.prototype.addTextMessage = function(text) {
    var message = {
        type: 'text',
        text: text
    };
    this.addMessage(message);
}

Reply.prototype.execute = function() {
    // TODO: エラーハンドリング
    request({
        url: this.url,
        method: this.method,
        headers: this.headers,
        body: {
            replyToken: this.replyToken,
            messages: this.messages
        },
        json: true
    });
};

module.exports = class bbbot {
    static date(replyToken) {
        var ask = new Reply(replyToken);
        ask.addTextMessage('こんにちは！会議室を取るよ！');
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: 'いつ？',
                actions: [
                    {
                        type: 'postback',
                        label: '今日',
                        text: '今日',
                        data: "{ \"date\": \"today\" }"
                    },
                    {
                        type: 'postback',
                        label: '明日',
                        text: '明日',
                        data: "{ \"date\": \"tomorrow\" }"
                    },
                    {
                        type: 'postback',
                        label: 'あさって',
                        text: 'あさって',
                        data: "{ \"date\": \"after_tomorrow\" }"
                    },
                    {
                        type: 'postback',
                        label: 'それ以外',
                        text: 'それ以外',
                        data: "{ \"date\": \"none_of_them\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static time(replyToken, botMemory) {
        var date;
        if (botMemory.date == 'today') {
            date = '今日';
        } else if (botMemory.date == 'tomorrow') {
            date = 'あした';
        } else if (botMemory.date == 'after_tomorrow') {
            date = 'あさって';
        }
        var ask = new Reply(replyToken);
        ask.addTextMessage(date + 'だね！');
        ask.addTextMessage('なんじから？');
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'carousel',
                text: '何時？',
                columns: [
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/morning.jpg',
                        text: '朝',
                        actions: [
                            {
                                type: 'postback',
                                text: '9時',
                                label: '9時',
                                data: "{ \"time\": 9 }"
                            },
                            {
                                type: 'postback',
                                text: '10時',
                                label: '10時',
                                data: "{ \"time\": 10 }"
                            },
                            {
                                type: 'postback',
                                text: '11時',
                                label: '11時',
                                data: "{ \"time\": 11 }"
                            }
                        ]
                    },
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/daytime.jpg',
                        text: '昼',
                        actions: [
                            {
                                type: 'postback',
                                text: '13時',
                                label: '13時',
                                data: "{ \"time\": 13 }"
                            },
                            {
                                type: 'postback',
                                text: '14時',
                                label: '14時',
                                data: "{ \"time\": 14 }"
                            },
                            {
                                type: 'postback',
                                text: '15時',
                                label: '15時',
                                data: "{ \"time\": 15 }"
                            }
                        ]
                    },
                    {
                        thumbnailImageUrl: 'https://bbbot-a63225.apaas.us6.oraclecloud.com/images/evening.jpg',
                        text: '夜',
                        actions: [
                            {
                                type: 'postback',
                                text: '16時',
                                label: '16時',
                                data: "{ \"time\": 16 }"
                            },
                            {
                                type: 'postback',
                                text: '17時',
                                label: '17時',
                                data: "{ \"time\": 17 }"
                            },
                            {
                                type: 'postback',
                                text: 'それ以外',
                                label: 'それ以外',
                                data: "{ \"time\": \"none_of_them\" }"
                            }
                        ]
                    }
                ]
            }
        });
        ask.execute();
    }

    static turnout(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addTextMessage(botMemory.time + '時だね！');
        ask.addTextMessage('なんにん？');
        ask.execute();
    }

    static duration(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addTextMessage(botMemory.turnout + '人だね！');
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: 'なんぷん？',
                actions: [
                    {
                        type: 'postback',
                        label: '30分',
                        text: '30分',
                        data: "{ \"duration\": 30 }"
                    },
                    {
                        type: 'postback',
                        label: '60分',
                        text: '60分',
                        data: "{ \"duration\": 60 }"
                    },
                    {
                        type: 'postback',
                        label: 'それ以外',
                        text: 'それ以外',
                        data: "{ \"duration\": \"none_of_them\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static room(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addTextMessage(botMemory.duration + '分だね！');
        ask.addMessage({
            type: 'template',
            altText: 'スマートフォンでないと話せないんだよね…。',
            template: {
                type: 'buttons',
                text: '開いている部屋はこれだよ。どれがいい？',
                actions: [
                    {
                        type: 'postback',
                        label: '15M1',
                        text: '15M1',
                        data: "{ \"room\": \"15M1\" }"
                    },
                    {
                        type: 'postback',
                        label: '17M2',
                        text: '17M2',
                        data: "{ \"room\": \"17M2\" }"
                    },
                    {
                        type: 'postback',
                        label: '19M3',
                        text: '19M3',
                        data: "{ \"room\": \"19M3\" }"
                    },
                    {
                        type: 'postback',
                        label: '20M6',
                        text: '20M6',
                        data: "{ \"room\": \"20M6\" }"
                    }
                ]
            }
        });
        ask.execute();
    }

    static title(replyToken, botMemory) {
        var ask = new Reply(replyToken);
        ask.addTextMessage('OK! 予約しちゃうよ。');
        ask.addTextMessage('なんて名前で予約する？');
        ask.execute();
    }

    static result(replyToken, botMemory) {
        var date;
        if (botMemory.date == 'today') {
            date = new Date();
        } else if (botMemory.date == 'tomorrow') {
            date = new Date((new Date().getTime()) + (60 * 60 * 24 * 1000));
        } else if (botMemory.date == 'after_tomorrow') {
            date = new Date((new Date().getTime()) + (60 * 60 * 24 * 1000 * 2));
        }
        var dateString = (date.getMonth() + 1) + '/' + date.getDate();

        var begin = botMemory.time + ':00';
        var end;
        if (botMemory.duration == 30) {
            end = botMemory.time + ':30';
        } else if (botMemory.duration == 60) {
            end = botMemory.time + 1 + ':00';
        }
        var ask = new Reply(replyToken);
        ask.addTextMessage('OK! 予約できたよ！');
        ask.addTextMessage('名前: ' + botMemory.title + '\n' + '日時: ' + dateString + ' ' + begin + '～' + end + '\n' + '場所: ' + botMemory.room);
        ask.execute();
    }

    static abandon(replyToken) {
        var ask = new Reply(replyToken);
        ask.addTextMessage('ちょっと僕にはむずかしいな・・・');
        ask.addTextMessage('近くのアドミさんに相談してみてね！');
        ask.execute();
    }
}
<|endoftext|>


<commit_before>exports.QUERY_ERROR      = 1;
exports.NOT_FOUND        = 2;
exports.NOT_DEFINED      = 3;
exports.NO_SUPPORT       = 4;
exports.MISSING_CALLBACK = 5;
exports.PARAM_MISMATCH   = 6;

exports.CONNECTION_LOST  = 10;

// Deprecated, remove on next major release.
Object.defineProperty(exports, "PARAM_MISSMATCH", {
  enumerable: true, get: function () {
    console.log("PARAM_MISSMATCH spelling is deprecated. Use PARAM_MISMATCH instead");
    return exports.PARAM_MISMATCH;
  }
});

Object.defineProperty(exports, "generateError", {
	value: function (code, message, extra) {
		var err = new Error(message);
		err.code = code;

		if (extra) {
			for (var k in extra) {
				err[k] = extra[k];
			}
		}

		return err;
	},
	enumerable : false
});
<commit_msg>Adds ORM own type of errors (#455)<commit_after>exports.QUERY_ERROR      = 1;
exports.NOT_FOUND        = 2;
exports.NOT_DEFINED      = 3;
exports.NO_SUPPORT       = 4;
exports.MISSING_CALLBACK = 5;
exports.PARAM_MISMATCH   = 6;

exports.CONNECTION_LOST  = 10;

// Deprecated, remove on next major release.
Object.defineProperty(exports, "PARAM_MISSMATCH", {
  enumerable: true, get: function () {
    console.log("PARAM_MISSMATCH spelling is deprecated. Use PARAM_MISMATCH instead");
    return exports.PARAM_MISMATCH;
  }
});

Object.defineProperty(exports, "generateError", {
	value: function (code, message, extra) {
		var err = new ORMError(message);

		err.setCode(code);

		if (extra) {
			err.setExtra(extra);
		}

		return err;
	},
	enumerable : false
});

function ORMError(message) {
	this.message = message;
}

ORMError.prototype = new Error();
ORMError.prototype.constructor = ORMError;

ORMError.prototype.setCode = function (code) {
	this.code = code;
};

ORMError.prototype.setExtra = function (extra) {
	for (var k in extra) {
		this[k] = extra[k];
	}
};
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var range = require('./range');
var parser = require('./parser');
var XDate = require('./XDate');
var Set = require('./Set');

/** @type {Expression} */
module.exports = Expression;

/**
 * Field aliases
 * @type {Object}
 * @constant
 */
var ALIASES = {
    seconds: {},
    minutes: {},
    hours: {},
    daysOfMonth: {},
    months: {
        JAN: 1,
        FEB: 2,
        MAR: 3,
        APR: 4,
        MAY: 5,
        JUN: 6,
        JUL: 7,
        AUG: 8,
        SEP: 9,
        OCT: 10,
        NOV: 11,
        DEC: 12
    },
    daysOfWeek: {
        SUN: 0,
        MON: 1,
        TUE: 2,
        WED: 3,
        THU: 4,
        FRI: 5,
        SAT: 6
    }
};

/**
 * Field mappings
 * @type {Array}
 * @constant
 */
var FIELDS = ['seconds', 'minutes', 'hours', 'daysOfMonth', 'months', 'daysOfWeek'];

/**
 * Field defaults
 * @type {Array}
 * @constant
 */
var DEFAULTS = {
    seconds:     '*',
    minutes:     '*',
    hours:       '*',
    daysOfMonth: '*',
    months:      '*',
    daysOfWeek:  '*'
};

/**
 * Prefined intervals
 * @type {Object}
 * @constant
 */
var PRESETS = {
    hourly: '0 0 * * * ?',
    daily: '0 0 0 * * ?',
    weekly: '0 0 0 ? * *',
    monthly: '0 0 0 1 * ?'
};

/**
 * Fields constraints
 * @type {Object.<Array.<number>>}
 * @constant
 */
var CONSTRAINTS = {
    seconds:     [0, 59],
    minutes:     [0, 59],
    hours:       [0, 23],
    daysOfMonth: [1, 31],
    months:      [1, 12],
    daysOfWeek:  [0, 7]
};

/**
 * Days in months
 * @type {Array.<number>}
 * @constant
 */
var DAYS_IN_MONTH = [
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
];

/**
 * Expression class constructor
 * @constructor
 * @param {Array.<Array.<number>>} fields  Expression fields parsed values
 */
function Expression(fields) {
    /** @type {Array.<number>} */
    this.seconds = fields[0];
    /** @type {Array.<number>} */
    this.minutes = fields[1];
    /** @type {Array.<number>} */
    this.hours = fields[2];
    /** @type {Array.<number>} */
    this.daysOfMonth = fields[3];
    /** @type {Array.<number>} */
    this.months = fields[4];
    /** @type {Array.<number>} */
    this.daysOfWeek = fields[5];
}

/**
 * Set field value
 * @param {String} field
 * @param {(String|number)} value
 */
Expression.prototype.set = function (field, value) {
    if (typeof value === 'number') {
        return this.at(field, value);
    }

    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var parsedValue = parser.parseField(field, value, constraints[0], constraints[1], ALIASES[field]);
    this._setField(field, parsedValue);
};

/**
 * Set wildcard for field
 * @param field
 */
Expression.prototype.every = function (field) {
    return this.set(field, '*');
};

/**
 * Comma-separated values
 * @param {String} field
 * @param {Array.<number>} values
 */
Expression.prototype.and = function (field, values) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var fieldValue = parser.andSet(values, constraints[0], constraints[1]);
    this._setField(field, fieldValue);
};

/**
 * Set literal value
 * @param {String} field
 * @param {number} value
 */
Expression.prototype.at = function (field, value) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var literalValue = parser.literalSet(value, constraints[0], constraints[1], ALIASES[field]);
    this._setField(field, literalValue);
};

/**
 * Range field (start-end)
 * @param {String} field
 * @param {number} start
 * @param {number} end
 */
Expression.prototype.range = function (field, start, end) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var rangeValue = parser.rangeSet(start, end, constraints[0], constraints[1]);
    this._setField(field, rangeValue);
};

/**
 * Repeat field (start/step)
 * @param {String} field
 * @param {number} offset
 * @param {number} step
 */
Expression.prototype.repeat = function (field, offset, step) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var repeatValue = parser.repeatSet(offset, step, constraints[0], constraints[1]);
    this._setField(field, repeatValue);
};

/**
 * Set value for field
 * @param {String} field
 * @param {(Array.<number>|Set.<number>)} value
 * @private
 */
Expression.prototype._setField = function (field, value) {
    if (field === 'daysOfWeek' || field === 'daysOfMonth') {
        var sibling = field === 'daysOfWeek' ? 'daysOfMonth' : 'daysOfWeek';
        assert(value, 'Cannot directly set ? for ' + field + '. Instead, just set ' + sibling + ' to any valid value');

        // Since we are setting a valid value for 'field', we must unset its sibling's value (set to ?=null)
        this[sibling] = null;
    }

    // Set the value of the field anyway
    this[field] = value instanceof Set ? value.toSortedArray() : value;
};

/**
 * Find next suitable dates
 * @public
 * @param {Date} startDate Start date
 * @param {Number=} steps Numbers of steps to iterate
 * @return {Array.<Date>} The next future dates
 */
Expression.prototype.nextDates = function (startDate, steps) {
    var dates = [];
    while (--steps >= 0) {
        startDate = this.next(startDate);
        dates.push(startDate);
    }

    return dates;
};

/**
 * Find next suitable date
 * @public
 * @param {Date} currentDate Current date
 * @param {Date=} endDate Current date
 * @return {Date} The next future date
 */
Expression.prototype.next = function (currentDate, endDate) {
    /** @type XDate */
    currentDate = new XDate(currentDate !== undefined ? currentDate : new Date());
    /** @type XDate */
    endDate = endDate ? new XDate(endDate) : null;

    currentDate.addSecond();

    // Find matching schedule
    while (true) {
        // Validate timespan
        if (endDate && endDate.getTime() < currentDate.getTime()) {
            return null;
        }

        // Day of months and week matching:
        //
        // "The day of a command's execution can be specified by two fields --
        // day of months, and day of week.  If  both	 fields	 are  restricted  (ie,
        // aren't  *),  the command will be run when either field matches the cur-
        // rent time.  For example, "30 4 1,15 * 5" would cause a command to be
        // run at 4:30 am on the  1st and 15th of each months, plus every Friday."
        //
        // http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
        //
        var dayOfMonthConstraints = CONSTRAINTS.daysOfMonth;
        var monthConstraints = CONSTRAINTS.months;
        var dayOfWeekConstraints = CONSTRAINTS.daysOfWeek;

        var daysOfMonth = this.daysOfMonth || range(dayOfMonthConstraints[0], dayOfMonthConstraints[1]);
        var daysOfWeek = this.daysOfWeek || range(dayOfWeekConstraints[0], dayOfWeekConstraints[1]);

        var daysOfMonthMatch = _matchSchedule(currentDate.getDate(), daysOfMonth);
        var daysOfWeekMatch = _matchSchedule(currentDate.getDay(), daysOfWeek);

        var isDayOfMonthWildcardMatch = _isWildcard(daysOfMonth, dayOfMonthConstraints[0], dayOfMonthConstraints[1]);
        var isMonthWildcardMatch = _isWildcard(daysOfWeek, monthConstraints[0], monthConstraints[1]);
        var isDayOfWeekWildcardMatch = _isWildcard(daysOfWeek, dayOfWeekConstraints[0], dayOfWeekConstraints[1]);

        // Validate days in months if explicit value is given
        if (!isMonthWildcardMatch) {
            var currentYear = currentDate.getFullYear();
            var currentMonth = currentDate.getMonth() + 1;
            var previousMonth = currentMonth === 1 ? 11 : currentMonth - 1;
            var daysInPreviousMonth = DAYS_IN_MONTH[previousMonth - 1];
            var daysOfMonthRangeMax = daysOfMonth[daysOfMonth.length - 1];

            // Handle leap year
            var isLeap = currentYear % 4 === 0 && (currentYear % 100 !== 0 || currentYear % 400 === 0);
            if (isLeap) {
                daysInPreviousMonth = 29;
            }

            assert(this.months[0] !== previousMonth || daysInPreviousMonth >= daysOfMonthRangeMax, 'Invalid explicit day of months definition');
        }

        // Add day if not day of months is set (and no match) and day of week is wildcard
        if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !daysOfMonthMatch) {
            currentDate.addDay();
            continue;
        }

        // Add day if not day of week is set (and no match) and day of months is wildcard
        if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !daysOfWeekMatch) {
            currentDate.addDay();
            continue;
        }

        // Add day if day of month and week are non-wildcard values and both doesn't match
        if (!(isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch) && !daysOfMonthMatch && !daysOfWeekMatch) {
            currentDate.addDay();
            continue;
        }

        // Match months
        if (!_matchSchedule(currentDate.getMonth() + 1, this.months)) {
            currentDate.addMonth();
            continue;
        }

        // Match hours
        if (!_matchSchedule(currentDate.getHours(), this.hours)) {
            currentDate.addHour();
            continue;
        }

        // Match minutes
        if (!_matchSchedule(currentDate.getMinutes(), this.minutes)) {
            currentDate.addMinute();
            continue;
        }

        // Match seconds
        if (!_matchSchedule(currentDate.getSeconds(), this.seconds)) {
            currentDate.addSecond();
            continue;
        }

        break;
    }

    return currentDate.date();
};

/**
 * Check if next suitable date exists
 *
 * @public
 * @param {Date} startDate
 * @return {Boolean}
 */
Expression.prototype.hasNext = function (startDate) {
    return !!this.next(startDate);
};

/**
 * String representation of the expression
 * @returns {String}
 */
Expression.prototype.toString = function () {

    return _fieldToString('seconds', this.seconds) + ' '
        + _fieldToString('minutes', this.minutes) + ' '
        + _fieldToString('hours', this.hours) + ' '
        + _fieldToString('daysOfMonth', this.daysOfMonth) + ' '
        + _fieldToString('months', this.months) + ' '
        + _fieldToString('daysOfWeek', this.daysOfWeek);
};

/**
 * String representation of a field
 * @returns {String}
 */
Expression.prototype.fieldToString = function (field) {
    _validateField(field);

    return _fieldToString(field, this[field]);
};

/**
 * Parse input expression
 *
 * @public
 * @param {String} expression Input expression
 * @returns {Expression}
 */
Expression.parse = function (expression) {
    // Is input expression predefined?
    if (PRESETS[expression]) {
        expression = PRESETS[expression];
    }

    // Split fields
    var len = FIELDS.length;
    var atoms = expression.match(/\S+/g);
    assert(atoms.length === len, 'Invalid expression. Should contain exactly ' + len + ' atoms');

    // Resolve fields
    var fields = FIELDS.map(function (field, i) {
        var value = atoms[i] || DEFAULTS[field];
        var constraints = CONSTRAINTS[field];
        return parser.parseField(field, value, constraints[0], constraints[1], ALIASES[field]);
    });

    assert(fields[3] || fields[5], 'Must specify either daysOfMonth or daysOfWeek');
    assert((fields[3] && !fields[5]) || (!fields[3] && fields[5]), 'Cannot specify both daysOfMonth and daysOfWeek');

    return new Expression(fields);
};

/**
 * Set/get alias value
 * @param {String} field
 * @param {String} key
 * @param {number} [value]
 * @returns {(number|undefined)}
 */
Expression.alias = function (field, key, value) {
    _validateField(field);

    var aliases = ALIASES[field];
    var ikey = String(key).toUpperCase();

    if (value !== undefined) {
        aliases[ikey] = value;
        return;
    }

    return aliases[ikey];
};

/**
 * Validate field name
 * @param field
 * @private
 */
function _validateField(field) {
    assert(FIELDS.indexOf(field) !== -1, 'Invalid field ' + field);
}

/**
 * Match field value
 *
 * @param {number} value
 * @param {Array} sequence
 * @returns {Boolean}
 * @private
 */
function _matchSchedule(value, sequence) {
    var i, len = sequence.length;
    for (i = 0; i < len; i++) {
        if (sequence[i] >= value) {
            return sequence[i] === value;
        }
    }

    return sequence[0] === value;
}

/**
 * Stringify field
 * @param {String} field
 * @param {Array.<number>} values
 * @returns {String}
 * @private
 */
function _fieldToString(field, values) {
    var constraints = CONSTRAINTS[field];
    var minValue = constraints[0];
    var maxValue = constraints[1];

    if (!values) {
        return '?';
    }

     if (_isWildcard(values, minValue, maxValue)) {
         return '*';
     }

    return values.join(',');
}

/**
 * Detect if values array fully matches constraint bounds
 * @param {Array} values Input range
 * @param {number} minValue
 * @param {number} maxValue
 * @returns {Boolean}
 * @private
 */
function _isWildcard(values, minValue, maxValue) {
    return values.length > 0 && values.length === maxValue + (minValue < 1 ? 1 : 0);
}
<commit_msg>Minor fix<commit_after>'use strict';

var assert = require('assert');
var range = require('./range');
var parser = require('./parser');
var XDate = require('./XDate');
var Set = require('./Set');

/** @type {Expression} */
module.exports = Expression;

/**
 * Field aliases
 * @type {Object}
 * @constant
 */
var ALIASES = {
    seconds: {},
    minutes: {},
    hours: {},
    daysOfMonth: {},
    months: {
        JAN: 1,
        FEB: 2,
        MAR: 3,
        APR: 4,
        MAY: 5,
        JUN: 6,
        JUL: 7,
        AUG: 8,
        SEP: 9,
        OCT: 10,
        NOV: 11,
        DEC: 12
    },
    daysOfWeek: {
        SUN: 0,
        MON: 1,
        TUE: 2,
        WED: 3,
        THU: 4,
        FRI: 5,
        SAT: 6
    }
};

/**
 * Field mappings
 * @type {Array}
 * @constant
 */
var FIELDS = ['seconds', 'minutes', 'hours', 'daysOfMonth', 'months', 'daysOfWeek'];

/**
 * Field defaults
 * @type {Array}
 * @constant
 */
var DEFAULTS = {
    seconds:     '*',
    minutes:     '*',
    hours:       '*',
    daysOfMonth: '*',
    months:      '*',
    daysOfWeek:  '*'
};

/**
 * Prefined intervals
 * @type {Object}
 * @constant
 */
var PRESETS = {
    hourly: '0 0 * * * ?',
    daily: '0 0 0 * * ?',
    weekly: '0 0 0 ? * *',
    monthly: '0 0 0 1 * ?'
};

/**
 * Fields constraints
 * @type {Object.<Array.<number>>}
 * @constant
 */
var CONSTRAINTS = {
    seconds:     [0, 59],
    minutes:     [0, 59],
    hours:       [0, 23],
    daysOfMonth: [1, 31],
    months:      [1, 12],
    daysOfWeek:  [0, 7]
};

/**
 * Days in months
 * @type {Array.<number>}
 * @constant
 */
var DAYS_IN_MONTH = [
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
];

/**
 * Expression class constructor
 * @constructor
 * @param {Array.<Array.<number>>} fields  Expression fields parsed values
 */
function Expression(fields) {
    /** @type {Array.<number>} */
    this.seconds = fields[0];
    /** @type {Array.<number>} */
    this.minutes = fields[1];
    /** @type {Array.<number>} */
    this.hours = fields[2];
    /** @type {Array.<number>} */
    this.daysOfMonth = fields[3];
    /** @type {Array.<number>} */
    this.months = fields[4];
    /** @type {Array.<number>} */
    this.daysOfWeek = fields[5];
}

/**
 * Set field value
 * @param {String} field
 * @param {(String|number)} value
 */
Expression.prototype.set = function (field, value) {
    if (typeof value === 'number') {
        return this.at(field, value);
    }

    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var parsedValue = parser.parseField(field, value, constraints[0], constraints[1], ALIASES[field]);
    this._setField(field, parsedValue);
};

/**
 * Set wildcard for field
 * @param field
 */
Expression.prototype.every = function (field) {
    return this.set(field, '*');
};

/**
 * Comma-separated values
 * @param {String} field
 * @param {Array.<number>} values
 */
Expression.prototype.and = function (field, values) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var fieldValue = parser.andSet(values, constraints[0], constraints[1]);
    this._setField(field, fieldValue);
};

/**
 * Set literal value
 * @param {String} field
 * @param {number} value
 */
Expression.prototype.at = function (field, value) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var literalValue = parser.literalSet(value, constraints[0], constraints[1], ALIASES[field]);
    this._setField(field, literalValue);
};

/**
 * Range field (start-end)
 * @param {String} field
 * @param {number} start
 * @param {number} end
 */
Expression.prototype.range = function (field, start, end) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var rangeValue = parser.rangeSet(start, end, constraints[0], constraints[1]);
    this._setField(field, rangeValue);
};

/**
 * Repeat field (start/step)
 * @param {String} field
 * @param {number} offset
 * @param {number} step
 */
Expression.prototype.repeat = function (field, offset, step) {
    _validateField(field);

    var constraints = CONSTRAINTS[field];
    var repeatValue = parser.repeatSet(offset, step, constraints[0], constraints[1]);
    this._setField(field, repeatValue);
};

/**
 * Set value for field
 * @param {String} field
 * @param {(Array.<number>|Set.<number>)} value
 * @private
 */
Expression.prototype._setField = function (field, value) {
    if (field === 'daysOfWeek' || field === 'daysOfMonth') {
        var sibling = field === 'daysOfWeek' ? 'daysOfMonth' : 'daysOfWeek';
        assert(value, 'Cannot directly set ? for ' + field + '. Instead, just set ' + sibling + ' to any valid value');

        // Since we are setting a valid value for 'field', we must unset its sibling's value (set to ?=null)
        this[sibling] = null;
    }

    // Set the value of the field anyway
    this[field] = value instanceof Set ? value.toSortedArray() : value;
};

/**
 * Find next suitable dates
 * @public
 * @param {Date} startDate Start date
 * @param {Number=} steps Numbers of steps to iterate
 * @return {Array.<Date>} The next future dates
 */
Expression.prototype.nextDates = function (startDate, steps) {
    var dates = [];
    while (--steps >= 0) {
        startDate = this.next(startDate);
        dates.push(startDate);
    }

    return dates;
};

/**
 * Find next suitable date
 * @public
 * @param {Date} currentDate Current date
 * @param {Date=} endDate Current date
 * @return {Date} The next future date
 */
Expression.prototype.next = function (currentDate, endDate) {
    /** @type XDate */
    currentDate = new XDate(currentDate !== undefined ? currentDate : new Date());
    /** @type XDate */
    endDate = endDate ? new XDate(endDate) : null;

    currentDate.addSecond();

    // Find matching schedule
    while (true) {
        // Validate timespan
        if (endDate && endDate.getTime() < currentDate.getTime()) {
            return null;
        }

        // Day of months and week matching:
        //
        // "The day of a command's execution can be specified by two fields --
        // day of months, and day of week.  If  both	 fields	 are  restricted  (ie,
        // aren't  *),  the command will be run when either field matches the cur-
        // rent time.  For example, "30 4 1,15 * 5" would cause a command to be
        // run at 4:30 am on the  1st and 15th of each months, plus every Friday."
        //
        // http://unixhelp.ed.ac.uk/CGI/man-cgi?crontab+5
        //
        var dayOfMonthConstraints = CONSTRAINTS.daysOfMonth;
        var monthConstraints = CONSTRAINTS.months;
        var dayOfWeekConstraints = CONSTRAINTS.daysOfWeek;

        var daysOfMonth = this.daysOfMonth || range(dayOfMonthConstraints[0], dayOfMonthConstraints[1]);
        var daysOfWeek = this.daysOfWeek || range(dayOfWeekConstraints[0], dayOfWeekConstraints[1]);

        var daysOfMonthMatch = _matchSchedule(currentDate.getDate(), daysOfMonth);
        var daysOfWeekMatch = _matchSchedule(currentDate.getDay(), daysOfWeek);

        var isDayOfMonthWildcardMatch = _isWildcard(daysOfMonth, dayOfMonthConstraints[0], dayOfMonthConstraints[1]);
        var isMonthWildcardMatch = _isWildcard(daysOfWeek, monthConstraints[0], monthConstraints[1]);
        var isDayOfWeekWildcardMatch = _isWildcard(daysOfWeek, dayOfWeekConstraints[0], dayOfWeekConstraints[1]);

        // Validate days in months if explicit value is given
        if (!isMonthWildcardMatch) {
            var currentYear = currentDate.getFullYear();
            var currentMonth = currentDate.getMonth() + 1;
            var previousMonth = currentMonth === 1 ? 12 : currentMonth - 1;
            var daysInPreviousMonth = DAYS_IN_MONTH[previousMonth - 1];
            var daysOfMonthRangeMax = daysOfMonth[daysOfMonth.length - 1];

            // Handle leap year
            var isLeap = currentYear % 4 === 0 && (currentYear % 100 !== 0 || currentYear % 400 === 0);
            if (isLeap && previousMonth === 2) {
                daysInPreviousMonth = 29;
            }

            assert(this.months[0] !== previousMonth || daysInPreviousMonth >= daysOfMonthRangeMax, 'Invalid explicit day of months definition');
        }

        // Add day if not day of months is set (and no match) and day of week is wildcard
        if (!isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch && !daysOfMonthMatch) {
            currentDate.addDay();
            continue;
        }

        // Add day if not day of week is set (and no match) and day of months is wildcard
        if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && !daysOfWeekMatch) {
            currentDate.addDay();
            continue;
        }

        // Add day if day of month and week are non-wildcard values and both doesn't match
        if (!(isDayOfMonthWildcardMatch && isDayOfWeekWildcardMatch) && !daysOfMonthMatch && !daysOfWeekMatch) {
            currentDate.addDay();
            continue;
        }

        // Match months
        if (!_matchSchedule(currentDate.getMonth() + 1, this.months)) {
            currentDate.addMonth();
            continue;
        }

        // Match hours
        if (!_matchSchedule(currentDate.getHours(), this.hours)) {
            currentDate.addHour();
            continue;
        }

        // Match minutes
        if (!_matchSchedule(currentDate.getMinutes(), this.minutes)) {
            currentDate.addMinute();
            continue;
        }

        // Match seconds
        if (!_matchSchedule(currentDate.getSeconds(), this.seconds)) {
            currentDate.addSecond();
            continue;
        }

        break;
    }

    return currentDate.date();
};

/**
 * Check if next suitable date exists
 *
 * @public
 * @param {Date} startDate
 * @return {Boolean}
 */
Expression.prototype.hasNext = function (startDate) {
    return !!this.next(startDate);
};

/**
 * String representation of the expression
 * @returns {String}
 */
Expression.prototype.toString = function () {

    return _fieldToString('seconds', this.seconds) + ' '
        + _fieldToString('minutes', this.minutes) + ' '
        + _fieldToString('hours', this.hours) + ' '
        + _fieldToString('daysOfMonth', this.daysOfMonth) + ' '
        + _fieldToString('months', this.months) + ' '
        + _fieldToString('daysOfWeek', this.daysOfWeek);
};

/**
 * String representation of a field
 * @returns {String}
 */
Expression.prototype.fieldToString = function (field) {
    _validateField(field);

    return _fieldToString(field, this[field]);
};

/**
 * Parse input expression
 *
 * @public
 * @param {String} expression Input expression
 * @returns {Expression}
 */
Expression.parse = function (expression) {
    // Is input expression predefined?
    if (PRESETS[expression]) {
        expression = PRESETS[expression];
    }

    // Split fields
    var len = FIELDS.length;
    var atoms = expression.match(/\S+/g);
    assert(atoms.length === len, 'Invalid expression. Should contain exactly ' + len + ' atoms');

    // Resolve fields
    var fields = FIELDS.map(function (field, i) {
        var value = atoms[i] || DEFAULTS[field];
        var constraints = CONSTRAINTS[field];
        return parser.parseField(field, value, constraints[0], constraints[1], ALIASES[field]);
    });

    assert(fields[3] || fields[5], 'Must specify either daysOfMonth or daysOfWeek');
    assert((fields[3] && !fields[5]) || (!fields[3] && fields[5]), 'Cannot specify both daysOfMonth and daysOfWeek');

    return new Expression(fields);
};

/**
 * Set/get alias value
 * @param {String} field
 * @param {String} key
 * @param {number} [value]
 * @returns {(number|undefined)}
 */
Expression.alias = function (field, key, value) {
    _validateField(field);

    var aliases = ALIASES[field];
    var ikey = String(key).toUpperCase();

    if (value !== undefined) {
        aliases[ikey] = value;
        return;
    }

    return aliases[ikey];
};

/**
 * Validate field name
 * @param field
 * @private
 */
function _validateField(field) {
    assert(FIELDS.indexOf(field) !== -1, 'Invalid field ' + field);
}

/**
 * Match field value
 *
 * @param {number} value
 * @param {Array} sequence
 * @returns {Boolean}
 * @private
 */
function _matchSchedule(value, sequence) {
    var i, len = sequence.length;
    for (i = 0; i < len; i++) {
        if (sequence[i] >= value) {
            return sequence[i] === value;
        }
    }

    return sequence[0] === value;
}

/**
 * Stringify field
 * @param {String} field
 * @param {Array.<number>} values
 * @returns {String}
 * @private
 */
function _fieldToString(field, values) {
    var constraints = CONSTRAINTS[field];
    var minValue = constraints[0];
    var maxValue = constraints[1];

    if (!values) {
        return '?';
    }

     if (_isWildcard(values, minValue, maxValue)) {
         return '*';
     }

    return values.join(',');
}

/**
 * Detect if values array fully matches constraint bounds
 * @param {Array} values Input range
 * @param {number} minValue
 * @param {number} maxValue
 * @returns {Boolean}
 * @private
 */
function _isWildcard(values, minValue, maxValue) {
    return values.length > 0 && values.length === maxValue + (minValue < 1 ? 1 : 0);
}
<|endoftext|>


<commit_before>var http = require('http');

/*global console*/

function HttpSocket(options) {
    options = options || {};

    this._hostname = options.host || 'localhost';
    this._port = options.port || 8125;
    this._debug = options.debug || false;
    this._socketTimeoutMsec = 'socketTimeout' in options ? options.socketTimeout : 1000;

    // Check https://github.com/etsy/statsd/blob/master/docs/metric_types.md#multi-metric-packets for advisable sizes.
    this._maxBufferSize = 'maxBufferSize' in options ? options.maxBufferSize : 1200;

    // Set up re-usable socket
    this._socketTimer = undefined; // Reference to check-timer
    this._buffer = "";
}

HttpSocket.prototype.log = function (messages) {
    //console.log.apply(null, arguments);
};

/* Checks if there is anything in it's buffer that need to be sent. If it is
 * non-empty, it will be flushed.
 */
HttpSocket.prototype._socketTimeout = function () {
    this.log("_socketTimeout()");
    // Flush the buffer, if it contain anything.
    if (this._buffer.length > 0) {
        this._flushBuffer();
        return;
    }
};

/*
 * Flush all current data and stop any timers running.
 */
HttpSocket.prototype.close = function () {
    this.log("close()");
    if (this._buffer.length > 0) {
        this._flushBuffer();
    }

    // Cancel the running timer
    if (this._socketTimer) {
        clearInterval(this._socketTimer);
        this._socketTimer = undefined;
    }

    // Wait a tick or two, so any remaining stats can be sent.
    setTimeout(this.kill.bind(this), 10);
};

/* Kill the socket RIGHT NOW.
 */
HttpSocket.prototype.kill = function () {
    this.log("kill()");

    // Clear the timer and catch any further errors silently
    if (this._socketTimer) {
        clearInterval(this._socketTimer);
        this._socketTimer = undefined;
    }
};

/* Buffer management
 */
HttpSocket.prototype._enqueue = function (data) {
    this.log("_enqueue(", data, ")");

    if (!this._socketTimer) {
        this._socketTimer = setInterval(this._socketTimeout.bind(this), this._socketTimeoutMsec);
    }

    // Empty buffer if it's too full
    if (this._buffer.length + data.length > this._maxBufferSize) {
        this._flushBuffer();
    }

    if (this._buffer.length === 0) {
        this._buffer = data;
    } else {
        this._buffer += "\n" + data;
    }
};

HttpSocket.prototype._flushBuffer = function () {
    this.log("_flushBuffer() →", this._buffer);
    this._send(this._buffer);
    this._buffer = "";
};

/* Send data - public interface.
 */
HttpSocket.prototype.send = function (data) {
    this.log("send(", data, ")");
    if (this._maxBufferSize === 0) {
        return this._send(data);
    } else {
        this._enqueue(data);
    }
};

/*
 * Send data.
 */
HttpSocket.prototype._send = function (data) {
    this.log("_send(", data, ")");
    // If we don't have a socket, or we have created one but it isn't
    // ready yet, we need to enqueue data to send once the socket is ready.
    var that = this;

    var req = http.request({
        method: 'PUT',
        port: this._port,
        hostname: this._hostname
    });

    // Catch but ignore errors
    req.on('error', function () {});

    // Send data
    req.end(data);

    if (that._debug) { console.warn(data); }
};

module.exports = HttpSocket;
<commit_msg>HttpSocket: Increase buffer size + allow setting HTTP path and headers.<commit_after>var http = require('http');

/*global console*/

function HttpSocket(options) {
    options = options || {};

    this._hostname = options.host || 'localhost';
    this._port = options.port || 8125;
    this._debug = options.debug || false;
    this._socketTimeoutMsec = 'socketTimeout' in options ? options.socketTimeout : 1000;

    // HTTP stuffs
    this._headers = options.headers || {};
    this._path = options.path || '/';

    this._maxBufferSize = 'maxBufferSize' in options ? options.maxBufferSize : 10000;

    // Set up re-usable socket
    this._socketTimer = undefined; // Reference to check-timer
    this._buffer = "";
}

HttpSocket.prototype.log = function (messages) {
    //console.log.apply(null, arguments);
};

/* Checks if there is anything in it's buffer that need to be sent. If it is
 * non-empty, it will be flushed.
 */
HttpSocket.prototype._socketTimeout = function () {
    this.log("_socketTimeout()");
    // Flush the buffer, if it contain anything.
    if (this._buffer.length > 0) {
        this._flushBuffer();
        return;
    }
};

/*
 * Flush all current data and stop any timers running.
 */
HttpSocket.prototype.close = function () {
    this.log("close()");
    if (this._buffer.length > 0) {
        this._flushBuffer();
    }

    // Cancel the running timer
    if (this._socketTimer) {
        clearInterval(this._socketTimer);
        this._socketTimer = undefined;
    }

    // Wait a tick or two, so any remaining stats can be sent.
    setTimeout(this.kill.bind(this), 10);
};

/* Kill the socket RIGHT NOW.
 */
HttpSocket.prototype.kill = function () {
    this.log("kill()");

    // Clear the timer and catch any further errors silently
    if (this._socketTimer) {
        clearInterval(this._socketTimer);
        this._socketTimer = undefined;
    }
};

/* Buffer management
 */
HttpSocket.prototype._enqueue = function (data) {
    this.log("_enqueue(", data, ")");

    if (!this._socketTimer) {
        this._socketTimer = setInterval(this._socketTimeout.bind(this), this._socketTimeoutMsec);
    }

    // Empty buffer if it's too full
    if (this._buffer.length + data.length > this._maxBufferSize) {
        this._flushBuffer();
    }

    if (this._buffer.length === 0) {
        this._buffer = data;
    } else {
        this._buffer += "\n" + data;
    }
};

HttpSocket.prototype._flushBuffer = function () {
    this.log("_flushBuffer() →", this._buffer);
    this._send(this._buffer);
    this._buffer = "";
};

/* Send data - public interface.
 */
HttpSocket.prototype.send = function (data) {
    this.log("send(", data, ")");
    if (this._maxBufferSize === 0) {
        return this._send(data);
    } else {
        this._enqueue(data);
    }
};

/*
 * Send data.
 */
HttpSocket.prototype._send = function (data) {
    this.log("_send(", data, ")");
    // If we don't have a socket, or we have created one but it isn't
    // ready yet, we need to enqueue data to send once the socket is ready.
    var that = this;

    var req = http.request({
        method: 'PUT',
        hostname: this._hostname,
        path: this._path,
        port: this._port,
        headers: this._headers
    });

    // Catch but ignore errors
    req.on('error', function () {});

    // Send data
    req.end(data);

    if (that._debug) { console.warn(data); }
};

module.exports = HttpSocket;
<|endoftext|>