<commit_before>import * as util from '../util';

/**
 *  Rule for validating a regular expression pattern.
 *
 *  @param rule The validation rule.
 *  @param value The value of the field on the source object.
 *  @param source The source object being validated.
 *  @param errors An array of errors that this rule may add
 *  validation errors to.
 *  @param options The validation options.
 *  @param options.messages The validation messages.
 */
function pattern(rule, value, source, errors, options) {
  if (rule.pattern) {
    if (rule.pattern instanceof RegExp) {
      // if a RegExp instance is passed, reset `lastIndex` in case its `global`
      // flag is accidentally set ` to `true`, which in a validation scenario
      // is not necessary and the result might be misleading
      rule.pattern.lastIndex = 0;
      if (!rule.pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    } else if (typeof rule.pattern === 'string') {
      const _pattern = new RegExp(rule.pattern);
      if (!_pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    }
  }
}

export default pattern;
<commit_msg>clean: remove unused chars<commit_after>import * as util from '../util';

/**
 *  Rule for validating a regular expression pattern.
 *
 *  @param rule The validation rule.
 *  @param value The value of the field on the source object.
 *  @param source The source object being validated.
 *  @param errors An array of errors that this rule may add
 *  validation errors to.
 *  @param options The validation options.
 *  @param options.messages The validation messages.
 */
function pattern(rule, value, source, errors, options) {
  if (rule.pattern) {
    if (rule.pattern instanceof RegExp) {
      // if a RegExp instance is passed, reset `lastIndex` in case its `global`
      // flag is accidentally set to `true`, which in a validation scenario
      // is not necessary and the result might be misleading
      rule.pattern.lastIndex = 0;
      if (!rule.pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    } else if (typeof rule.pattern === 'string') {
      const _pattern = new RegExp(rule.pattern);
      if (!_pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    }
  }
}

export default pattern;
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

var HELP_TEXT = `
I will respond to the following messages:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
slapp.message('help', ['mention', 'direct_message'], (msg) => {
  msg.say(HELP_TEXT)
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
  .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
    msg
      .say(`${text}, how are you?`)
      // sends next event from user to this route, passing along state
      .route('how-are-you', { greeting: text })
  })
  .route('how-are-you', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("Whoops, I'm still waiting to hear how you're doing.")
        .say('How are you?')
        .route('how-are-you', state)
    }

    // add their response to state
    state.status = text

    msg
      .say(`Ok then. What's your favorite color?`)
      .route('color', state)
  })
  .route('color', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("I'm eagerly awaiting to hear your favorite color.")
        .route('color', state)
    }

    // add their response to state
    state.color = text

    msg
      .say('Thanks for sharing.')
      .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
    // At this point, since we don't route anywhere, the "conversation" is over
  })

// Can use a regex as well
slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
  // You can provide a list of responses, and a random one will be chosen
  // You can also include slack emoji in your responses
  msg.say([
    "You're welcome :smile:",
    'You bet',
    ':+1: Of course',
    'Anytime :sun_with_face: :full_moon_with_face:'
  ])
})

// demonstrate returning an attachment...
slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
  msg.say({
    text: 'Check out this amazing attachment! :confetti_ball: ',
    attachments: [{
      text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
      title: 'Slapp Library - Open Source',
      image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
      title_link: 'https://beepboophq.com/',
      color: '#7CD197'
    }]
  })
})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_mention', 'direct_message'], (msg) => {
  // respond only 40% of the time
  if (Math.random() < 0.4) {
    msg.say([':wave:', ':pray:', ':raised_hands:'])
  }
})

// attach Slapp to express server
var server = slapp.attachToExpress(express())

slapp.message('SYNC ISSUE RESOLVED', (msg) => {
msg.say('Thank you!')})

slapp.message('SYNC TABLE ALERT', (msg) => {
  msg.say({
	text: '',
    attachments: [
        {
            text: 'I have detected an out of sync issue from Slackbot, who is working on it?',
            fallback: 'You are unable to choose a game',
            callback_id: 'sync_ack',
            color: '#3AA3E3',
            attachment_type: 'default',
            actions: [
                {
                    'name': 'acknowledge',
                    'text': 'Acknowledge',
                    'type': 'button',
                    'value': 'acknowledge'
                }
            ]
        }
    ]
} )
}, msg.meta.bot_id = null)
slapp.action('sync_ack', 'acknowledge', (msg, user) => {
  msg.respond(msg.body.response_url, `*Someone has acknowledged they are looking into the sync alert!*`)
})
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})
<commit_msg>reverting earlier commits<commit_after>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})

var HELP_TEXT = `
I will respond to the following messages:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
slapp.message('help', ['mention', 'direct_message'], (msg) => {
  msg.say(HELP_TEXT)
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
  .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
    msg
      .say(`${text}, how are you?`)
      // sends next event from user to this route, passing along state
      .route('how-are-you', { greeting: text })
  })
  .route('how-are-you', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("Whoops, I'm still waiting to hear how you're doing.")
        .say('How are you?')
        .route('how-are-you', state)
    }

    // add their response to state
    state.status = text

    msg
      .say(`Ok then. What's your favorite color?`)
      .route('color', state)
  })
  .route('color', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''

    // user may not have typed text as their next action, ask again and re-route
    if (!text) {
      return msg
        .say("I'm eagerly awaiting to hear your favorite color.")
        .route('color', state)
    }

    // add their response to state
    state.color = text

    msg
      .say('Thanks for sharing.')
      .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
    // At this point, since we don't route anywhere, the "conversation" is over
  })

// Can use a regex as well
slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
  // You can provide a list of responses, and a random one will be chosen
  // You can also include slack emoji in your responses
  msg.say([
    "You're welcome :smile:",
    'You bet',
    ':+1: Of course',
    'Anytime :sun_with_face: :full_moon_with_face:'
  ])
})

// demonstrate returning an attachment...
slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
  msg.say({
    text: 'Check out this amazing attachment! :confetti_ball: ',
    attachments: [{
      text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
      title: 'Slapp Library - Open Source',
      image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
      title_link: 'https://beepboophq.com/',
      color: '#7CD197'
    }]
  })
})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_mention', 'direct_message'], (msg) => {
  // respond only 40% of the time
  if (Math.random() < 0.4) {
    msg.say([':wave:', ':pray:', ':raised_hands:'])
  }
})

// attach Slapp to express server
var server = slapp.attachToExpress(express())

slapp.message('SYNC ISSUE RESOLVED', (msg) => {
msg.say('Thank you!')})

slapp.message('SYNC TABLE ALERT', (msg) => {
  msg.say({
	text: '',
    attachments: [
        {
            text: 'I have detected an out of sync issue from Slackbot, who is working on it?',
            fallback: 'You are unable to choose a game',
            callback_id: 'sync_ack',
            color: '#3AA3E3',
            attachment_type: 'default',
            actions: [
                {
                    'name': 'acknowledge',
                    'text': 'Acknowledge',
                    'type': 'button',
                    'value': 'acknowledge'
                }
            ]
        }
    ]
} )
})
slapp.action('sync_ack', 'acknowledge', (msg, user) => {
  msg.respond(msg.body.response_url, `*Someone has acknowledged they are looking into the sync alert!*`)
})
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})
<|endoftext|>


<commit_before>//Initilize Cocoen
document.addEventListener('DOMContentLoaded', function(){
	document.sliders = [];
	let cocoen = document.querySelectorAll('.cocoen');
	 for (let i = cocoen.length - 1; i > -1; i--)	{
			  document.sliders.push (new Cocoen(cocoen[i]));
	 }
});

var mouseTimeout,
		mouseDown = false,
    direction = "",
		oldx = 0,
		dragHandle = null,
		handleMouseMove = function (e) {
			slideDirection(e.pageX);
		},
		handleTouchMove = function (e) {
      var touch;
			try {
				touch = e.changedTouches[0]
			} catch(err) {
				touch = e.touches[0];
			}
			slideDirection(touch.pageX);
		},
		slideDirection = function (x_value) {
			if (x_value < oldx) {
				$(dragHandle)
					.addClass("dragging-left")
					.removeClass("dragging-right");
			} else if (x_value > oldx) {
        $(dragHandle)
					.addClass("dragging-right")
					.removeClass("dragging-left");
			}
			oldx = x_value;
			clearTimeout(mouseTimeout);
			mouseTimeout = setTimeout(function(){
				$(dragHandle)
					.removeClass("dragging-left dragging-right");
			}, 300);
	  }
document.onmousemove = function(){
	if(!dragHandle){
		return;
	}
}
$('.cocoen')
//Disable context menu on the element when right clicking
	.on('contextmenu', function () {
		return false;
	})
  .on('mousedown', function(e){
		dragHandle = e.currentTarget.getElementsByClassName("cocoen-drag")[0];
    switch (e.which) {
      case 1:
				setSlidingMode(true);
        document.addEventListener('mousemove', handleMouseMove);
        break;
      case 2:
          break;
      case 3:
        $(this)
          .find('img')
          .css({'transform': 'translateZ(0) scale('+ $(this).attr('data-scale') +')'})
          .addClass("mouseDown");
				setSlidingMode(true);
        document.addEventListener('mousemove', handleMouseMove);
        break;
      default:
    }
		mouseDown = true;
  })
  .on('mouseup', function(e){
    $(this)
      .find('img')
      .css({'transform': 'scale(1)'})
      .removeClass("mouseDown");
		mouseUpCommonHandler(e);

  })
  .on('mousemove', function(e){
    $(this)
      .find('img')
      .css({'transform-origin': ((e.pageX - $(this).offset().left) / $(this).width()) * 100 + '% ' + ((e.pageY - $(this).offset().top) / $(this).height()) * 100 +'%'});
  })
	.on('mouseenter', function (e) {
		let mouse_down = e.buttons;
		if (!mouse_down) {
			dragHandle = null;
		}
	})
  .on('mouseleave', function(e){
    $(this)
      .find('img')
      .css({'transform': 'scale(1)'})
      .removeClass("mouseDown");
  });

//Removes opacity when mouseup is outside the element
document.addEventListener('mouseup', function(e){
	if(mouseDown) {
		mouseUpCommonHandler(e);
	}
});

var mouseUpCommonHandler = function (e) {
	setSlidingMode(false);
	mouseDown = false;
	document.removeEventListener("mousemove",handleMouseMove);
};

//Mobile Zoom pan
sliderComponent = document.getElementsByClassName("cocoen");
for(var i = 0; i < sliderComponent.length; i++){
	sliderComponent[i].addEventListener("touchstart", tapStart);
	sliderComponent[i].addEventListener("touchmove", handleTouchMove);
	sliderComponent[i].addEventListener("touchend", tapEnd);
	sliderComponent[i].addEventListener('orientationchange', doOnOrientationChange);
}

var tappedTwice = false;
function tapStart(e) {
	dragHandle = e.currentTarget.getElementsByClassName("cocoen-drag")[0];
	setSlidingMode(true);
  if(!tappedTwice) {
      tappedTwice = true;
      setTimeout( function() { tappedTwice = false; }, 300 );
      return false;
  }
  e.preventDefault();
  //action on double tap goes below
	$(this)
		.find('img')
		.css({'transform': 'translateZ(0) scale('+ $(this).attr('data-scale') +')'})
		.addClass("mouseDown");
}

function tapEnd() {
	setSlidingMode(false);
}

function setSlidingMode(sliding) {
	var dragOpacity = (sliding) ? 0.6 : 1;
	$(dragHandle)
		.css('opacity', dragOpacity);
}

function doOnOrientationChange() {
	setTimeout(function() {
		document.slider.dimensions(); //Fixes an issue with the drag not getting updated with the proper dimensions
	}, 500);
}

/*
The MIT License (MIT)
Copyright (c) 2015 Koen Romers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

! function(e) {
    if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();
    else if ("function" == typeof define && define.amd) define([], e);
    else {
        var t;
        t = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this, t.Cocoen = e()
    }
}(function() {
    return function e(t, n, i) {
        function s(o, a) {
            if (!n[o]) {
                if (!t[o]) {
                    var l = "function" == typeof require && require;
                    if (!a && l) return l(o, !0);
                    if (r) return r(o, !0);
                    var d = new Error("Cannot find module '" + o + "'");
                    throw d.code = "MODULE_NOT_FOUND", d
                }
                var h = n[o] = {
                    exports: {}
                };
                t[o][0].call(h.exports, function(e) {
                    var n = t[o][1][e];
                    return s(n ? n : e)
                }, h, h.exports, e, t, n, i)
            }
            return n[o].exports
        }
        for (var r = "function" == typeof require && require, o = 0; o < i.length; o++) s(i[o]);
        return s
    }({
        1: [function(e, t, n) {
            "use strict";

            function i(e, t) {
                if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function")
            }
            var s = Object.assign || function(e) {
                    for (var t = 1; t < arguments.length; t++) {
                        var n = arguments[t];
                        for (var i in n) Object.prototype.hasOwnProperty.call(n, i) && (e[i] = n[i])
                    }
                    return e
                },
                r = function() {
                    function e(e, t) {
                        for (var n = 0; n < t.length; n++) {
                            var i = t[n];
                            i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i)
                        }
                    }
                    return function(t, n, i) {
                        return n && e(t.prototype, n), i && e(t, i), t
                    }
                }(),
                o = function() {
                    function e(t, n) {
                        i(this, e), this.options = s({}, e.defaults, n), this.element = t || document.querySelector(".cocoen"), this.init()
                    }
                    return r(e, [{
                        key: "init",
                        value: function() {
                            this.createElements(), this.addEventListeners(), this.dimensions()
                        }
                    }, {
                        key: "createElements",
                        value: function() {
                            var e = document.createElement("span");
                            e.className = this.options.dragElementSelector.replace(".", ""), this.element.appendChild(e);
                            var t = document.createElement("div"),
                                n = this.element.querySelector("img:first-child");
                            t.appendChild(n.cloneNode(!0)), n.parentNode.replaceChild(t, n), this.dragElement = this.element.querySelector(this.options.dragElementSelector), this.beforeElement = this.element.querySelector("div:first-child"), this.beforeImage = this.beforeElement.querySelector("img")
                        }
                    }, {
                        key: "addEventListeners",
                        value: function() {
                            this.element.addEventListener("click", this.onTap.bind(this)), this.element.addEventListener("mousemove", this.onDrag.bind(this)), this.element.addEventListener("touchmove", this.onDrag.bind(this)), this.dragElement.addEventListener("mousedown", this.onDragStart.bind(this)), this.dragElement.addEventListener("touchstart", this.onDragStart.bind(this)), window.addEventListener("mouseup", this.onDragEnd.bind(this)), window.addEventListener("resize", this.dimensions.bind(this))
                        }
                    }, {
                        key: "dimensions",
                        value: function() {
                            this.elementWidth = parseInt(window.getComputedStyle(this.element).width, 10), this.elementOffsetLeft = this.element.getBoundingClientRect().left + document.body.scrollLeft, this.beforeImage.style.width = this.elementWidth + "px", this.dragElementWidth = parseInt(window.getComputedStyle(this.dragElement).width, 10), this.minLeftPos = this.elementOffsetLeft + 10, this.maxLeftPos = this.elementOffsetLeft + this.elementWidth - this.dragElementWidth - 10
                        }
                    }, {
                        key: "onTap",
                        value: function(e) {
                            e.preventDefault(), this.leftPos = e.pageX ? e.pageX : e.touches[0].pageX, this.requestDrag()
                        }
                    }, {
                        key: "onDragStart",
                        value: function(e) {
                            e.preventDefault();
                            var t = e.pageX ? e.pageX : e.touches[0].pageX,
                                n = this.dragElement.getBoundingClientRect().left + document.body.scrollLeft;
                            this.posX = n + this.dragElementWidth - t, this.isDragging = !0
                        }
                    }, {
                        key: "onDragEnd",
                        value: function(e) {
                            e.preventDefault(), this.isDragging = !1
                        }
                    }, {
                        key: "onDrag",
                        value: function(e) {
                            e.preventDefault(), this.isDragging && (this.moveX = e.pageX ? e.pageX : e.touches[0].pageX, this.leftPos = this.moveX + this.posX - this.dragElementWidth, this.requestDrag())
                        }
                    }, {
                        key: "drag",
                        value: function() {
                            this.leftPos < this.minLeftPos ? this.leftPos = this.minLeftPos : this.leftPos > this.maxLeftPos && (this.leftPos = this.maxLeftPos);
                            var e = this.leftPos + this.dragElementWidth / 2 - this.elementOffsetLeft;
                            e /= this.elementWidth;
                            var t = 100 * e + "%";
                            this.dragElement.style.left = t, this.beforeElement.style.width = t, this.options.dragCallback && this.options.dragCallback(e)
                        }
                    }, {
                        key: "requestDrag",
                        value: function() {
                            window.requestAnimationFrame(this.drag.bind(this))
                        }
                    }]), e
                }();
            o.defaults = {
                dragElementSelector: ".cocoen-drag",
                dragCallback: null
            }, t.exports = o
        }, {}]
    }, {}, [1])(1)
});
<commit_msg>Read Me file update to include mobile controls.<commit_after>//Initilize Cocoen
document.addEventListener('DOMContentLoaded', function(){
	document.sliders = [];
	var cocoen = document.querySelectorAll('.cocoen');
	 for (var i = cocoen.length - 1; i > -1; i--)	{
			  document.sliders.push (new Cocoen(cocoen[i]));
	 }
});

var mouseTimeout,
		mouseDown = false,
    direction = "",
		oldx = 0,
		dragHandle = null,
		handleMouseMove = function (e) {
			slideDirection(e.pageX);
		},
		handleTouchMove = function (e) {
      var touch;
			try {
				touch = e.changedTouches[0]
			} catch(err) {
				touch = e.touches[0];
			}
			slideDirection(touch.pageX);
		},
		slideDirection = function (x_value) {
			if (x_value < oldx) {
				$(dragHandle)
					.addClass("dragging-left")
					.removeClass("dragging-right");
			} else if (x_value > oldx) {
        $(dragHandle)
					.addClass("dragging-right")
					.removeClass("dragging-left");
			}
			oldx = x_value;
			clearTimeout(mouseTimeout);
			mouseTimeout = setTimeout(function(){
				$(dragHandle)
					.removeClass("dragging-left dragging-right");
			}, 300);
	  }
document.onmousemove = function(){
	if(!dragHandle){
		return;
	}
}
$('.cocoen')
//Disable context menu on the element when right clicking
	.on('contextmenu', function () {
		return false;
	})
  .on('mousedown', function(e){
		dragHandle = e.currentTarget.getElementsByClassName("cocoen-drag")[0];
    switch (e.which) {
      case 1:
				setSlidingMode(true);
			  if($(e.target).hasClass('cocoen-drag')){
	        document.addEventListener('mousemove', handleMouseMove);
			  }
        break;
      case 2:
          break;
      case 3:
        $(this)
          .find('img')
          .css({'transform': 'translateZ(0) scale('+ $(this).attr('data-scale') +')'})
          .addClass("mouseDown");
				setSlidingMode(true);
				if($(e.target).hasClass('cocoen-drag')){
	        document.addEventListener('mousemove', handleMouseMove);
			  }
        break;
      default:
    }
		mouseDown = true;
  })
  .on('mouseup', function(e){
    $(this)
      .find('img')
      .css({'transform': 'scale(1)'})
      .removeClass("mouseDown");
		mouseUpCommonHandler(e);

  })
  .on('mousemove', function(e){
    $(this)
      .find('img')
      .css({'transform-origin': ((e.pageX - $(this).offset().left) / $(this).width()) * 100 + '% ' + ((e.pageY - $(this).offset().top) / $(this).height()) * 100 +'%'});
  })
	.on('mouseenter', function (e) {
		var mouse_down = e.buttons;
		if (!mouse_down) {
			dragHandle = null;
		}
	})
  .on('mouseleave', function(e){
    $(this)
      .find('img')
      .css({'transform': 'scale(1)'})
      .removeClass("mouseDown");
  });

//Removes opacity when mouseup is outside the element
document.addEventListener('mouseup', function(e){
	if(mouseDown) {
		mouseUpCommonHandler(e);
	}
});

var mouseUpCommonHandler = function (e) {
	setSlidingMode(false);
	mouseDown = false;
	document.removeEventListener("mousemove",handleMouseMove);
};

//Mobile Zoom pan
sliderComponent = document.getElementsByClassName("cocoen");
for(var i = 0; i < sliderComponent.length; i++){
	sliderComponent[i].addEventListener("touchstart", tapStart);
	sliderComponent[i].addEventListener("touchmove", handleTouchMove);
	sliderComponent[i].addEventListener("touchend", tapEnd);
	sliderComponent[i].addEventListener('orientationchange', doOnOrientationChange);
}

var tappedTwice = false;
function tapStart(e) {
	dragHandle = e.currentTarget.getElementsByClassName("cocoen-drag")[0];
	setSlidingMode(true);
  if(!tappedTwice) {
      tappedTwice = true;
      setTimeout( function() { tappedTwice = false; }, 300 );
      return false;
  }
  e.preventDefault();
  //action on double tap goes below
	$(this)
		.find('img')
		.css({'transform': 'translateZ(0) scale('+ $(this).attr('data-scale') +')'})
		.addClass("mouseDown");
}

function tapEnd() {
	setSlidingMode(false);
}

function setSlidingMode(sliding) {
	var dragOpacity = (sliding) ? 0.6 : 1;
	$(dragHandle)
		.css('opacity', dragOpacity);
}

function doOnOrientationChange() {
	setTimeout(function() {
		document.slider.dimensions(); //Fixes an issue with the drag not getting updated with the proper dimensions
	}, 500);
}

/*
The MIT License (MIT)
Copyright (c) 2015 Koen Romers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. */

! function(e) {
    if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();
    else if ("function" == typeof define && define.amd) define([], e);
    else {
        var t;
        t = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this, t.Cocoen = e()
    }
}(function() {
    return function e(t, n, i) {
        function s(o, a) {
            if (!n[o]) {
                if (!t[o]) {
                    var l = "function" == typeof require && require;
                    if (!a && l) return l(o, !0);
                    if (r) return r(o, !0);
                    var d = new Error("Cannot find module '" + o + "'");
                    throw d.code = "MODULE_NOT_FOUND", d
                }
                var h = n[o] = {
                    exports: {}
                };
                t[o][0].call(h.exports, function(e) {
                    var n = t[o][1][e];
                    return s(n ? n : e)
                }, h, h.exports, e, t, n, i)
            }
            return n[o].exports
        }
        for (var r = "function" == typeof require && require, o = 0; o < i.length; o++) s(i[o]);
        return s
    }({
        1: [function(e, t, n) {
            "use strict";

            function i(e, t) {
                if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function")
            }
            var s = Object.assign || function(e) {
                    for (var t = 1; t < arguments.length; t++) {
                        var n = arguments[t];
                        for (var i in n) Object.prototype.hasOwnProperty.call(n, i) && (e[i] = n[i])
                    }
                    return e
                },
                r = function() {
                    function e(e, t) {
                        for (var n = 0; n < t.length; n++) {
                            var i = t[n];
                            i.enumerable = i.enumerable || !1, i.configurable = !0, "value" in i && (i.writable = !0), Object.defineProperty(e, i.key, i)
                        }
                    }
                    return function(t, n, i) {
                        return n && e(t.prototype, n), i && e(t, i), t
                    }
                }(),
                o = function() {
                    function e(t, n) {
                        i(this, e), this.options = s({}, e.defaults, n), this.element = t || document.querySelector(".cocoen"), this.init()
                    }
                    return r(e, [{
                        key: "init",
                        value: function() {
                            this.createElements(), this.addEventListeners(), this.dimensions()
                        }
                    }, {
                        key: "createElements",
                        value: function() {
                            var e = document.createElement("span");
                            e.className = this.options.dragElementSelector.replace(".", ""), this.element.appendChild(e);
                            var t = document.createElement("div"),
                                n = this.element.querySelector("img:first-child");
                            t.appendChild(n.cloneNode(!0)), n.parentNode.replaceChild(t, n), this.dragElement = this.element.querySelector(this.options.dragElementSelector), this.beforeElement = this.element.querySelector("div:first-child"), this.beforeImage = this.beforeElement.querySelector("img")
                        }
                    }, {
                        key: "addEventListeners",
                        value: function() {
                            this.element.addEventListener("click", this.onTap.bind(this)), this.element.addEventListener("mousemove", this.onDrag.bind(this)), this.element.addEventListener("touchmove", this.onDrag.bind(this)), this.dragElement.addEventListener("mousedown", this.onDragStart.bind(this)), this.dragElement.addEventListener("touchstart", this.onDragStart.bind(this)), window.addEventListener("mouseup", this.onDragEnd.bind(this)), window.addEventListener("resize", this.dimensions.bind(this))
                        }
                    }, {
                        key: "dimensions",
                        value: function() {
                            this.elementWidth = parseInt(window.getComputedStyle(this.element).width, 10), this.elementOffsetLeft = this.element.getBoundingClientRect().left + document.body.scrollLeft, this.beforeImage.style.width = this.elementWidth + "px", this.dragElementWidth = parseInt(window.getComputedStyle(this.dragElement).width, 10), this.minLeftPos = this.elementOffsetLeft + 10, this.maxLeftPos = this.elementOffsetLeft + this.elementWidth - this.dragElementWidth - 10
                        }
                    }, {
                        key: "onTap",
                        value: function(e) {
                            e.preventDefault(), this.leftPos = e.pageX ? e.pageX : e.touches[0].pageX, this.requestDrag()
                        }
                    }, {
                        key: "onDragStart",
                        value: function(e) {
                            e.preventDefault();
                            var t = e.pageX ? e.pageX : e.touches[0].pageX,
                                n = this.dragElement.getBoundingClientRect().left + document.body.scrollLeft;
                            this.posX = n + this.dragElementWidth - t, this.isDragging = !0
                        }
                    }, {
                        key: "onDragEnd",
                        value: function(e) {
                            e.preventDefault(), this.isDragging = !1
                        }
                    }, {
                        key: "onDrag",
                        value: function(e) {
                            e.preventDefault(), this.isDragging && (this.moveX = e.pageX ? e.pageX : e.touches[0].pageX, this.leftPos = this.moveX + this.posX - this.dragElementWidth, this.requestDrag())
                        }
                    }, {
                        key: "drag",
                        value: function() {
                            this.leftPos < this.minLeftPos ? this.leftPos = this.minLeftPos : this.leftPos > this.maxLeftPos && (this.leftPos = this.maxLeftPos);
                            var e = this.leftPos + this.dragElementWidth / 2 - this.elementOffsetLeft;
                            e /= this.elementWidth;
                            var t = 100 * e + "%";
                            this.dragElement.style.left = t, this.beforeElement.style.width = t, this.options.dragCallback && this.options.dragCallback(e)
                        }
                    }, {
                        key: "requestDrag",
                        value: function() {
                            window.requestAnimationFrame(this.drag.bind(this))
                        }
                    }]), e
                }();
            o.defaults = {
                dragElementSelector: ".cocoen-drag",
                dragCallback: null
            }, t.exports = o
        }, {}]
    }, {}, [1])(1)
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
const { performance } = require("perf_hooks");
const LoadTest = require("./LoadTest");
const blessed = require("blessed");
const contrib = require("blessed-contrib");

module.exports = (config) => {
  const screen = blessed.screen();
  const { grid: Grid } = contrib;

  // create layout and widgets
  const grid = new Grid({
    rows: 3,
    cols: 3,
    screen,
  });

  const throughputGraph = grid.set(0, 0, 1, 3, contrib.line, {
    label: " Throughput [request/s] ",
    showLegend: false,
    style: {
      text: "white",
      baseline: [150, 150, 150],
    },
  });

  const requestsGraph = grid.set(1, 0, 1, 3, contrib.line, {
    label: " Response time [ms] ",
    showLegend: false,
    style: {
      text: "white",
      baseline: [150, 150, 150],
    },
  });

  const table = grid.set(2, 0, 1, 1, contrib.table, {
    keys: false,
    interactive: false,
    fg: "white",
    label: " Stats ",
    columnSpacing: 0,
    headers: false,
    columnWidth: [30, 10],
  });

  const out = grid.set(2, 1, 1, 2, contrib.log, { label: "Log" });

  const tableData = {
    workers: {
      title: "Workers",
      value: 0,
    },
    total: {
      title: "Requetst sent",
      value: 0,
    },
    success: {
      title: "Succeeded responses",
      value: 0,
    },
    failed: {
      title: "Failed responses",
      value: 0,
    },
    throughput: {
      title: "Throughput [request/s]",
      value: 0,
    },
    average: {
      title: "Response time [ms]",
      value: 0,
    },
  };

  const round = (num, decimals = 2) => {
    const factor = Math.pow(10, decimals);
    return Math.round(num * factor) / factor;
  };
  const compileTableData = () =>
    Object.keys(tableData).reduce((acc, key) => {
      acc.push([tableData[key].title, tableData[key].value]);
      return acc;
    }, []);
  table.setData({ headers: [], data: compileTableData() });

  // set line charts data
  let successDurations = [];
  const updateInterval = 100;
  const intervalsOnScreen = 250;
  const rollingAverageCount = 10;
  const start = performance.now();
  let responseTimestamps = [];
  const throughputInterval = 5000;
  const generateY = (amount) =>
    " "
      .repeat(amount)
      .split("")
      .map((str) => 0);
  const generateX = (amount) =>
    generateY(amount).map((str, i) => ((i * updateInterval) / 1000).toString());
  const average = (arr, rolling = 0) => {
    if (rolling) {
      arr = arr.slice(-1 * rolling);
    }
    return arr.reduce((acc, num) => acc + num, 0) / arr.length || 1;
  };

  const lineSuccess = {
    style: { line: "green" },
    y: generateY(intervalsOnScreen),
    x: generateX(intervalsOnScreen),
  };
  requestsGraph.setData([lineSuccess]);

  const lineTrhoughput = {
    style: { line: "green" },
    y: generateY(intervalsOnScreen),
    x: generateX(intervalsOnScreen),
  };
  throughputGraph.setData([lineTrhoughput]);

  const lineInterval = setInterval(function () {
    const success = successDurations.slice(-1 * rollingAverageCount);
    lineSuccess.y.unshift(average(success, rollingAverageCount));
    lineSuccess.y = lineSuccess.y.slice(0, intervalsOnScreen - 1);
    requestsGraph.setData([lineSuccess]);
    requestsGraph.calcSize();

    const now = performance.now();
    const actualInterval = Math.min(throughputInterval, now - start);
    responseTimestamps = responseTimestamps.filter(
      (timestamp) => timestamp + throughputInterval > now
    );
    tableData.throughput.value = round(
      responseTimestamps.length / (actualInterval / 1000),
      2
    );
    lineTrhoughput.y.unshift(tableData.throughput.value);
    lineTrhoughput.y = lineTrhoughput.y.slice(0, intervalsOnScreen - 1);
    throughputGraph.setData([lineTrhoughput]);
    throughputGraph.calcSize();

    table.setData({ headers: [" ", " "], data: compileTableData() });

    screen.render();
  }, updateInterval);

  const loadTest = new LoadTest({
    ...config,
    onSuccess: (response, duration, text) => {
      successDurations.push(duration);
      tableData.success.value++;
      tableData.average.value = round(
        average(successDurations, rollingAverageCount),
        2
      );
      out.log(" Ok: " + response.url);
    },
    onFail: (response, duration, text) => {
      tableData.failed.value++;
      out.log(" Failed: " + response.url + " " + JSON.stringify(response));
    },
    onRequest({ url }) {
      tableData.total.value++;
    },
    onResponse(response, duration) {
      const now = performance.now();
      responseTimestamps.push(now);
    },
  });

  screen.key(["escape"], function (ch, key) {
    clearInterval(lineInterval);
    loadTest.stop();
  });
  screen.key(["C-c"], function (ch, key) {
    return process.exit(0);
  });

  loadTest.start();
  screen.render();

  tableData.workers.value = loadTest.getWorkerCount();
};
<commit_msg>UI language clearness<commit_after>#!/usr/bin/env node
const { performance } = require("perf_hooks");
const LoadTest = require("./LoadTest");
const blessed = require("blessed");
const contrib = require("blessed-contrib");

module.exports = (config) => {
  const screen = blessed.screen();
  const { grid: Grid } = contrib;

  // create layout and widgets
  const grid = new Grid({
    rows: 3,
    cols: 3,
    screen,
  });

  const throughputGraph = grid.set(0, 0, 1, 3, contrib.line, {
    label: " Throughput [request/s] ",
    showLegend: false,
    style: {
      text: "white",
      baseline: [150, 150, 150],
    },
  });

  const requestsGraph = grid.set(1, 0, 1, 3, contrib.line, {
    label: " Response time [ms] ",
    showLegend: false,
    style: {
      text: "white",
      baseline: [150, 150, 150],
    },
  });

  const table = grid.set(2, 0, 1, 1, contrib.table, {
    keys: false,
    interactive: false,
    fg: "white",
    label: " Stats ",
    columnSpacing: 0,
    headers: false,
    columnWidth: [30, 10],
  });

  const out = grid.set(2, 1, 1, 2, contrib.log, { label: "Log" });

  const tableData = {
    workers: {
      title: "Workers",
      value: 0,
    },
    total: {
      title: "Sent requests",
      value: 0,
    },
    success: {
      title: "Succeeded requests",
      value: 0,
    },
    failed: {
      title: "Failed requests",
      value: 0,
    },
    throughput: {
      title: "Throughput [sequests/s]",
      value: 0,
    },
    average: {
      title: "Response time [ms]",
      value: 0,
    },
  };

  const round = (num, decimals = 2) => {
    const factor = Math.pow(10, decimals);
    return Math.round(num * factor) / factor;
  };
  const compileTableData = () =>
    Object.keys(tableData).reduce((acc, key) => {
      acc.push([tableData[key].title, tableData[key].value]);
      return acc;
    }, []);
  table.setData({ headers: [], data: compileTableData() });

  // set line charts data
  let successDurations = [];
  const updateInterval = 100;
  const intervalsOnScreen = 250;
  const rollingAverageCount = 10;
  const start = performance.now();
  let responseTimestamps = [];
  const throughputInterval = 5000;
  const generateY = (amount) =>
    " "
      .repeat(amount)
      .split("")
      .map((str) => 0);
  const generateX = (amount) =>
    generateY(amount).map((str, i) => ((i * updateInterval) / 1000).toString());
  const average = (arr, rolling = 0) => {
    if (rolling) {
      arr = arr.slice(-1 * rolling);
    }
    return arr.reduce((acc, num) => acc + num, 0) / arr.length || 1;
  };

  const lineSuccess = {
    style: { line: "green" },
    y: generateY(intervalsOnScreen),
    x: generateX(intervalsOnScreen),
  };
  requestsGraph.setData([lineSuccess]);

  const lineTrhoughput = {
    style: { line: "green" },
    y: generateY(intervalsOnScreen),
    x: generateX(intervalsOnScreen),
  };
  throughputGraph.setData([lineTrhoughput]);

  const lineInterval = setInterval(function () {
    const success = successDurations.slice(-1 * rollingAverageCount);
    lineSuccess.y.unshift(average(success, rollingAverageCount));
    lineSuccess.y = lineSuccess.y.slice(0, intervalsOnScreen - 1);
    requestsGraph.setData([lineSuccess]);
    requestsGraph.calcSize();

    const now = performance.now();
    const actualInterval = Math.min(throughputInterval, now - start);
    responseTimestamps = responseTimestamps.filter(
      (timestamp) => timestamp + throughputInterval > now
    );
    tableData.throughput.value = round(
      responseTimestamps.length / (actualInterval / 1000),
      2
    );
    lineTrhoughput.y.unshift(tableData.throughput.value);
    lineTrhoughput.y = lineTrhoughput.y.slice(0, intervalsOnScreen - 1);
    throughputGraph.setData([lineTrhoughput]);
    throughputGraph.calcSize();

    table.setData({ headers: [" ", " "], data: compileTableData() });

    screen.render();
  }, updateInterval);

  const loadTest = new LoadTest({
    ...config,
    onSuccess: (response, duration, text) => {
      successDurations.push(duration);
      tableData.success.value++;
      tableData.average.value = round(
        average(successDurations, rollingAverageCount),
        2
      );
      out.log(" Ok: " + response.url);
    },
    onFail: (response, duration, text) => {
      tableData.failed.value++;
      out.log(" Failed: " + response.url + " " + JSON.stringify(response));
    },
    onRequest({ url }) {
      tableData.total.value++;
    },
    onResponse(response, duration) {
      const now = performance.now();
      responseTimestamps.push(now);
    },
  });

  screen.key(["escape"], function (ch, key) {
    clearInterval(lineInterval);
    loadTest.stop();
  });
  screen.key(["C-c"], function (ch, key) {
    return process.exit(0);
  });

  loadTest.start();
  screen.render();

  tableData.workers.value = loadTest.getWorkerCount();
};
<|endoftext|>


<commit_before>let express = require('express');
let app = express();
let cors = require('cors');
let spawn = require('child_process').spawn;
let ffmpeg = require('fluent-ffmpeg');
let fs = require('fs');
let crypto = require('crypto');

// Config information
const config = require('./config.json');
const port = config.port;
const cameraName = config.cameraName;
const hlsEncryptionEnabled = config.hlsEncryption.enabled;

// Camera stream options
const raspividOptions = ['-o', '-', '-t', '0', '-vf', '-fps', '30'];
const ffmpegInputOptions = ['-re'];
const ffmpegOutputOptions = ['-vcodec copy', '-g 50', '-hls_wrap 20'];

// Create the camera output directory if it doesn't already exist
// Directory contains all of the streaming video files
if (fs.existsSync(cameraName) === false) {
  fs.mkdirSync(cameraName);
}

// Encrypt HLS stream?
if (hlsEncryptionEnabled) {
  // Config information for hls encryption
  const publicBaseURL = config.hlsEncryption.publicBaseURL;
  const keyFileName = config.hlsEncryption.keyFileName;
  const keyInfoFileName = config.hlsEncryption.keyInfoFileName;

  // Setup encryption
  let keyFileContents = crypto.randomBytes(16);
  let initializationVector = crypto.randomBytes(16).toString('hex');
  let keyInfoFileContents = `${publicBaseURL}/${keyFileName}\n./${cameraName}/${keyFileName}\n${initializationVector}`;

  // Populate the encryption files, overwrite them if necessary
  fs.writeFileSync(`./${cameraName}/${keyFileName}`, keyFileContents);
  fs.writeFileSync(keyInfoFileName, keyInfoFileContents);

  // Add an option to the output stream to include the key info file in the livestream playlist
  ffmpegOutputOptions.push(`-hls_key_info_file ${keyInfoFileName}`);
}

// Start the camera stream
let cameraStream = spawn('raspivid', raspividOptions);

// Convert the camera stream to hls
let conversion = new ffmpeg(cameraStream.stdout).noAudio().format('hls').inputOptions(ffmpegInputOptions).outputOptions(ffmpegOutputOptions).output(`${cameraName}/livestream.m3u8`);

// Set up stream conversion listeners
conversion.on('error', function(err, stdout, stderr) {
  console.log('Cannot process video: ' + err.message);
});

conversion.on('start', function(commandLine) {
  console.log('Spawned Ffmpeg with command: ' + commandLine);
});

conversion.on('stderr', function(stderrLine) {
  console.log('Stderr output: ' + stderrLine);
});

// Start the conversion
conversion.run();

// Allows CORS
app.use(cors());

// Set up a fileserver for the streaming video files
app.use(`/${cameraName}`, express.static(cameraName));

app.listen(port);
console.log(`STARTING CAMERA STREAM SERVER AT PORT ${port}`);
<commit_msg>Changed resolution to 720p for bandwidth reasons<commit_after>let express = require('express');
let app = express();
let cors = require('cors');
let spawn = require('child_process').spawn;
let ffmpeg = require('fluent-ffmpeg');
let fs = require('fs');
let crypto = require('crypto');

// Config information
const config = require('./config.json');
const port = config.port;
const cameraName = config.cameraName;
const hlsEncryptionEnabled = config.hlsEncryption.enabled;

// Camera stream options
const raspividOptions = ['-o', '-', '-t', '0', '-vf', '-w', '1280', '-h', '720', '-fps', '30'];
const ffmpegInputOptions = ['-re'];
const ffmpegOutputOptions = ['-vcodec copy', '-g 50', '-hls_wrap 20'];

// Create the camera output directory if it doesn't already exist
// Directory contains all of the streaming video files
if (fs.existsSync(cameraName) === false) {
  fs.mkdirSync(cameraName);
}

// Encrypt HLS stream?
if (hlsEncryptionEnabled) {
  // Config information for hls encryption
  const publicBaseURL = config.hlsEncryption.publicBaseURL;
  const keyFileName = config.hlsEncryption.keyFileName;
  const keyInfoFileName = config.hlsEncryption.keyInfoFileName;

  // Setup encryption
  let keyFileContents = crypto.randomBytes(16);
  let initializationVector = crypto.randomBytes(16).toString('hex');
  let keyInfoFileContents = `${publicBaseURL}/${keyFileName}\n./${cameraName}/${keyFileName}\n${initializationVector}`;

  // Populate the encryption files, overwrite them if necessary
  fs.writeFileSync(`./${cameraName}/${keyFileName}`, keyFileContents);
  fs.writeFileSync(keyInfoFileName, keyInfoFileContents);

  // Add an option to the output stream to include the key info file in the livestream playlist
  ffmpegOutputOptions.push(`-hls_key_info_file ${keyInfoFileName}`);
}

// Start the camera stream
let cameraStream = spawn('raspivid', raspividOptions);

// Convert the camera stream to hls
let conversion = new ffmpeg(cameraStream.stdout).noAudio().format('hls').inputOptions(ffmpegInputOptions).outputOptions(ffmpegOutputOptions).output(`${cameraName}/livestream.m3u8`);

// Set up stream conversion listeners
conversion.on('error', function(err, stdout, stderr) {
  console.log('Cannot process video: ' + err.message);
});

conversion.on('start', function(commandLine) {
  console.log('Spawned Ffmpeg with command: ' + commandLine);
});

conversion.on('stderr', function(stderrLine) {
  console.log('Stderr output: ' + stderrLine);
});

// Start the conversion
conversion.run();

// Allows CORS
app.use(cors());

// Set up a fileserver for the streaming video files
app.use(`/${cameraName}`, express.static(cameraName));

app.listen(port);
console.log(`STARTING CAMERA STREAM SERVER AT PORT ${port}`);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 Andr Mekkawi <contact@andremekkawi.com>
 *
 * LICENSE
 *
 * This source file is subject to the MIT license in the file LICENSE.txt.
 * The license is also available at http://diskusagereport.sf.net/license.html
 */

var Controller;

;(function($){

Controller = function() {
	var self = this;

	// Add the built-in languages.
	this.addLanguage([ [ 'en-us', 'English (US)' ] ]);

	$('#Container').show();

	// Used to determine change in value.
	this._lastHash = null;
	this._lastSection = null;
	this._lastSectionOptions = null;
	this._lastTreeOptions = null;

	// jQuery references
	this._sections = $('#Sections');
	this._filesSection = $('#Section_Files');
	this._typesSection = $('#Section_Types');
	this._sizesSection = $('#Section_Sizes');
	this._modifiedSection = $('#Section_Modified');
	this._subdirsSection = $('#Section_SubDirs');
	this._top100Section = $('#Section_Top100');

	this.gradient =
		['007eff','0081f8','0084ef','0088e4','008dd8','0092cb','0097bd','009cae',
		 '00a29f','00a88f','00ae80','00b275','00b66a','00ba60','00be56','00c24c',
		 '00c642','00ca38','00cd2f','00d127','00d41f','00d717','00da10','03dd0a',
		 '07e005','0be200','15e600','22eb00','31ef00','42f300','55f600','69fa00',
		 '7dfd00','8bfe00','93ff00','9cff00','a5ff00','adff00','b5ff00','bdff00',
		 'c5ff00','cdff00','d4ff00','dbff00','e2ff00','e8ff00','eeff00','f3ff00',
		 'f8ff00','fcff00','ffff00','fffd00','fffa00','fff800','fff500','fff200',
		 'ffee00','ffea00','ffe600','ffe200','ffde00','ffd900','ffd400','ffcf00',
		 'ffca00','ffc500','ffc000','ffba00','ffb500','ffb000','ffaa00','ffa500',
		 'ffa000','ff9a00','ff9500','ff9000','ff8d00','ff8900','ff8600','ff8200',
		 'ff7f00','ff7b00','ff7700','ff7300','ff6f00','ff6b00','ff6700','ff6300',
		 'ff5f00','ff5a00','ff5600','ff5200','ff4e00','ff4900','ff4500','ff4100',
		 'ff3100','ff2200','ff1400','ff0900' ];

	// Set the report's name, which determines the path to find the report data.
	this.report = window.location.search.substring(1);

	// Set the maximum number of rows per page.
	this.pageMax = 100;

	// Calculate the width (which caches it).
	$.scrollbarWidth();

	// Adjust heights when the window is resized.
	$(window)
		.resize(function(){
			if (document.timeout_resize) clearTimeout(document.timeout_resize);
			document.timeout_resize = setTimeout(function() {
				self.resizeWindow();
			}, 100);
		});

	$('.pager a[href], #Sections a[href]').live('click', function(ev) {
		self.setOptions(self._parseLocation($(this).attr('href')));
		ev.preventDefault();
	});

	$('#ChangeLanguage').live('click', function(){
		$('#LanguageDialog').dialog('open');
	});

	$('#LanguageDialog').dialog({
		opening: function(e, contents) {
			contents.empty();

			var langs = self.getSupportedLanguages(true);

			var ul = $('
');
			$.each(langs, function(i, lang) {
				$('').click(function(e){
					e.preventDefault();
					self.setLanguage(lang);
					$('#LanguageDialog').dialog('close');
				}).text(self.getLanguageName(lang)).appendTo($('
').appendTo(ul));
			});

			ul.appendTo(contents);
		},
		backgroundColor: '#FFF'
	});

	$('#ErrorsDialog').dialog({
		backgroundColor: '#FFF',
		borderColor: '#CC0000'
	});

	// Initial adjustments to height.
	this.resizeWindow();

	// Create the history helper.
	$.history = new History();
};

$.extend(Controller.prototype, {

	_ajaxStage: null,
	_debugTimeout: 50,
	_preLoad: true,

	// Directory lookup.
	directories: null,

	// Default URL for the report data.
	reportsBaseURL: 'data/',

	options: null,
	defaultOptions: {
		hash: null,
		section: 'subdirs',
		totalsSortBy: 'byte',
		totalsSortRev: true,
		filesSortBy: 'size',
		filesSortRev: true,
		top100SortBy: 'size',
		top100SortRev: true,
		treeSortBy: 'label',
		treeSortRev: false,
		page: 1
	},

	load: function() {
		var self = this;

		// Mark that load() has been called.
		this._preLoad = false;

		// Make sure the default language is added.
		this.addLanguage(this.defaultLanguage);

		// Set the language.
		// Note: Also makes the header/footer visible.
		this.setLanguage(!this.language ? this.defaultLanguage : this.language, function(ret, message) {
			if (ret) {
				$('#Loading').html(self.translate('loading_settings'));

				setTimeout(function(){
					self._downloadSettings();
				}, self._debugTimeout);
			}
			else {
				$('#Loading').text('Language file could not be loaded: ' + message);
			}
		});
	},

	setLocation: function(location, completeFn) {
		// Parse the new location.
		this.options = $.extend({}, this.defaultOptions, this._parseLocation(location));

		// View the root hash if hash is not set.
		if (!this.options.hash) {
			this.options.hash = this.settings.root;
		}

		this.displayReport(completeFn);
	},

	setOptions: function(options, skipHistory) {
		$.extend(this.options, options);
		if (!skipHistory) {
			$.history.newItem(this._createLocation(this.options));
		}
		this.displayReport();
	},

	displayReport: function(completeFn) {
		// If the hash changed, load the data for the new directory.
		if (this._lastHash != this.options.hash) {
			this._downloadHash(completeFn);
		}
		else {
			this._populateReport();
			if ($.isFunction(completeFn)) completeFn();
		}
	},

	_downloadHash: function(completeFn) {
		var self = this;

		if (this._xhr) this._xhr.abort();
		this._xhr = $.ajax({
			cache: false,
			url: this.reportsBaseURL + this.report + '/' + this.options.hash,
			type: 'GET',
			dataType: 'json',
			error: function(xhr, status, ex) {
				// Clear the data since it was not loaded.
				self._data = null;

				switch (status) {
					case 'parsererror':
						$('#Error').html(self.translate('hash_parse_error'));
						break;
					case 'timeout':
						$('#Error').html(self.translate('hash_timeout_error'));
						break;
					case 'error':
						switch (xhr.status) {
							case 404:
								$('#Error').html(self.translate('hash_notfound_error'));
								break;
							case 401:
								$('#Error').html(self.translate('hash_username_error'));
								break;
							default:
								$('#Error').html(self.translate('hash_unknown_error', xhr.status));
						}
						break;
					default:
						$('#Error').html(self.translate('hash_unknown_error', status));
				}

				// Attempt to select the tree node.
				// This may be possible if the hash exists but its data could not be loaded.
				if (self.directories) {
					self._tree.tree('select', self.options.hash);
				}
			},
			success: function(data, status, xhr) {
				// Set the loaded data.
				self._data = data;

				if (self.directories) {
					// Determine the path to the selected directory.
					var hashPath = [];
					for (var i = 0; i < data.parents.length; i++) {
						hashPath.push(data.parents[i].hash);
					}
					hashPath.push(self.options.hash);

					// Load that path in the tree.
					self._tree.tree('select', hashPath);
				}
			},
			complete: function() {

				// Reset the page, unless this is the first load.
				if (self._lastHash != null) self.options.page = 1;

				self._lastHash = self.options.hash;

				// Reset the section so it is freshly loaded.
				self._lastSection = self._lastSectionOptions = null;

				$('#Error')[self._data ? 'hide' : 'show']();
				$('#RightColumn')[self._data ? 'show' : 'hide']();

				self._populateReport();

				// Correct the height of divs.
				self.resizeWindow();

				if ($.isFunction(completeFn)) completeFn();
			}
		});
	},

	_parseLocation: function(location) {
		// Make sure the location is an array.
		if ($.isString(location)) {
			if (location.indexOf('http://') == 0 && location.indexOf('#') >= 0) location = location.substring(location.indexOf('#'));
			if (location.indexOf('#') == 0) location = location.substring(1);
			location = location.parseQS();
		}

		// Reset options to defaults.
		var opts = {};

		// Validate and set options.
		if (location.h && location.h.match(/^[a-f0-9]{32}$/i)) {
			opts.hash = location.h.toLowerCase();
		}
		if (location.s && location.s.match(/^(subdirs|files|modified|types|sizes|top100)$/i)) {
			opts.section = location.s.toLowerCase();
		}
		if (location.tsb && location.tsb.match(/^(label|byte|num)$/)) {
			opts.totalsSortBy = location.tsb;
		}
		if (location.tsr && location.tsr.match(/^[01]$/)) {
			opts.totalsSortRev = location.tsr == '1';
		}
		if (location.fsb && location.fsb.match(/^(name|type|size|modified)$/)) {
			opts.filesSortBy = location.fsb;
		}
		if (location.fsr && location.fsr.match(/^[01]$/)) {
			opts.filesSortRev = location.fsr == '1';
		}
		if (location.bsb && location.bsb.match(/^(name|type|size|modified|path)$/)) {
			opts.top100SortBy = location.bsb;
		}
		if (location.bsr && location.bsr.match(/^[01]$/)) {
			opts.top100SortRev = location.bsr == '1';
		}
		if (location.dsb && location.dsb.match(/^(name|byte|num)$/)) {
			opts.treeSortBy = location.dsb;
		}
		if (location.dsr && location.dsr.match(/^[01]$/)) {
			opts.treeSortRev = location.dsr == '1';
		}
		if (location.p && location.p.match(/^[0-9]+$/)) {
			opts.page = parseInt(location.p);
		}

		return opts;
	},

	_createLocation: function(options, type) {
		var opts = $.extend({}, this.options, options),
			hash = section = totalsSort = filesSort = top100Sort = treeSort = page = true;

		switch (type) {
			case 'path':
			case 'contents':
			case 'top100':
				page = false;
			case 'pager':
				treeSort = false;
				break;
		}

		return ((hash ? '&h=' + escape(opts.hash) : '')
			+ (section ? '&s=' + escape(opts.section) : '')
			+ (totalsSort ? '&tsb=' + escape(opts.totalsSortBy) : '')
			+ (totalsSort ? '&tsr=' + escape(opts.totalsSortRev ? '1' : '0') : '')
			+ (filesSort ? '&fsb=' + escape(opts.filesSortBy) : '')
			+ (filesSort ? '&fsr=' + escape(opts.filesSortRev ? '1' : '0') : '')
			+ (top100Sort ? '&bsb=' + escape(opts.top100SortBy) : '')
			+ (top100Sort ? '&bsr=' + escape(opts.top100SortRev ? '1' : '0') : '')
			+ (treeSort ? '&dsb=' + escape(opts.treeSortBy) : '')
			+ (treeSort ? '&dsr=' + escape(opts.treeSortRev ? '1' : '0') : '')
			+ (page ? '&p=' + escape(opts.page) : '')).substring(1);
	},

	resizeWindow: function() {
		var footerHeight = $('#Footer').outerHeight();

		// Set the correct height for the container.
		var containerHeightDiff = $('#Container').outerHeight(true) - $('#Container').height();
		$('#Container').height($('body').height() - containerHeightDiff - $('#Container').offset().top - footerHeight - 5);

		$('#ErrorsDialog, #LanguageDialog').dialog('resize');

		if ($('#LeftColumn').is(':visible')) {
			var leftColumnBorderHeightDiff = $('#LeftColumnBorder').outerHeight(true) - $('#LeftColumnBorder').height();
			$('#LeftColumnBorder').height($('#LeftColumn').height() - leftColumnBorderHeightDiff);

			var leftColumnScrollerHeightDiff = $('#LeftColumnScroller').outerHeight(true) - $('#LeftColumnScroller').height();
			$('#LeftColumnScroller').height($('#LeftColumnBorder').height() - $('#LeftColumnScroller').position().top - leftColumnScrollerHeightDiff);

			$('#LeftColumn').width(Math.max(100, Math.min($('#Columns').width() - 200, $('#LeftColumn').width())));
		}

		if ($('#Report').is(':visible')) {
			var reportHeightDiff = $('#Report').outerHeight(true) - $('#Report').height();
			$('#Report').height($('#RightColumn').height() - reportHeightDiff - $('#Report').position().top);
		}
	}

});

})(jQuery);<commit_msg>* Adjusted the resizeWindow delay on the resize check so that it resizes at least every 500 milliseconds.<commit_after>/*
 * Copyright (c) 2011 Andr Mekkawi <contact@andremekkawi.com>
 *
 * LICENSE
 *
 * This source file is subject to the MIT license in the file LICENSE.txt.
 * The license is also available at http://diskusagereport.sf.net/license.html
 */

var Controller;

;(function($){

Controller = function() {
	var self = this;

	// Add the built-in languages.
	this.addLanguage([ [ 'en-us', 'English (US)' ] ]);

	$('#Container').show();

	// Used to determine change in value.
	this._lastHash = null;
	this._lastSection = null;
	this._lastSectionOptions = null;
	this._lastTreeOptions = null;

	// jQuery references
	this._sections = $('#Sections');
	this._filesSection = $('#Section_Files');
	this._typesSection = $('#Section_Types');
	this._sizesSection = $('#Section_Sizes');
	this._modifiedSection = $('#Section_Modified');
	this._subdirsSection = $('#Section_SubDirs');
	this._top100Section = $('#Section_Top100');

	this.gradient =
		['007eff','0081f8','0084ef','0088e4','008dd8','0092cb','0097bd','009cae',
		 '00a29f','00a88f','00ae80','00b275','00b66a','00ba60','00be56','00c24c',
		 '00c642','00ca38','00cd2f','00d127','00d41f','00d717','00da10','03dd0a',
		 '07e005','0be200','15e600','22eb00','31ef00','42f300','55f600','69fa00',
		 '7dfd00','8bfe00','93ff00','9cff00','a5ff00','adff00','b5ff00','bdff00',
		 'c5ff00','cdff00','d4ff00','dbff00','e2ff00','e8ff00','eeff00','f3ff00',
		 'f8ff00','fcff00','ffff00','fffd00','fffa00','fff800','fff500','fff200',
		 'ffee00','ffea00','ffe600','ffe200','ffde00','ffd900','ffd400','ffcf00',
		 'ffca00','ffc500','ffc000','ffba00','ffb500','ffb000','ffaa00','ffa500',
		 'ffa000','ff9a00','ff9500','ff9000','ff8d00','ff8900','ff8600','ff8200',
		 'ff7f00','ff7b00','ff7700','ff7300','ff6f00','ff6b00','ff6700','ff6300',
		 'ff5f00','ff5a00','ff5600','ff5200','ff4e00','ff4900','ff4500','ff4100',
		 'ff3100','ff2200','ff1400','ff0900' ];

	// Set the report's name, which determines the path to find the report data.
	this.report = window.location.search.substring(1);

	// Set the maximum number of rows per page.
	this.pageMax = 100;

	// Calculate the width (which caches it).
	$.scrollbarWidth();

	// Adjust heights when the window is resized.
	var resizeTimeout, resizeCounter = 0, resizeFn = function(){
		resizeCounter = 0;
		self.resizeWindow();
	};
	$(window).resize(function(){
		if (resizeCounter >= 500) {
			resizeFn();
		}
		else {
			if (resizeTimeout) clearTimeout(resizeTimeout);
			resizeTimeout = setTimeout(resizeFn, resizeCounter += 150);
		}
	});

	$('.pager a[href], #Sections a[href]').live('click', function(ev) {
		self.setOptions(self._parseLocation($(this).attr('href')));
		ev.preventDefault();
	});

	$('#ChangeLanguage').live('click', function(){
		$('#LanguageDialog').dialog('open');
	});

	$('#LanguageDialog').dialog({
		opening: function(e, contents) {
			contents.empty();

			var langs = self.getSupportedLanguages(true);

			var ul = $('
');
			$.each(langs, function(i, lang) {
				$('').click(function(e){
					e.preventDefault();
					self.setLanguage(lang);
					$('#LanguageDialog').dialog('close');
				}).text(self.getLanguageName(lang)).appendTo($('
').appendTo(ul));
			});

			ul.appendTo(contents);
		},
		backgroundColor: '#FFF'
	});

	$('#ErrorsDialog').dialog({
		backgroundColor: '#FFF',
		borderColor: '#CC0000'
	});

	// Initial adjustments to height.
	this.resizeWindow();

	// Create the history helper.
	$.history = new History();
};

$.extend(Controller.prototype, {

	_ajaxStage: null,
	_debugTimeout: 50,
	_preLoad: true,

	// Directory lookup.
	directories: null,

	// Default URL for the report data.
	reportsBaseURL: 'data/',

	options: null,
	defaultOptions: {
		hash: null,
		section: 'subdirs',
		totalsSortBy: 'byte',
		totalsSortRev: true,
		filesSortBy: 'size',
		filesSortRev: true,
		top100SortBy: 'size',
		top100SortRev: true,
		treeSortBy: 'label',
		treeSortRev: false,
		page: 1
	},

	load: function() {
		var self = this;

		// Mark that load() has been called.
		this._preLoad = false;

		// Make sure the default language is added.
		this.addLanguage(this.defaultLanguage);

		// Set the language.
		// Note: Also makes the header/footer visible.
		this.setLanguage(!this.language ? this.defaultLanguage : this.language, function(ret, message) {
			if (ret) {
				$('#Loading').html(self.translate('loading_settings'));

				setTimeout(function(){
					self._downloadSettings();
				}, self._debugTimeout);
			}
			else {
				$('#Loading').text('Language file could not be loaded: ' + message);
			}
		});
	},

	setLocation: function(location, completeFn) {
		// Parse the new location.
		this.options = $.extend({}, this.defaultOptions, this._parseLocation(location));

		// View the root hash if hash is not set.
		if (!this.options.hash) {
			this.options.hash = this.settings.root;
		}

		this.displayReport(completeFn);
	},

	setOptions: function(options, skipHistory) {
		$.extend(this.options, options);
		if (!skipHistory) {
			$.history.newItem(this._createLocation(this.options));
		}
		this.displayReport();
	},

	displayReport: function(completeFn) {
		// If the hash changed, load the data for the new directory.
		if (this._lastHash != this.options.hash) {
			this._downloadHash(completeFn);
		}
		else {
			this._populateReport();
			if ($.isFunction(completeFn)) completeFn();
		}
	},

	_downloadHash: function(completeFn) {
		var self = this;

		if (this._xhr) this._xhr.abort();
		this._xhr = $.ajax({
			cache: false,
			url: this.reportsBaseURL + this.report + '/' + this.options.hash,
			type: 'GET',
			dataType: 'json',
			error: function(xhr, status, ex) {
				// Clear the data since it was not loaded.
				self._data = null;

				switch (status) {
					case 'parsererror':
						$('#Error').html(self.translate('hash_parse_error'));
						break;
					case 'timeout':
						$('#Error').html(self.translate('hash_timeout_error'));
						break;
					case 'error':
						switch (xhr.status) {
							case 404:
								$('#Error').html(self.translate('hash_notfound_error'));
								break;
							case 401:
								$('#Error').html(self.translate('hash_username_error'));
								break;
							default:
								$('#Error').html(self.translate('hash_unknown_error', xhr.status));
						}
						break;
					default:
						$('#Error').html(self.translate('hash_unknown_error', status));
				}

				// Attempt to select the tree node.
				// This may be possible if the hash exists but its data could not be loaded.
				if (self.directories) {
					self._tree.tree('select', self.options.hash);
				}
			},
			success: function(data, status, xhr) {
				// Set the loaded data.
				self._data = data;

				if (self.directories) {
					// Determine the path to the selected directory.
					var hashPath = [];
					for (var i = 0; i < data.parents.length; i++) {
						hashPath.push(data.parents[i].hash);
					}
					hashPath.push(self.options.hash);

					// Load that path in the tree.
					self._tree.tree('select', hashPath);
				}
			},
			complete: function() {

				// Reset the page, unless this is the first load.
				if (self._lastHash != null) self.options.page = 1;

				self._lastHash = self.options.hash;

				// Reset the section so it is freshly loaded.
				self._lastSection = self._lastSectionOptions = null;

				$('#Error')[self._data ? 'hide' : 'show']();
				$('#RightColumn')[self._data ? 'show' : 'hide']();

				self._populateReport();

				// Correct the height of divs.
				self.resizeWindow();

				if ($.isFunction(completeFn)) completeFn();
			}
		});
	},

	_parseLocation: function(location) {
		// Make sure the location is an array.
		if ($.isString(location)) {
			if (location.indexOf('http://') == 0 && location.indexOf('#') >= 0) location = location.substring(location.indexOf('#'));
			if (location.indexOf('#') == 0) location = location.substring(1);
			location = location.parseQS();
		}

		// Reset options to defaults.
		var opts = {};

		// Validate and set options.
		if (location.h && location.h.match(/^[a-f0-9]{32}$/i)) {
			opts.hash = location.h.toLowerCase();
		}
		if (location.s && location.s.match(/^(subdirs|files|modified|types|sizes|top100)$/i)) {
			opts.section = location.s.toLowerCase();
		}
		if (location.tsb && location.tsb.match(/^(label|byte|num)$/)) {
			opts.totalsSortBy = location.tsb;
		}
		if (location.tsr && location.tsr.match(/^[01]$/)) {
			opts.totalsSortRev = location.tsr == '1';
		}
		if (location.fsb && location.fsb.match(/^(name|type|size|modified)$/)) {
			opts.filesSortBy = location.fsb;
		}
		if (location.fsr && location.fsr.match(/^[01]$/)) {
			opts.filesSortRev = location.fsr == '1';
		}
		if (location.bsb && location.bsb.match(/^(name|type|size|modified|path)$/)) {
			opts.top100SortBy = location.bsb;
		}
		if (location.bsr && location.bsr.match(/^[01]$/)) {
			opts.top100SortRev = location.bsr == '1';
		}
		if (location.dsb && location.dsb.match(/^(name|byte|num)$/)) {
			opts.treeSortBy = location.dsb;
		}
		if (location.dsr && location.dsr.match(/^[01]$/)) {
			opts.treeSortRev = location.dsr == '1';
		}
		if (location.p && location.p.match(/^[0-9]+$/)) {
			opts.page = parseInt(location.p);
		}

		return opts;
	},

	_createLocation: function(options, type) {
		var opts = $.extend({}, this.options, options),
			hash = section = totalsSort = filesSort = top100Sort = treeSort = page = true;

		switch (type) {
			case 'path':
			case 'contents':
			case 'top100':
				page = false;
			case 'pager':
				treeSort = false;
				break;
		}

		return ((hash ? '&h=' + escape(opts.hash) : '')
			+ (section ? '&s=' + escape(opts.section) : '')
			+ (totalsSort ? '&tsb=' + escape(opts.totalsSortBy) : '')
			+ (totalsSort ? '&tsr=' + escape(opts.totalsSortRev ? '1' : '0') : '')
			+ (filesSort ? '&fsb=' + escape(opts.filesSortBy) : '')
			+ (filesSort ? '&fsr=' + escape(opts.filesSortRev ? '1' : '0') : '')
			+ (top100Sort ? '&bsb=' + escape(opts.top100SortBy) : '')
			+ (top100Sort ? '&bsr=' + escape(opts.top100SortRev ? '1' : '0') : '')
			+ (treeSort ? '&dsb=' + escape(opts.treeSortBy) : '')
			+ (treeSort ? '&dsr=' + escape(opts.treeSortRev ? '1' : '0') : '')
			+ (page ? '&p=' + escape(opts.page) : '')).substring(1);
	},

	resizeWindow: function() {
		var footerHeight = $('#Footer').outerHeight();

		// Set the correct height for the container.
		var containerHeightDiff = $('#Container').outerHeight(true) - $('#Container').height();
		$('#Container').height($('body').height() - containerHeightDiff - $('#Container').offset().top - footerHeight - 5);

		$('#ErrorsDialog, #LanguageDialog').dialog('resize');

		if ($('#LeftColumn').is(':visible')) {
			var leftColumnBorderHeightDiff = $('#LeftColumnBorder').outerHeight(true) - $('#LeftColumnBorder').height();
			$('#LeftColumnBorder').height($('#LeftColumn').height() - leftColumnBorderHeightDiff);

			var leftColumnScrollerHeightDiff = $('#LeftColumnScroller').outerHeight(true) - $('#LeftColumnScroller').height();
			$('#LeftColumnScroller').height($('#LeftColumnBorder').height() - $('#LeftColumnScroller').position().top - leftColumnScrollerHeightDiff);

			$('#LeftColumn').width(Math.max(100, Math.min($('#Columns').width() - 200, $('#LeftColumn').width())));
		}

		if ($('#Report').is(':visible')) {
			var reportHeightDiff = $('#Report').outerHeight(true) - $('#Report').height();
			$('#Report').height($('#RightColumn').height() - reportHeightDiff - $('#Report').position().top);
		}
	}

});

})(jQuery);<|endoftext|>


<commit_before>/**
 * @file 数据类
 * @author errorrik(errorrik@gmail.com)
 */

var ExprType = require('../parser/expr-type');
var evalExpr = require('./eval-expr');
var DataChangeType = require('./data-change-type');
var createAccessor = require('../parser/create-accessor');
var parseExpr = require('../parser/parse-expr');
var guid = require('../util/guid');
var dataCache = require('./data-cache');

/**
 * 数据类
 *
 * @class
 * @param {Object?} data 初始数据
 * @param {Model?} parent 父级数据容器
 */
function Data(data, parent) {
    this.id = guid();
    this.parent = parent;
    this.raw = data || {};
    this.listeners = [];
}

// #[begin] error
// 以下两个函数只在开发模式下可用，在生产模式下不存在
/**
 * DataTypes 检测
 */
Data.prototype.checkDataTypes = function () {
    if (this.typeChecker) {
        this.typeChecker(this.raw);
    }
};

/**
 * 设置 type checker
 *
 * @param  {Function} typeChecker 类型校验器
 */
Data.prototype.setTypeChecker = function (typeChecker) {
    this.typeChecker = typeChecker;
};

// #[end]

/**
 * 添加数据变更的事件监听器
 *
 * @param {Function} listener 监听函数
 */
Data.prototype.listen = function (listener) {
    if (typeof listener === 'function') {
        this.listeners.push(listener);
    }
};

/**
 * 移除数据变更的事件监听器
 *
 * @param {Function} listener 监听函数
 */
Data.prototype.unlisten = function (listener) {
    var len = this.listeners.length;
    while (len--) {
        if (!listener || this.listeners[len] === listener) {
            this.listeners.splice(len, 1);
        }
    }
};

/**
 * 触发数据变更
 *
 * @param {Object} change 变更信息对象
 */
Data.prototype.fire = function (change) {
    if (change.option.silent || change.option.silence || change.option.quiet) {
        return;
    }

    for (var i = 0; i < this.listeners.length; i++) {
        this.listeners[i].call(this, change);
    }
};

/**
 * 获取数据项
 *
 * @param {string|Object?} expr 数据项路径
 * @param {Data?} callee 当前数据获取的调用环境
 * @return {*}
 */
Data.prototype.get = function (expr, callee) {
    var value = this.raw;
    if (!expr) {
        return value;
    }

    expr = parseExpr(expr);

    var paths = expr.paths;
    callee = callee || this;

    value = value[paths[0].value];

    if (value == null && this.parent) {
        value = this.parent.get(expr, callee);
    }
    else {
        for (var i = 1, l = paths.length; value != null && i < l; i++) {
            value = value[paths[i].value || evalExpr(paths[i], callee)];
        }
    }

    return value;
};

/**
 * 数据对象变更操作
 *
 * @inner
 * @param {Object|Array} source 要变更的源数据
 * @param {Array} exprPaths 属性路径
 * @param {*} value 变更属性值
 * @param {Data} data 对应的Data对象
 * @return {*} 变更后的新数据
 */
function immutableSet(source, exprPaths, pathsStart, pathsLen, value, data) {
    if (pathsStart >= pathsLen) {
        return value;
    }

    if (source == null) {
        source = {};
    }

    var prop = evalExpr(exprPaths[pathsStart], data);
    var result = source;

    if (source instanceof Array) {
        var index = +prop;

        result = source.slice(0);
        result[isNaN(index) ? prop : index] = immutableSet(source[index], exprPaths, ++pathsStart, pathsLen, value, data);
    }
    else if (typeof source === 'object') {
        result = {};

        for (var key in source) {
            if (key !== prop) {
                result[key] = source[key];
            }
        }

        result[prop] = immutableSet(source[prop], exprPaths, ++pathsStart, pathsLen, value, data);
    }

    return result;
}

/**
 * 设置数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} value 数据值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.set = function (expr, value, option) {
    option = option || {};

    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data set: ' + exprRaw);
    }
    // #[end]

    if (this.get(expr) === value && !option.force) {
        return;
    }

    expr = {
        type: ExprType.ACCESSOR,
        paths: expr.paths.slice(0)
    };

    dataCache.clear();
    this.raw = immutableSet(this.raw, expr.paths, 0, expr.paths.length, value, this);
    this.fire({
        type: DataChangeType.SET,
        expr: expr,
        value: value,
        option: option
    });

    // #[begin] error
    this.checkDataTypes();
    // #[end]

};

/**
 * 合并更新数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object} source 待合并的数据值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.merge = function (expr, source, option) {
    option = option || {};

    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data merge: ' + exprRaw);
    }

    if (typeof this.get(expr) !== 'object') {
        throw new Error('[SAN ERROR] Merge Expects a Target of Type \'object\'; got ' + typeof oldValue);
    }

    if (typeof source !== 'object') {
        throw new Error('[SAN ERROR] Merge Expects a Source of Type \'object\'; got ' + typeof source);
    }
    // #[end]

    for (var key in source) { // eslint-disable-line
        this.set(
            createAccessor(
                expr.paths.concat(
                    [
                        {
                            type: ExprType.STRING,
                            value: key
                        }
                    ]
                )
            ),
            source[key],
            option
        );
    }
};

/**
 * 基于更新函数更新数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {Function} fn 数据处理函数
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.apply = function (expr, fn, option) {
    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data apply: ' + exprRaw);
    }
    // #[end]

    var oldValue = this.get(expr);

    // #[begin] error
    if (typeof fn !== 'function') {
        throw new Error(
            '[SAN ERROR] Invalid Argument\'s Type in Data apply: '
            + 'Expected Function but got ' + typeof fn
        );
    }
    // #[end]

    this.set(expr, fn(oldValue), option);
};

/**
 * 数组数据项splice操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Array} args splice 接受的参数列表，数组项与Array.prototype.splice的参数一致
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {Array} 新数组
 */
Data.prototype.splice = function (expr, args, option) {
    option = option || {};
    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data splice: ' + exprRaw);
    }
    // #[end]

    var target = this.get(expr);
    var returnValue = [];

    if (target instanceof Array) {
        var index = args[0];
        var len = target.length;
        if (index > len) {
            index = len;
        }
        else if (index < 0) {
            index = len + index;
            if (index < 0) {
                index = 0;
            }
        }

        var newArray = target.slice(0);
        returnValue = newArray.splice.apply(newArray, args);
        dataCache.clear();
        this.raw = immutableSet(this.raw, expr.paths, 0, expr.paths.length, newArray, this);

        this.fire({
            expr: expr,
            type: DataChangeType.SPLICE,
            index: index,
            deleteCount: returnValue.length,
            value: returnValue,
            insertions: args.slice(2),
            option: option
        });
    }

    // #[begin] error
    this.checkDataTypes();
    // #[end]

    return returnValue;
};

/**
 * 数组数据项push操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} item 要push的值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {number} 新数组的length属性
 */
Data.prototype.push = function (expr, item, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        this.splice(expr, [target.length, 0, item], option);
        return target.length + 1;
    }
};

/**
 * 数组数据项pop操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {*}
 */
Data.prototype.pop = function (expr, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        var len = target.length;
        if (len) {
            return this.splice(expr, [len - 1, 1], option)[0];
        }
    }
};

/**
 * 数组数据项shift操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {*}
 */
Data.prototype.shift = function (expr, option) {
    return this.splice(expr, [0, 1], option)[0];
};

/**
 * 数组数据项unshift操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} item 要unshift的值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {number} 新数组的length属性
 */
Data.prototype.unshift = function (expr, item, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        this.splice(expr, [0, 0, item], option);
        return target.length + 1;
    }
};

/**
 * 数组数据项移除操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {number} index 要移除项的索引
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.removeAt = function (expr, index, option) {
    this.splice(expr, [index, 1], option);
};

/**
 * 数组数据项移除操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} value 要移除的项
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.remove = function (expr, value, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        var len = target.length;
        while (len--) {
            if (target[len] === value) {
                this.splice(expr, [len, 1], option);
                break;
            }
        }
    }
};

exports = module.exports = Data;
<commit_msg>fix #365<commit_after>/**
 * @file 数据类
 * @author errorrik(errorrik@gmail.com)
 */

var ExprType = require('../parser/expr-type');
var evalExpr = require('./eval-expr');
var DataChangeType = require('./data-change-type');
var createAccessor = require('../parser/create-accessor');
var parseExpr = require('../parser/parse-expr');
var guid = require('../util/guid');
var dataCache = require('./data-cache');

/**
 * 数据类
 *
 * @class
 * @param {Object?} data 初始数据
 * @param {Model?} parent 父级数据容器
 */
function Data(data, parent) {
    this.id = guid();
    this.parent = parent;
    this.raw = data || {};
    this.listeners = [];
}

// #[begin] error
// 以下两个函数只在开发模式下可用，在生产模式下不存在
/**
 * DataTypes 检测
 */
Data.prototype.checkDataTypes = function () {
    if (this.typeChecker) {
        this.typeChecker(this.raw);
    }
};

/**
 * 设置 type checker
 *
 * @param  {Function} typeChecker 类型校验器
 */
Data.prototype.setTypeChecker = function (typeChecker) {
    this.typeChecker = typeChecker;
};

// #[end]

/**
 * 添加数据变更的事件监听器
 *
 * @param {Function} listener 监听函数
 */
Data.prototype.listen = function (listener) {
    if (typeof listener === 'function') {
        this.listeners.push(listener);
    }
};

/**
 * 移除数据变更的事件监听器
 *
 * @param {Function} listener 监听函数
 */
Data.prototype.unlisten = function (listener) {
    var len = this.listeners.length;
    while (len--) {
        if (!listener || this.listeners[len] === listener) {
            this.listeners.splice(len, 1);
        }
    }
};

/**
 * 触发数据变更
 *
 * @param {Object} change 变更信息对象
 */
Data.prototype.fire = function (change) {
    if (change.option.silent || change.option.silence || change.option.quiet) {
        return;
    }

    for (var i = 0; i < this.listeners.length; i++) {
        this.listeners[i].call(this, change);
    }
};

/**
 * 获取数据项
 *
 * @param {string|Object?} expr 数据项路径
 * @param {Data?} callee 当前数据获取的调用环境
 * @return {*}
 */
Data.prototype.get = function (expr, callee) {
    var value = this.raw;
    if (!expr) {
        return value;
    }

    expr = parseExpr(expr);

    var paths = expr.paths;
    callee = callee || this;

    value = value[paths[0].value];

    if (value == null && this.parent) {
        value = this.parent.get(expr, callee);
    }
    else {
        for (var i = 1, l = paths.length; value != null && i < l; i++) {
            value = value[paths[i].value || evalExpr(paths[i], callee)];
        }
    }

    return value;
};

/**
 * 数据对象变更操作
 *
 * @inner
 * @param {Object|Array} source 要变更的源数据
 * @param {Array} exprPaths 属性路径
 * @param {*} value 变更属性值
 * @param {Data} data 对应的Data对象
 * @return {*} 变更后的新数据
 */
function immutableSet(source, exprPaths, pathsStart, pathsLen, value, data) {
    if (pathsStart >= pathsLen) {
        return value;
    }

    if (source == null) {
        source = {};
    }

    var pathExpr = exprPaths[pathsStart];
    var prop = evalExpr(pathExpr, data);
    var result = source;

    if (source instanceof Array) {
        var index = +prop;
        prop = isNaN(index) ? prop : index;

        result = source.slice(0);
        result[prop] = immutableSet(source[prop], exprPaths, pathsStart + 1, pathsLen, value, data);
    }
    else if (typeof source === 'object') {
        result = {};

        for (var key in source) {
            if (key !== prop) {
                result[key] = source[key];
            }
        }

        result[prop] = immutableSet(source[prop], exprPaths, pathsStart + 1, pathsLen, value, data);
    }

    if (pathExpr.value == null) {
        exprPaths[pathsStart] = {
            type: typeof prop === 'string' ? ExprType.STRING : ExprType.NUMBER,
            value: prop
        };
    }

    return result;
}

/**
 * 设置数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} value 数据值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.set = function (expr, value, option) {
    option = option || {};

    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data set: ' + exprRaw);
    }
    // #[end]

    if (this.get(expr) === value && !option.force) {
        return;
    }

    expr = {
        type: ExprType.ACCESSOR,
        paths: expr.paths.slice(0)
    };

    dataCache.clear();
    this.raw = immutableSet(this.raw, expr.paths, 0, expr.paths.length, value, this);
    this.fire({
        type: DataChangeType.SET,
        expr: expr,
        value: value,
        option: option
    });

    // #[begin] error
    this.checkDataTypes();
    // #[end]

};

/**
 * 合并更新数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object} source 待合并的数据值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.merge = function (expr, source, option) {
    option = option || {};

    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data merge: ' + exprRaw);
    }

    if (typeof this.get(expr) !== 'object') {
        throw new Error('[SAN ERROR] Merge Expects a Target of Type \'object\'; got ' + typeof oldValue);
    }

    if (typeof source !== 'object') {
        throw new Error('[SAN ERROR] Merge Expects a Source of Type \'object\'; got ' + typeof source);
    }
    // #[end]

    for (var key in source) { // eslint-disable-line
        this.set(
            createAccessor(
                expr.paths.concat(
                    [
                        {
                            type: ExprType.STRING,
                            value: key
                        }
                    ]
                )
            ),
            source[key],
            option
        );
    }
};

/**
 * 基于更新函数更新数据项
 *
 * @param {string|Object} expr 数据项路径
 * @param {Function} fn 数据处理函数
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.apply = function (expr, fn, option) {
    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data apply: ' + exprRaw);
    }
    // #[end]

    var oldValue = this.get(expr);

    // #[begin] error
    if (typeof fn !== 'function') {
        throw new Error(
            '[SAN ERROR] Invalid Argument\'s Type in Data apply: '
            + 'Expected Function but got ' + typeof fn
        );
    }
    // #[end]

    this.set(expr, fn(oldValue), option);
};

/**
 * 数组数据项splice操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Array} args splice 接受的参数列表，数组项与Array.prototype.splice的参数一致
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {Array} 新数组
 */
Data.prototype.splice = function (expr, args, option) {
    option = option || {};
    // #[begin] error
    var exprRaw = expr;
    // #[end]

    expr = parseExpr(expr);

    // #[begin] error
    if (expr.type !== ExprType.ACCESSOR) {
        throw new Error('[SAN ERROR] Invalid Expression in Data splice: ' + exprRaw);
    }
    // #[end]

    var target = this.get(expr);
    var returnValue = [];

    if (target instanceof Array) {
        var index = args[0];
        var len = target.length;
        if (index > len) {
            index = len;
        }
        else if (index < 0) {
            index = len + index;
            if (index < 0) {
                index = 0;
            }
        }

        var newArray = target.slice(0);
        returnValue = newArray.splice.apply(newArray, args);

        expr = {
            type: ExprType.ACCESSOR,
            paths: expr.paths.slice(0)
        };

        dataCache.clear();
        this.raw = immutableSet(this.raw, expr.paths, 0, expr.paths.length, newArray, this);

        this.fire({
            expr: expr,
            type: DataChangeType.SPLICE,
            index: index,
            deleteCount: returnValue.length,
            value: returnValue,
            insertions: args.slice(2),
            option: option
        });
    }

    // #[begin] error
    this.checkDataTypes();
    // #[end]

    return returnValue;
};

/**
 * 数组数据项push操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} item 要push的值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {number} 新数组的length属性
 */
Data.prototype.push = function (expr, item, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        this.splice(expr, [target.length, 0, item], option);
        return target.length + 1;
    }
};

/**
 * 数组数据项pop操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {*}
 */
Data.prototype.pop = function (expr, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        var len = target.length;
        if (len) {
            return this.splice(expr, [len - 1, 1], option)[0];
        }
    }
};

/**
 * 数组数据项shift操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {*}
 */
Data.prototype.shift = function (expr, option) {
    return this.splice(expr, [0, 1], option)[0];
};

/**
 * 数组数据项unshift操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} item 要unshift的值
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 * @return {number} 新数组的length属性
 */
Data.prototype.unshift = function (expr, item, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        this.splice(expr, [0, 0, item], option);
        return target.length + 1;
    }
};

/**
 * 数组数据项移除操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {number} index 要移除项的索引
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.removeAt = function (expr, index, option) {
    this.splice(expr, [index, 1], option);
};

/**
 * 数组数据项移除操作
 *
 * @param {string|Object} expr 数据项路径
 * @param {*} value 要移除的项
 * @param {Object=} option 设置参数
 * @param {boolean} option.silent 静默设置，不触发变更事件
 */
Data.prototype.remove = function (expr, value, option) {
    var target = this.get(expr);

    if (target instanceof Array) {
        var len = target.length;
        while (len--) {
            if (target[len] === value) {
                this.splice(expr, [len, 1], option);
                break;
            }
        }
    }
};

exports = module.exports = Data;
<|endoftext|>