<commit_before>/*

MRAA - Low Level Skeleton Library for Communication on GNU/Linux platforms
Library in C/C++ to interface with Galileo & other Intel platforms, in a structured and sane API with port nanmes/numbering that match boards & with bindings to javascript & python.

Steps for installing MRAA & UPM Library on Intel IoT Platform with IoTDevKit Linux* image
Using a ssh client:
1. echo "src maa-upm http://iotdk.intel.com/repos/1.1/intelgalactic" > /etc/opkg/intel-iotdk.conf
2. opkg update
3. opkg upgrade

*/

var mraa = require("mraa");
var upm = require("jsupm_grove");
//var upm_sound = require('jsupm_mic'); // parameter mismatch for getSampledWindow()

/* Temperature Sensor */
TemperatureSensor = {
    myAnalogPin : 0,
    initialize: function(AnalogPinNumber){
        //GROVE Kit Analog Pin (0,1,2 or 3) Connector --> Aio(AnalogpinNumber)
        myAnalogPin = new mraa.Aio(AnalogPinNumber);
    },
    getTemperature: function(){
        var B = 3975;
        var a = myAnalogPin.read();
        console.log("Checking....");
        var resistance = (1023 - a) * 10000 / a; //get the resistance of the sensor;
        //console.log("Resistance: "+resistance);
        var celsius_temperature = 1 / (Math.log(resistance / 10000) / B + 1 / 298.15) - 273.15;//convert to temperature via datasheet ;
        //console.log("Celsius Temperature:"+celsius_temperature);
        var fahrenheit_temperature = (celsius_temperature * (9 / 5)) + 32;
        //console.log("Fahrenheit Temperature: " + fahrenheit_temperature);
        Temp = { celsius : celsius_temperature, fahrenheit: fahrenheit_temperature};
        //console.log(Temp);
        return Temp;
    }
};

/* Button */
Button = {
    myButton : 2,
    initialize: function(buttonPinNumber){
        myButton = new mraa.Gpio(buttonPinNumber);
        myButton.dir(mraa.DIR_IN);
    },
    checkButton: function(){
        return myButton.read();
    }
};

/* LED */
LED = {
    myLED: 2,
    initialize: function(ledPinNumber){
        myLED = new upm.GroveLed(ledPinNumber);
    },
    setLedOnOff: function(status){
        if(status === true){
            myLED.on();
        }
        if(status === false){
            myLED.off();
        }
    }
};

/* Ligth Sensor */
LightSensor= {
    myLight: 0,
    initialize: function(lightPinNumber){
        myLight = new upm.GroveLight(lightPinNumber);
    },
    getLightValue: function(){
        console.log(myLight.value());
    }
};

/* Buzzer Sensor */
BuzzerSensor= {
    myBuzzer: 0,
    state: 0,
    initialize: function(buzzerPinNumber){
        myBuzzer = new mraa.Gpio(buzzerPinNumber);
        myBuzzer.dir(mraa.DIR_OUT);
    },
    buzzerOn: function(){
        if(this.state === 0){
            myBuzzer.write(1);
            this.state = 1;
        }
        else{
            myBuzzer.write(0);
            this.state = 0;
        }
    }
};

/* Touch Sensor */
TouchSensor= {
    myTouch: 0,
    initialize: function(touchPinNumber){
        myTouch = new mraa.Gpio(touchPinNumber);
        myTouch.dir(mraa.DIR_IN);
    },
    checkTouch: function(){
        return myTouch.read();
    }
};

/* Sound Sensor */
SoundSensor = {
    mySound: 0,
    thresholdContext: {
        averageReading : 0,
        runningAverage : 0,
        averagedOver   : 2,
    },
    initialize: function(soundPinNumber){
        mySound = new mraa.Aio(soundPinNumber);
    },
    getSampledWindow: function(freqMS, numberofSample){
        return mySound.read();
    },
    findThreshold: function(thresholdValue, buffer){
        var sum = 0;
        for(var i=0;i<buffer.length;i++){
            sum  = sum + buffer[i];
        }
        this.thresholdContext.averageReading = sum / buffer.length;
        this.thresholdContext.runningAverage = (((this.thresholdContext.averagedOver-1) * this.thresholdContext.runningAverage) + this.thresholdContext.averageReading) / this.thresholdContext.averagedOver;
        if(this.thresholdContext.runningAverage > thresholdValue){
            return this.thresholdContext.runningAverage;
        }
        else{
            return 0;
        }
    }
}

var groveRotary = new upm.GroveRotary(2);
loop();

function loop()
{
    var abs = groveRotary.abs_value();
    var absdeg = groveRotary.abs_deg();
    var absrad = groveRotary.abs_rad();

    var rel = groveRotary.rel_value();
    var reldeg = groveRotary.rel_deg();
    var relrad = groveRotary.rel_rad();

    //write the knob value to the console in different formats
    console.log("Abs: " + abs + " " + Math.round(parseInt(absdeg)) + " " + absrad.toFixed(3));
    console.log("Rel: " + rel + " " + Math.round(parseInt(reldeg)) + " " + relrad.toFixed(3));

    //wait 2 s and call function again
    setTimeout(loop, 2000);
}

<commit_msg>rotary angle sensor working<commit_after>/*

MRAA - Low Level Skeleton Library for Communication on GNU/Linux platforms
Library in C/C++ to interface with Galileo & other Intel platforms, in a structured and sane API with port nanmes/numbering that match boards & with bindings to javascript & python.

Steps for installing MRAA & UPM Library on Intel IoT Platform with IoTDevKit Linux* image
Using a ssh client:
1. echo "src maa-upm http://iotdk.intel.com/repos/1.1/intelgalactic" > /etc/opkg/intel-iotdk.conf
2. opkg update
3. opkg upgrade

*/

var mraa = require("mraa");
var upm = require("jsupm_grove");
//var upm_sound = require('jsupm_mic'); // parameter mismatch for getSampledWindow()

/* Temperature Sensor */
TemperatureSensor = {
    myAnalogPin : 0,
    initialize: function(AnalogPinNumber){
        //GROVE Kit Analog Pin (0,1,2 or 3) Connector --> Aio(AnalogpinNumber)
        myAnalogPin = new mraa.Aio(AnalogPinNumber);
    },
    getTemperature: function(){
        var B = 3975;
        var a = myAnalogPin.read();
        console.log("Checking....");
        var resistance = (1023 - a) * 10000 / a; //get the resistance of the sensor;
        //console.log("Resistance: "+resistance);
        var celsius_temperature = 1 / (Math.log(resistance / 10000) / B + 1 / 298.15) - 273.15;//convert to temperature via datasheet ;
        //console.log("Celsius Temperature:"+celsius_temperature);
        var fahrenheit_temperature = (celsius_temperature * (9 / 5)) + 32;
        //console.log("Fahrenheit Temperature: " + fahrenheit_temperature);
        Temp = { celsius : celsius_temperature, fahrenheit: fahrenheit_temperature};
        //console.log(Temp);
        return Temp;
    }
};

/* Button */
Button = {
    myButton : 2,
    initialize: function(buttonPinNumber){
        myButton = new mraa.Gpio(buttonPinNumber);
        myButton.dir(mraa.DIR_IN);
    },
    checkButton: function(){
        return myButton.read();
    }
};

/* LED */
LED = {
    myLED: 2,
    initialize: function(ledPinNumber){
        myLED = new upm.GroveLed(ledPinNumber);
    },
    setLedOnOff: function(status){
        if(status === true){
            myLED.on();
        }
        if(status === false){
            myLED.off();
        }
    }
};

/* Ligth Sensor */
LightSensor= {
    myLight: 0,
    initialize: function(lightPinNumber){
        myLight = new upm.GroveLight(lightPinNumber);
    },
    getLightValue: function(){
        console.log(myLight.value());
    }
};

/* Buzzer Sensor */
BuzzerSensor= {
    myBuzzer: 0,
    state: 0,
    initialize: function(buzzerPinNumber){
        myBuzzer = new mraa.Gpio(buzzerPinNumber);
        myBuzzer.dir(mraa.DIR_OUT);
    },
    buzzerOn: function(){
        if(this.state === 0){
            myBuzzer.write(1);
            this.state = 1;
        }
        else{
            myBuzzer.write(0);
            this.state = 0;
        }
    }
};

/* Touch Sensor */
TouchSensor= {
    myTouch: 0,
    initialize: function(touchPinNumber){
        myTouch = new mraa.Gpio(touchPinNumber);
        myTouch.dir(mraa.DIR_IN);
    },
    checkTouch: function(){
        return myTouch.read();
    }
};

/* Sound Sensor */
SoundSensor = {
    mySound: 0,
    thresholdContext: {
        averageReading : 0,
        runningAverage : 0,
        averagedOver   : 2,
    },
    initialize: function(soundPinNumber){
        mySound = new mraa.Aio(soundPinNumber);
    },
    getSampledWindow: function(freqMS, numberofSample){
        return mySound.read();
    },
    findThreshold: function(thresholdValue, buffer){
        var sum = 0;
        for(var i=0;i<buffer.length;i++){
            sum  = sum + buffer[i];
        }
        this.thresholdContext.averageReading = sum / buffer.length;
        this.thresholdContext.runningAverage = (((this.thresholdContext.averagedOver-1) * this.thresholdContext.runningAverage) + this.thresholdContext.averageReading) / this.thresholdContext.averagedOver;
        if(this.thresholdContext.runningAverage > thresholdValue){
            return this.thresholdContext.runningAverage;
        }
        else{
            return 0;
        }
    }
}

/* Rotary Angle Sensor */
AngleSensor = {
    myAngle: 0,
    initialize: function(anglePinNumber){
        myAngle = new upm.GroveRotary(anglePinNumber);
    },
    getAbsValue: function(){
        buffer = [];
        buffer.push(myAngle.abs_value());
        buffer.push(myAngle.abs_deg());
        buffer.push(myAngle.abs_rad().toFixed(3));
        return buffer;
    }
    getRelValue: function(){
        buffer = [];
        buffer.push(myAngle.rel_value());
        buffer.push(myAngle.rel_deg());
        buffer.push(myAngle.rel_rad().toFixed(3));
        return buffer;
    }
}
<|endoftext|>


<commit_before>$.ajaxSetup({async: false});

var map, points, info, bounds, data, codes = {};

$.getJSON('http://ks-opendata-community.github.io/chimney/data/工廠清單.json', {}, function (p) {
    points = p;
});

$.getJSON('http://ks-opendata-community.github.io/chimney/data/項目代碼.json', {}, function (p) {
    $.each(p, function (k, v) {
        codes[v['ITEM']] = v;
    });
});

$.get('http://ks-opendata-community.github.io/chimney/data/daily/latest.csv', {}, function (p) {
    data = $.csv.toArrays(p);
    data.sort(function (a, b) {
        return b[3] - a[3];
    });
});

function initialize() {
    /*map setting*/
    $('#map-canvas').height(window.outerHeight / 2.2);

    map = new google.maps.Map(document.getElementById('map-canvas'), {
        zoom: 12,
        center: {lat: 22.672925, lng: 120.309465}
    });
    info = new google.maps.InfoWindow();
    bounds = new google.maps.LatLngBounds();

    $.each(points, function (k, p) {
        var geoPoint = (new google.maps.LatLng(parseFloat(p.Lat), parseFloat(p.Lng)));
        var marker = new google.maps.Marker({
            position: geoPoint,
            map: map,
            title: p['工廠']
        });
        marker.data = p;
        marker.addListener('click', function (cp) {
            info.setContent(this.data['工廠']);
            info.open(map, this);
            $('#title').html(this.data['工廠']);
            map.setZoom(15);
            map.setCenter(this.getPosition());
            var currentKey = this.data['管制編號'];
            var contentText = '
';
            $.each(data, function (b, l) {
                if (l[0] === currentKey) {
                    contentText += '
';
                    for (i in l) {
                        switch(i) {
                            case '0':
                                break;
                            case '2':
                                k = l[i].replace(' ', '');
                                contentText += '' + codes[k].DESP + '(' + codes[k].ABBR + ')	';
                                break;
                            case '4':
                                contentText += '' + l[i] + ' ' + codes[k].UNIT + '	';
                                break;
                            default:
                                contentText += '' + l[i] + '	';
                        }
                    }
                    contentText += '
';
                }
            });
            contentText += '
';
            $('#content').html(contentText);
        });
        bounds.extend(geoPoint);
    });

    map.fitBounds(bounds);
}

google.maps.event.addDomListener(window, 'load', initialize);<commit_msg>add table heading<commit_after>$.ajaxSetup({async: false});

var map, points, info, bounds, data, codes = {};

$.getJSON('http://ks-opendata-community.github.io/chimney/data/工廠清單.json', {}, function (p) {
    points = p;
});

$.getJSON('http://ks-opendata-community.github.io/chimney/data/項目代碼.json', {}, function (p) {
    $.each(p, function (k, v) {
        codes[v['ITEM']] = v;
    });
});

$.get('http://ks-opendata-community.github.io/chimney/data/daily/latest.csv', {}, function (p) {
    data = $.csv.toArrays(p);
    data.sort(function (a, b) {
        return b[3] - a[3];
    });
});

function initialize() {
    /*map setting*/
    $('#map-canvas').height(window.outerHeight / 2.2);

    map = new google.maps.Map(document.getElementById('map-canvas'), {
        zoom: 12,
        center: {lat: 22.672925, lng: 120.309465}
    });
    info = new google.maps.InfoWindow();
    bounds = new google.maps.LatLngBounds();

    $.each(points, function (k, p) {
        var geoPoint = (new google.maps.LatLng(parseFloat(p.Lat), parseFloat(p.Lng)));
        var marker = new google.maps.Marker({
            position: geoPoint,
            map: map,
            title: p['工廠']
        });
        marker.data = p;
        marker.addListener('click', function (cp) {
            info.setContent(this.data['工廠']);
            info.open(map, this);
            $('#title').html(this.data['工廠']);
            map.setZoom(15);
            map.setCenter(this.getPosition());
            var currentKey = this.data['管制編號'];
            var contentText = '

';
            contentText += '管制點	管制項目	檢驗時間	檢驗數值

';
            contentText += '
';
            $.each(data, function (b, l) {
                if (l[0] === currentKey) {
                    contentText += '
';
                    for (i in l) {
                        switch(i) {
                            case '0':
                                break;
                            case '2':
                                k = l[i].replace(' ', '');
                                contentText += '' + codes[k].DESP + '(' + codes[k].ABBR + ')	';
                                break;
                            case '4':
                                contentText += '' + l[i] + ' ' + codes[k].UNIT + '	';
                                break;
                            default:
                                contentText += '' + l[i] + '	';
                        }
                    }
                    contentText += '
';
                }
            });
            contentText += '

';
            $('#content').html(contentText);
        });
        bounds.extend(geoPoint);
    });

    map.fitBounds(bounds);
}

google.maps.event.addDomListener(window, 'load', initialize);<|endoftext|>


<commit_before>/**
 * MenuSectionOptionWidgets are used inside {@link OO.ui.MenuSelectWidget menu select widgets} to group one or more related
 * {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
 *
 *     @example
 *     var myDropdown = new OO.ui.DropdownWidget( {
 *         menu: {
 *             items: [
 *                 new OO.ui.MenuSectionOptionWidget( {
 *                     label: 'Dogs'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'corgi',
 *                     label: 'Welsh Corgi'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'poodle',
 *                     label: 'Standard Poodle'
 *                 } ),
 *                 new OO.ui.MenuSectionOptionWidget( {
 *                     label: 'Cats'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'lion',
 *                     label: 'Lion'
 *                 } )
 *             ]
 *         }
 *     } );
 *     $( 'body' ).append( myDropdown.$element );
 *
 * @class
 * @extends OO.ui.DecoratedOptionWidget
 *
 * @constructor
 * @param {Object} [config] Configuration options
 */
OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
	// Parent constructor
	OO.ui.MenuSectionOptionWidget.parent.call( this, config );

	// Initialization
	this.$element.addClass( 'oo-ui-menuSectionOptionWidget' )
		.attr( 'role', '' );
};

/* Setup */

OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );

/* Static Properties */

/**
 * @static
 * @inheritdoc
 */
OO.ui.MenuSectionOptionWidget.static.selectable = false;

/**
 * @static
 * @inheritdoc
 */
OO.ui.MenuSectionOptionWidget.static.highlightable = false;
<commit_msg>MenuSectionOptionWidget: Remove unsupported ARIA attribute<commit_after>/**
 * MenuSectionOptionWidgets are used inside {@link OO.ui.MenuSelectWidget menu select widgets} to group one or more related
 * {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
 *
 *     @example
 *     var myDropdown = new OO.ui.DropdownWidget( {
 *         menu: {
 *             items: [
 *                 new OO.ui.MenuSectionOptionWidget( {
 *                     label: 'Dogs'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'corgi',
 *                     label: 'Welsh Corgi'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'poodle',
 *                     label: 'Standard Poodle'
 *                 } ),
 *                 new OO.ui.MenuSectionOptionWidget( {
 *                     label: 'Cats'
 *                 } ),
 *                 new OO.ui.MenuOptionWidget( {
 *                     data: 'lion',
 *                     label: 'Lion'
 *                 } )
 *             ]
 *         }
 *     } );
 *     $( 'body' ).append( myDropdown.$element );
 *
 * @class
 * @extends OO.ui.DecoratedOptionWidget
 *
 * @constructor
 * @param {Object} [config] Configuration options
 */
OO.ui.MenuSectionOptionWidget = function OoUiMenuSectionOptionWidget( config ) {
	// Parent constructor
	OO.ui.MenuSectionOptionWidget.parent.call( this, config );

	// Initialization
	this.$element.addClass( 'oo-ui-menuSectionOptionWidget' )
		.removeAttr( 'role aria-selected' );
};

/* Setup */

OO.inheritClass( OO.ui.MenuSectionOptionWidget, OO.ui.DecoratedOptionWidget );

/* Static Properties */

/**
 * @static
 * @inheritdoc
 */
OO.ui.MenuSectionOptionWidget.static.selectable = false;

/**
 * @static
 * @inheritdoc
 */
OO.ui.MenuSectionOptionWidget.static.highlightable = false;
<|endoftext|>


<commit_before>var _ = require("../html5/libs/roarjs/external/underscore/underscore.js")

fs = require('fs')

function pad_left( v, str )
{
	if (v.length < str.length)
	{
		return  String(str+v).slice(-str.length)
	}
	return v
}

function pad_right( v, str )
{
	if (v.length < str.length)
	{
		return  String(v+str).slice(0,str.length)
	}
	return v
}

function capitalizeFirst( s )
{
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function fix_reserved_word( w )
{
  if( w==="set" ) return "_set";
  return w
}

function augment_template( t )
{
  t.fix_reserved_word = fix_reserved_word
  t.pad_left = pad_left
  t.pad_right = pad_right
  t.capitalizeFirst = capitalizeFirst
}

function build_web_api_cs()
{
  api_functions = require("../html5/libs/roarjs/api_functions.json")
  js_template = fs.readFileSync('src/WebAPI.template.cs',"utf8");
  augment_template( api_functions )

  var output = _.template(js_template, api_functions)

  fs.writeFile("plugins/RoarIO/implementation/WebAPI.cs",output);
}

function build_iweb_api_cs()
{
  api_functions = require("../html5/libs/roarjs/api_functions.json")
  js_template = fs.readFileSync('src/IWebAPI.template.cs',"utf8");
  augment_template( api_functions )

  var output = _.template(js_template, api_functions)

  fs.writeFile("plugins/RoarIO/IWebAPI.cs",output);
}

function build_event_manager()
{
  events = require("./data/events.json")
  cs_template = fs.readFileSync('src/RoarIOManager.template.cs',"utf8");
  augment_template( events )

  _.each(
    events.data.components,
    function(e,i,l)
    {
      events.data.events.push(
        {
          "name":e+"Ready",
          "notes":["Fired when the data have been retrieved from the server"],
          "args":[]
        } );
      events.data.events.push(
        {
          "name":e+"Change",
          "notes":["Fired when the data changes"],
          "args":[]
        } );
    } );

  var output = _.template(cs_template, events)

  fs.writeFile("plugins/RoarIO/RoarIOManager.cs",output);
}

function build_ievent_manager()
{
  events = require("./data/events.json")
  cs_template = fs.readFileSync('src/IRoarIOManager.template.cs',"utf8");
  augment_template( events )

  var output = _.template(cs_template, events)

  fs.writeFile("plugins/RoarIO/IRoarIOManager.cs",output);
}

build_web_api_cs();
build_iweb_api_cs();
build_event_manager();
//build_ievent_manager();
<commit_msg>Updated the template.js to use the right configuration json<commit_after>var _ = require("underscore")

fs = require('fs')

function pad_left( v, str )
{
	if (v.length < str.length)
	{
		return  String(str+v).slice(-str.length)
	}
	return v
}

function pad_right( v, str )
{
	if (v.length < str.length)
	{
		return  String(v+str).slice(0,str.length)
	}
	return v
}

function capitalizeFirst( s )
{
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function fix_reserved_word( w )
{
  if( w==="set" ) return "_set";
  return w
}

function augment_template( t )
{
  t.fix_reserved_word = fix_reserved_word
  t.pad_left = pad_left
  t.pad_right = pad_right
  t.capitalizeFirst = capitalizeFirst
}

function build_web_api_cs()
{
  api_functions = require("../api_functions.json")
  js_template = fs.readFileSync('src/WebAPI.template.cs',"utf8");
  augment_template( api_functions )

  var output = _.template(js_template, api_functions)

  fs.writeFile("plugins/RoarIO/implementation/WebAPI.cs",output);
}

function build_iweb_api_cs()
{
  api_functions = require("../api_functions.json")
  js_template = fs.readFileSync('src/IWebAPI.template.cs',"utf8");
  augment_template( api_functions )

  var output = _.template(js_template, api_functions)

  fs.writeFile("plugins/RoarIO/IWebAPI.cs",output);
}

function build_event_manager()
{
  events = require("./data/events.json")
  cs_template = fs.readFileSync('src/RoarIOManager.template.cs',"utf8");
  augment_template( events )

  _.each(
    events.data.components,
    function(e,i,l)
    {
      events.data.events.push(
        {
          "name":e+"Ready",
          "notes":["Fired when the data have been retrieved from the server"],
          "args":[]
        } );
      events.data.events.push(
        {
          "name":e+"Change",
          "notes":["Fired when the data changes"],
          "args":[]
        } );
    } );

  var output = _.template(cs_template, events)

  fs.writeFile("plugins/RoarIO/RoarIOManager.cs",output);
}

build_web_api_cs();
build_iweb_api_cs();
build_event_manager();
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (C) 2013 Sergi Mansilla
 *
 * 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.
 */

/**
 * @fileoverview List panel for large items.
 *
 * @link https://github.com/sergi/virtual-list
 */

goog.provide('ydn.ui.LazyList');
goog.require('goog.async.Delay');
goog.require('goog.ui.Component');
goog.require('ydn.ui.ILazyListRenderer');
goog.require('ydn.ui.LazyListModel');
goog.require('ydn.ui.LazyListRenderer');

/**
 *
 * @param {ydn.ui.ILazyListModel} model
 * @param {ydn.ui.ILazyListRenderer} renderer
 * @param {goog.dom.TagName=} opt_tag
 * @param {goog.dom.DomHelper=} opt_dom
 * @constructor
 * @extends {goog.ui.Component}
 * @struct
 */
ydn.ui.LazyList = function(model, renderer, opt_tag, opt_dom) {
  ydn.ui.LazyList.base(this, 'constructor', opt_dom);
  /**
   * @type {goog.dom.TagName}
   * @private
   */
  this.tag_ = opt_tag || goog.dom.TagName.DIV;
  /**
   * @type {ydn.ui.ILazyListRenderer}
   * @private
   */
  this.renderer_ = renderer;
  this.setModel(model);

  /**
   * @protected
   * @type {number}
   */
  this.screenItemsLen = 0;

  /**
   * @protected
   * @type {number}
   */
  this.cachedItemsLen = 0;

  /**
   * @type {goog.async.Delay}
   * @private
   */
  this.delay_cleanup_ = new goog.async.Delay(this.cleanUpItems_, 100, this);

};
goog.inherits(ydn.ui.LazyList, goog.ui.Component);

/**
 * @return {ydn.ui.ILazyListModel}
 */
ydn.ui.LazyList.prototype.getModel = function() {
  return /** @type {ydn.ui.ILazyListModel} */(ydn.ui.LazyList.base(this, 'getModel'));
};

/**
 * @define {boolean} debug flag.
 */
ydn.ui.LazyList.DEBUG = false;

/**
 * @const
 * @type {string}
 */
ydn.ui.LazyList.CSS_CLASS = 'ydn-lazy-list';

/**
 * @override
 */
ydn.ui.LazyList.prototype.createDom = function() {
  var dom = this.getDomHelper();
  var el = ydn.ui.LazyList.createContainer_('100%', '100%', this.tag_);
  this.setElementInternal(el);
  el.classList.add(ydn.ui.LazyList.CSS_CLASS);

  var scroller = ydn.ui.LazyList.createScroller_(this.renderer_.getHeight() * this.getModel().getCount());
  el.appendChild(scroller);
};

/**
 * @inheritDoc
 */
ydn.ui.LazyList.prototype.disposeInternal = function() {
  this.delay_cleanup_.dispose();
  this.delay_cleanup_ = null;
  ydn.ui.LazyList.base(this, 'disposeInternal');
};

/**
 * @type {number}
 * @private
 */
ydn.ui.LazyList.prototype.lastRepaintY_ = 0;

/**
 * @param {goog.events.BrowserEvent} ev
 * @private
 */
ydn.ui.LazyList.prototype.onScroll_ = function(ev) {
  var el = this.getElement();
  var itemHeight = this.renderer_.getHeight();
  var maxBuffer = this.screenItemsLen * itemHeight;

  var scrollTop = el.scrollTop; // Triggers reflow
  if (!this.lastRepaintY_ || Math.abs(scrollTop - this.lastRepaintY_) > maxBuffer) {
    var first = ((scrollTop / itemHeight) | 0) - this.screenItemsLen;
    this.renderChunk_(this.getElement(), first < 0 ? 0 : first);
    this.lastRepaintY_ = scrollTop;
  }
  this.delay_cleanup_.start();
  ev.preventDefault();
};

/**
 * @override
 */
ydn.ui.LazyList.prototype.enterDocument = function() {
  ydn.ui.LazyList.base(this, 'enterDocument');

  this.getHandler().listen(this.getElement(), 'scroll', this.onScroll_);
  this.getHandler().listen(this.getModel(), goog.events.EventType.LOAD, this.reset);
  this.getHandler().listen(this.getModel(), goog.events.EventType.CHANGE, this.updated);

  this.reset();
};

/**
 * Reset.
 */
ydn.ui.LazyList.prototype.updated = function() {
  if (this.getElement().childElementCount == 1) {
    this.reset();
  } else {
    var h = this.renderer_.getHeight() * this.getModel().getCount();
    var el = this.getElement().querySelector('.ydn-lazy-list-scroller');
    goog.style.setHeight(el, h);
  }
};

/**
 * Reset.
 */
ydn.ui.LazyList.prototype.reset = function() {
  if (ydn.ui.LazyList.DEBUG) {
    console.log('reset');
  }
  this.delay_cleanup_.stop();
  this.lastRepaintY_ = 0;
  var el = this.getElement();

  this.screenItemsLen = Math.ceil(el.clientHeight / this.renderer_.getHeight());
  // Cache 4 times the number of items that fit in the container viewport
  this.cachedItemsLen = this.screenItemsLen * 3;
  var h = this.renderer_.getHeight() * this.getModel().getCount();
  goog.style.setHeight(el.querySelector('.ydn-lazy-list-scroller'), h);
  for (var i = el.childElementCount - 1; i > 0; i--) {
    el.removeChild(el.children[i]);
  }
  this.renderChunk_(el, 0);
  // HACK: Here calling offsetHeight is just to trigger a reflow.
  // otherwise, newly added elements are not show up, until user scroll the panel.
  el.scrollTop = Math.min(el.offsetHeight, 0);
};

ydn.ui.LazyList.prototype.createRow = function(i) {
  var itemHeight = this.renderer_.getHeight();
  var item = this.renderer_.render(this.getModel().getItemAt(i));
  item.style.position = 'absolute';
  item.style.top = (i * itemHeight) + 'px';
  item.setAttribute('data-offset', i);
  return item;
};

/**
 * @type {number}
 * @private
 */
ydn.ui.LazyList.prototype.prev_first_ = 0;

/**
 * Renders a particular, consecutive chunk of the total rows in the list. To
 * keep acceleration while scrolling, we mark the nodes that are candidate for
 * deletion instead of deleting them right away, which would suddenly stop the
 * acceleration. We delete them once scrolling has finished.
 *
 * @param {Element} node Parent node where we want to append the children chunk.
 * @param {number} from Starting position, i.e. first children index.
 * @return {void}
 */
ydn.ui.LazyList.prototype.renderChunk_ = function(node, from) {
  var finalItem = from + this.cachedItemsLen;
  var totalRows = this.getModel().getCount();

  if (ydn.ui.LazyList.DEBUG) {
    window.console.log('render from ' + from + ' to ' + finalItem + ' for ' + totalRows);
  }
  if (finalItem > totalRows) {
    finalItem = totalRows;
  }

  var prev_start = -1;
  var prev_end = -1;
  if (node.childElementCount > 1) {
    // Note: there is dummy element in the list
    prev_start = parseInt(node.firstElementChild.nextElementSibling.getAttribute('data-offset'), 10);
    prev_end = parseInt(node.lastElementChild.getAttribute('data-offset'), 10);
  }

  // Append all the new rows in a document fragment that we will later append to
  // the parent node
  var created = 0;
  var purged = 0;
  var fragment = document.createDocumentFragment();
  for (var i = from; i < finalItem; i++) {
    var already = i >= prev_start && i <= prev_end;
    if (!already) {
      fragment.appendChild(this.createRow(i));
      created++;
    }
  }

  // Hide and mark obsolete nodes for deletion.
  for (var i = 1; i < node.childElementCount; i++) {
    // Note: there is dummy element in the list
    var offset = parseInt(node.children[i].getAttribute('data-offset'), 10);
    if (offset < from || offset > finalItem) {
      node.children[i].style.display = 'none';
      node.children[i].setAttribute('data-rm', '1');
      purged++;
    }
  }

  if (ydn.ui.LazyList.DEBUG) {
    console.log(created + ' created' + ' ' + purged + ' purged from ' + prev_start + '-' + prev_end);
  }

  node.appendChild(fragment);
};

/**
 *
 * @private
 */
ydn.ui.LazyList.prototype.cleanUpItems_ = function() {
  var el = this.getElement();
  var badNodes = el.querySelectorAll('[data-rm="1"]');
  for (var i = 0, l = badNodes.length; i < l; i++) {
    el.removeChild(badNodes[i]);
  }
  if (ydn.ui.LazyList.DEBUG) {
    console.log(badNodes.length + ' elements clean');
  }
};

/**
 * @param {string} w
 * @param {string} h
 * @param {goog.dom.TagName=} opt_tag
 * @return {Element}
 * @private
 */
ydn.ui.LazyList.createContainer_ = function(w, h, opt_tag) {
  var c = document.createElement(opt_tag || goog.dom.TagName.DIV);
  c.style.width = w;
  c.style.height = h;
  c.style.overflowX = 'hidden';
  c.style.overflowY = 'scroll';
  c.style.position = 'relative';
  c.style.padding = 0;
  return c;
};

/**
 * @private
 * @param {number} h
 * @return {Element}
 */
ydn.ui.LazyList.createScroller_ = function(h) {
  var scroller = document.createElement('div');
  scroller.classList.add('ydn-lazy-list-scroller');
  scroller.style.opacity = 0;
  scroller.style.position = 'absolute';
  scroller.style.top = '0';
  scroller.style.left = '0';
  scroller.style.width = '1px';
  scroller.style.height = h + 'px';
  return scroller;
};

<commit_msg>expended ui<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (C) 2013 Sergi Mansilla
 *
 * 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.
 */

/**
 * @fileoverview List panel for large items.
 *
 * @link https://github.com/sergi/virtual-list
 */

goog.provide('ydn.ui.LazyList');
goog.require('goog.async.Delay');
goog.require('goog.ui.Component');
goog.require('ydn.ui.ILazyListRenderer');
goog.require('ydn.ui.LazyListModel');
goog.require('ydn.ui.LazyListRenderer');

/**
 *
 * @param {ydn.ui.ILazyListModel} model
 * @param {ydn.ui.ILazyListRenderer} renderer
 * @param {goog.dom.TagName=} opt_tag
 * @param {goog.dom.DomHelper=} opt_dom
 * @constructor
 * @extends {goog.ui.Component}
 * @struct
 */
ydn.ui.LazyList = function(model, renderer, opt_tag, opt_dom) {
  ydn.ui.LazyList.base(this, 'constructor', opt_dom);
  /**
   * @type {goog.dom.TagName}
   * @private
   */
  this.tag_ = opt_tag || goog.dom.TagName.DIV;
  /**
   * @type {ydn.ui.ILazyListRenderer}
   * @private
   */
  this.renderer_ = renderer;
  this.setModel(model);

  /**
   * @protected
   * @type {number}
   */
  this.screenItemsLen = 0;

  /**
   * @protected
   * @type {number}
   */
  this.cachedItemsLen = 0;

  /**
   * @type {goog.async.Delay}
   * @private
   */
  this.delay_cleanup_ = new goog.async.Delay(this.cleanUpItems_, 100, this);

};
goog.inherits(ydn.ui.LazyList, goog.ui.Component);

/**
 * @return {ydn.ui.ILazyListModel}
 */
ydn.ui.LazyList.prototype.getModel = function() {
  return /** @type {ydn.ui.ILazyListModel} */(ydn.ui.LazyList.base(this, 'getModel'));
};

/**
 * @define {boolean} debug flag.
 */
ydn.ui.LazyList.DEBUG = true;

/**
 * @const
 * @type {string}
 */
ydn.ui.LazyList.CSS_CLASS = 'ydn-lazy-list';

/**
 * @override
 */
ydn.ui.LazyList.prototype.createDom = function() {
  var dom = this.getDomHelper();
  var el = ydn.ui.LazyList.createContainer_('100%', '100%', this.tag_);
  this.setElementInternal(el);
  el.classList.add(ydn.ui.LazyList.CSS_CLASS);

  var scroller = ydn.ui.LazyList.createScroller_(this.renderer_.getHeight() * this.getModel().getCount());
  el.appendChild(scroller);
};

/**
 * @inheritDoc
 */
ydn.ui.LazyList.prototype.disposeInternal = function() {
  this.delay_cleanup_.dispose();
  this.delay_cleanup_ = null;
  ydn.ui.LazyList.base(this, 'disposeInternal');
};

/**
 * @type {number}
 * @private
 */
ydn.ui.LazyList.prototype.lastRepaintY_ = 0;

/**
 * @param {goog.events.BrowserEvent} ev
 * @private
 */
ydn.ui.LazyList.prototype.onScroll_ = function(ev) {
  var el = this.getElement();
  var itemHeight = this.renderer_.getHeight();
  var maxBuffer = this.screenItemsLen * itemHeight;

  var scrollTop = el.scrollTop; // Triggers reflow
  if (!this.lastRepaintY_ || Math.abs(scrollTop - this.lastRepaintY_) > maxBuffer) {
    var first = ((scrollTop / itemHeight) | 0) - this.screenItemsLen;
    this.renderChunk_(this.getElement(), first < 0 ? 0 : first);
    this.lastRepaintY_ = scrollTop;
  }
  this.delay_cleanup_.start();
  ev.preventDefault();
};

/**
 * @override
 */
ydn.ui.LazyList.prototype.enterDocument = function() {
  ydn.ui.LazyList.base(this, 'enterDocument');

  this.getHandler().listen(this.getElement(), 'scroll', this.onScroll_);
  this.getHandler().listen(this.getModel(), goog.events.EventType.LOAD, this.reset);
  this.getHandler().listen(this.getModel(), goog.events.EventType.CHANGE, this.updated);

  this.reset();
};

/**
 * Reset.
 */
ydn.ui.LazyList.prototype.updated = function() {
  if (this.getElement().childElementCount == 1) {
    this.reset();
  } else {
    var h = this.renderer_.getHeight() * this.getModel().getCount();
    var el = this.getElement().querySelector('.ydn-lazy-list-scroller');
    goog.style.setHeight(el, h);
  }
};

/**
 * Reset.
 */
ydn.ui.LazyList.prototype.reset = function() {
  if (ydn.ui.LazyList.DEBUG) {
    console.log('reset');
  }
  this.delay_cleanup_.stop();
  this.lastRepaintY_ = 0;
  var el = this.getElement();

  this.screenItemsLen = Math.ceil(el.clientHeight / this.renderer_.getHeight());
  // Cache 4 times the number of items that fit in the container viewport
  this.cachedItemsLen = this.screenItemsLen * 3;
  var h = this.renderer_.getHeight() * this.getModel().getCount();
  goog.style.setHeight(el.querySelector('.ydn-lazy-list-scroller'), h);
  for (var i = el.childElementCount - 1; i > 0; i--) {
    el.removeChild(el.children[i]);
  }
  this.renderChunk_(el, 0);
  // HACK: Here calling offsetHeight is just to trigger a reflow.
  // otherwise, newly added elements are not show up, until user scroll the panel.
  el.scrollTop = Math.min(el.offsetHeight, 0);
};

ydn.ui.LazyList.prototype.createRow = function(i) {
  var itemHeight = this.renderer_.getHeight();
  var item = this.renderer_.render(this.getModel().getItemAt(i));
  item.style.position = 'absolute';
  item.style.top = (i * itemHeight) + 'px';
  item.setAttribute('data-offset', i);
  return item;
};

/**
 * @type {number}
 * @private
 */
ydn.ui.LazyList.prototype.prev_first_ = 0;

/**
 * Renders a particular, consecutive chunk of the total rows in the list. To
 * keep acceleration while scrolling, we mark the nodes that are candidate for
 * deletion instead of deleting them right away, which would suddenly stop the
 * acceleration. We delete them once scrolling has finished.
 *
 * @param {Element} node Parent node where we want to append the children chunk.
 * @param {number} from Starting position, i.e. first children index.
 * @return {void}
 */
ydn.ui.LazyList.prototype.renderChunk_ = function(node, from) {
  var finalItem = from + this.cachedItemsLen;
  var totalRows = this.getModel().getCount();

  if (ydn.ui.LazyList.DEBUG) {
    window.console.log('render from ' + from + ' to ' + finalItem + ' for ' + totalRows);
  }
  if (finalItem > totalRows) {
    finalItem = totalRows;
  }

  var prev_start = -1;
  var prev_end = -1;
  if (node.childElementCount > 1) {
    // Note: there is dummy element in the list
    prev_start = parseInt(node.firstElementChild.nextElementSibling.getAttribute('data-offset'), 10);
    prev_end = parseInt(node.lastElementChild.getAttribute('data-offset'), 10);
  }

  // Append all the new rows in a document fragment that we will later append to
  // the parent node
  var created = 0;
  var purged = 0;
  var fragment = document.createDocumentFragment();
  for (var i = from; i < finalItem; i++) {
    var already = i >= prev_start && i <= prev_end;
    if (!already) {
      fragment.appendChild(this.createRow(i));
      created++;
    }
  }

  // Hide and mark obsolete nodes for deletion.
  for (var i = 1; i < node.childElementCount; i++) {
    // Note: there is dummy element in the list
    var offset = parseInt(node.children[i].getAttribute('data-offset'), 10);
    if (offset < from || offset > finalItem) {
      node.children[i].style.display = 'none';
      node.children[i].setAttribute('data-rm', '1');
      purged++;
    }
  }

  if (ydn.ui.LazyList.DEBUG) {
    console.log(created + ' created' + ' ' + purged + ' purged from ' + prev_start + '-' + prev_end);
  }

  node.appendChild(fragment);
};

/**
 *
 * @private
 */
ydn.ui.LazyList.prototype.cleanUpItems_ = function() {
  var el = this.getElement();
  var badNodes = el.querySelectorAll('[data-rm="1"]');
  for (var i = 0, l = badNodes.length; i < l; i++) {
    el.removeChild(badNodes[i]);
  }
  if (ydn.ui.LazyList.DEBUG) {
    console.log(badNodes.length + ' elements clean');
  }
};

/**
 * @param {string} w
 * @param {string} h
 * @param {goog.dom.TagName=} opt_tag
 * @return {Element}
 * @private
 */
ydn.ui.LazyList.createContainer_ = function(w, h, opt_tag) {
  var c = document.createElement(opt_tag || goog.dom.TagName.DIV);
  c.style.width = w;
  c.style.height = h;
  c.style.overflowX = 'hidden';
  c.style.overflowY = 'scroll';
  c.style.position = 'relative';
  c.style.padding = 0;
  return c;
};

/**
 * @private
 * @param {number} h
 * @return {Element}
 */
ydn.ui.LazyList.createScroller_ = function(h) {
  var scroller = document.createElement('div');
  scroller.classList.add('ydn-lazy-list-scroller');
  scroller.style.opacity = 0;
  // scroller.style.position = 'absolute';
  scroller.style.top = '0';
  scroller.style.left = '0';
  scroller.style.width = '1px';
  scroller.style.height = h + 'px';
  return scroller;
};

<|endoftext|>


<commit_before>'use strict';

// Declare app level module which depends on views, and components
angular.module('schedulerApp', [
  'ngRoute',
  'ngResource'
])
.factory('db', [ '$resource', '$q', '$rootScope', '$location', function($resource, $q, $rootScope, $location) {

	var
		Things = $resource("http://"+$location.host()+":8000/rest/v1/things", {}),
		Rooms = $resource("http://"+$location.host()+":8000/rest/v1/rooms", {}),
		Tasks = $resource("http://"+$location.host()+":"+$location.port()+"/rest/v1/tasks", {}),
		Task = $resource("http://"+$location.host()+":"+$location.port()+"/rest/v1/tasks/:id", {}),
		refreshThings = function() {
			return Things.get({}).$promise.then(
				function(things) {
					service.things = {}
					angular.forEach(things.data,
						function(thing) {
							var found = false
							if (!thing.promoted || !thing.name || !thing.location) {
								// console.debug("skipping thing because not promoted, described or assigned to room", thing)
								return
							}
							angular.forEach(thing.device.channels, function(channel) {
								if (channel.supportedMethods && channel.supportedMethods.indexOf("turnOff") >= 0) {
									found = true
								}
							})
							if (found) {
								service.things[thing["id"]] = thing
							}
						}
					)
					return service
				}
			)
		},
		refreshRooms = function() {
			return Rooms.get({}).$promise.then(
				function(rooms) {
					service.rooms = {}
					angular.forEach(rooms.data, function(room) {
						service.rooms[room["id"]] = room
					})
					return service
				}
			)
		},
		refreshTasks = function() {
			return Tasks.get({}).$promise.then(
				function(tasks) {
					service.tasks = {}
					angular.forEach(tasks.schedule, function(task) {
						if (!task.tags || task.tags.indexOf("simple-ui") < 0) {
							return;
						}
						if (task.description && task.description != '') {
							service.tasks[task["id"]] = task
						}
					})
					return service
				}
			)
		},
		service = {
			things: { } ,
			rooms: { } ,
			tasks: { } ,
			save: function(task) {
				var deferred = $q.defer()
				Tasks.save(
					{},
					task,
					function(r) {
						refreshTasks().then(
							function() { deferred.resolve(r) },
							deferred.reject
						)},
					deferred.reject)
				return deferred.promise
			},
			delete: function(id) {
				var deferred = $q.defer()
				Task.delete({"id": id},
					function(r) {
						refreshTasks().then(function() { deferred.resolve(r)}, deferred.reject)
					},
					deferred.reject
				)
				return deferred.promise
			},
			refresh: function() {
				return $q.all([ refreshRooms(),	refreshThings(), refreshTasks() ]).then(
					function() {
						return service
					}
				)
			}
		}
	return service
}])
.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/list', {templateUrl: 'task-list.html', controller: 'TaskList'});
  $routeProvider.when('/edit/:id', {templateUrl: 'task-edit.html', controller: 'TaskEdit'});
  $routeProvider.when('/create', {templateUrl: 'task-edit.html', controller: 'TaskEdit'});
  $routeProvider.otherwise({redirectTo: '/list'});
}])
.controller('ModelRefresh', ['$scope', 'db', function($scope, db) {
	db.refresh().then(
		function(things) {
			return things
		},
		function(m) {
			if (m && m.data) {
				$scope.message = m.data
			} else {
				$scope.message = "An error occurred."
			}
			console.debug("an error occurred while refreshing the model: ", m)
		}
	)
}])
.controller('TaskEdit', ['$scope', '$location', 'db', '$routeParams', function($scope, $location, db, $routeParams) {

	$scope.actionModels = {};
	$scope.isDescriptionFrozen = false
	$scope.repeatDaily = true

	$scope.thingToModel = function(thing) {
		var result = {}
		result.id = thing.id
		result.description = thing.name
		var room = db.rooms[thing.location]
		if (!room) {
			return null
		}
		result.room = room.name
		result.on = "true"
		return result
	}

	$scope.formatTime = function (date) {
	    var h = date.getHours();
	    var m = date.getMinutes();
	    var s = date.getSeconds();
	    var pad = function(d) { return d <= 9 ? '0'+d : d }
	    return ''+pad(h)+":"+pad(m)+":"+pad(s)
	}

	$scope.formatDate = function (date) {
	    var y = date.getFullYear();
	    var m = date.getMonth()+1;
	    var d = date.getDate();
	    var pad = function(d) { return d <= 9 ? '0'+d : d }
	    return ''+y+"-"+pad(m)+"-"+pad(d)
	}

	$scope.actionToModel = function(action) {
	    // {
        //    "action" : "turnOn",
        //    "thingID" : "94dc7d1a-aa67-11e4-803a-7c669d02a706",
        //     "type" : "thing-action"
        // }
        //
        // ==>
        //
	    // {
	    //	  "id": thing-id
        //    "description" : "thing description",
        //	  "room": "room description"
        //	  "on": true
        // }
        //

		var result = {}
		switch (action["action"]) {
		case "turnOn":
			result.on = "true"
			break
		case "turnOff":
			result.on = "false"
			break
		default:
			console.debug("bad action ", action)
			return null
		}

		if (action.type != "thing-action") {
			console.debug("bad type ", action.type)
			return null
		}

		if (!action.thingID) {
			console.debug("missing thing id")
			return null
		}

		result.id = action.thingID
		var thing = db.things[result.id]
		if (!thing) {
			console.debug("invalid thing id", result.id)
			return null
		}
		result.description = thing.name
		var room = db.rooms[thing.location]
		if (!room) {
			console.debug("invalid room id", thing.location)
			return null
		}
		result.room = room.name
		result.selected = true

		return result
	}

	$scope.task = function() {
		return db.tasks[$routeParams["id"]]
	}

	$scope.save = function() {
		$scope.message = ""
		var rule, param

		switch ($scope.timeOfDay) {
		case "dawn":
		case "dusk":
		case "sunrise":
		case "sunset":
			rule = $scope.timeOfDay
			param = ""
			break
		default:
			var
				now = new Date(),
				ts = new Date($scope.formatDate(now)+" "+$scope.timeOfDay)

			if (!ts.getFullYear()) {
				$scope.message = "enter a time of the form hh:mm:dd"
				return
			} else {
				if (ts < now) {
					ts.setDate(ts.getDate()+1)
				}
				if ($scope.repeatDaily) {
					rule = "time-of-day"
					param = $scope.formatTime(ts)
				} else {
					rule = "timestamp"
					param = $scope.formatDate(ts) + " " + $scope.formatTime(ts)
				}
			}
			break
		}

		var desc = (($scope.description == '') ? '@ '+$scope.timeOfDay : $scope.description )

		var open = (function() {
			var results = []
			angular.forEach($scope.actionModels, function(m) {
				if (m.selected) {
					results.push(
						{
							"type": "thing-action",
							"action": (m.on == "true" ? "turnOn" : "turnOff"),
							"thingID": m.id
						}
					)
				}
			})
			return results
		}())

		var task = {
			"id": $routeParams["id"],
			"description": desc,
			"tags": ["simple-ui"],
			"open": open,
			"close": [],
			"window": {
				"after": {
					"rule": rule,
					"param": param
				},
				"before": {
					"rule": "delay",
					"param": "00:01:00"
				}
			}
		}

		db.save(task).then(
			function() {
				$location.path('/list')
			}, function(m) {
				if (m && m.data) {
					$scope.message = m.data
				} else {
					$scope.message = "An error occurred."
				}
				console.debug(m)
			})
	}

	$scope.delete = function() {
		db.delete($routeParams["id"]).then(
			function() {
				$location.path('/list')
			}, function(m) {
				if (m && m.data) {
					$scope.message = m.data
				} else {
					$scope.message = "An error occurred."
				}
				console.debug(m)
			})
	}

	$scope.cancel = function() {
		$location.path('/list')
	}

	$scope.freezeDescription = function() {
		$scope.isDescriptionFrozen = true
	}

	$scope.toggleSelect = function(model) {
		model.selected = !model.selected
	}

	$scope.toggleActionState = function(model) {
		model.on = ! model.on
	}

	$scope.setRepeatDaily = function(value) {
		$scope.repeatDaily = value;
	}

	$scope.$watch('task()', function(task) {
		angular.forEach(db.things, function(t) {
			var m = $scope.thingToModel(t)
			if (m) {
				$scope.actionModels[t["id"]] = m
			}
		})

		$scope.isDescriptionFrozen = false
		$scope.timeOfDay = $scope.formatTime(new Date(new Date().valueOf()+(60*1000)))
		$scope.description = '@ '+$scope.timeOfDay
		$scope.repeatDaily = true

		if (!task) {
			return
		}

		$scope.isDescriptionFrozen = true
		$scope.description = task.description

		angular.forEach(task.open, function(action) {
			var model = $scope.actionToModel(action)
			if (model && $scope.actionModels[model.id]) {
				$scope.actionModels[model.id] = model
			} else {
				console.debug("found an action for a thing that no longer exists: ", action)
			}
		})

		switch (task.window.after.rule) {
		case "sunrise":
		case "sunset":
		case "dawn":
		case "dusk":
			$scope.timeOfDay = task.window.after.rule
			break;
		case "time-of-day":
			$scope.timeOfDay = task.window.after.param
			break;
		case "timestamp":
			var ts = new Date(task.window.after.param)
			if (ts.getFullYear()) {
				$scope.timeOfDay = $scope.formatTime(ts)
				$scope.repeatDaily = false
			}
			break;
		default:
			console.debug("can't edit rule of type: ", task.window.after.rule)
			return
		}

		// the description is frozen iff the generated description does not match the saved description
		$scope.isDescriptionFrozen = ($scope.description != '') && ($scope.description != $scope.generatedDescription())

	})

	$scope.generatedDescription = function() {
		return '@ '+$scope.timeOfDay
	}

	$scope.$watch('timeOfDay', function() {
		if (!$scope.isDescriptionFrozen) {
			$scope.description = $scope.generatedDescription()
		}
	});

}])
.controller('TaskList', ['$scope', 'db', '$rootScope', function($scope, db, $rootScope) {
	$scope.tasks = {}
	$scope.db = db
	$scope.$watch("db.tasks", function(tasks) {
		$scope.tasks = db.tasks
	})
}])

<commit_msg>Hide once-off tasks which have already passed their open interval.<commit_after>'use strict';

// Declare app level module which depends on views, and components
angular.module('schedulerApp', [
  'ngRoute',
  'ngResource'
])
.factory('db', [ '$resource', '$q', '$rootScope', '$location', function($resource, $q, $rootScope, $location) {

	var
		Things = $resource("http://"+$location.host()+":8000/rest/v1/things", {}),
		Rooms = $resource("http://"+$location.host()+":8000/rest/v1/rooms", {}),
		Tasks = $resource("http://"+$location.host()+":"+$location.port()+"/rest/v1/tasks", {}),
		Task = $resource("http://"+$location.host()+":"+$location.port()+"/rest/v1/tasks/:id", {}),
		refreshThings = function() {
			return Things.get({}).$promise.then(
				function(things) {
					service.things = {}
					angular.forEach(things.data,
						function(thing) {
							var found = false
							if (!thing.promoted || !thing.name || !thing.location) {
								// console.debug("skipping thing because not promoted, described or assigned to room", thing)
								return
							}
							angular.forEach(thing.device.channels, function(channel) {
								if (channel.supportedMethods && channel.supportedMethods.indexOf("turnOff") >= 0) {
									found = true
								}
							})
							if (found) {
								service.things[thing["id"]] = thing
							}
						}
					)
					return service
				}
			)
		},
		refreshRooms = function() {
			return Rooms.get({}).$promise.then(
				function(rooms) {
					service.rooms = {}
					angular.forEach(rooms.data, function(room) {
						service.rooms[room["id"]] = room
					})
					return service
				}
			)
		},
		refreshTasks = function() {
			return Tasks.get({}).$promise.then(
				function(tasks) {
					service.tasks = {}
					angular.forEach(tasks.schedule, function(task) {

						if (!task.tags || task.tags.indexOf("simple-ui") < 0) {
							return;
						}

						if (task.window.after.rule == 'timestamp') {
							var
								now = new Date(),
								ts = new Date(task.window.after.param)
							if (! ts.getFullYear() || ts < now) {
								// console.debug("skipping expired or invalid task")
								return
							}
						}

						if (task.description && task.description != '') {
							service.tasks[task["id"]] = task
						}
					})
					return service
				}
			)
		},
		service = {
			things: { } ,
			rooms: { } ,
			tasks: { } ,
			save: function(task) {
				var deferred = $q.defer()
				Tasks.save(
					{},
					task,
					function(r) {
						refreshTasks().then(
							function() { deferred.resolve(r) },
							deferred.reject
						)},
					deferred.reject)
				return deferred.promise
			},
			delete: function(id) {
				var deferred = $q.defer()
				Task.delete({"id": id},
					function(r) {
						refreshTasks().then(function() { deferred.resolve(r)}, deferred.reject)
					},
					deferred.reject
				)
				return deferred.promise
			},
			refresh: function() {
				return $q.all([ refreshRooms(),	refreshThings(), refreshTasks() ]).then(
					function() {
						return service
					}
				)
			}
		}
	return service
}])
.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/list', {templateUrl: 'task-list.html', controller: 'TaskList'});
  $routeProvider.when('/edit/:id', {templateUrl: 'task-edit.html', controller: 'TaskEdit'});
  $routeProvider.when('/create', {templateUrl: 'task-edit.html', controller: 'TaskEdit'});
  $routeProvider.otherwise({redirectTo: '/list'});
}])
.controller('ModelRefresh', ['$scope', 'db', function($scope, db) {
	db.refresh().then(
		function(things) {
			return things
		},
		function(m) {
			if (m && m.data) {
				$scope.message = m.data
			} else {
				$scope.message = "An error occurred."
			}
			console.debug("an error occurred while refreshing the model: ", m)
		}
	)
}])
.controller('TaskEdit', ['$scope', '$location', 'db', '$routeParams', function($scope, $location, db, $routeParams) {

	$scope.actionModels = {};
	$scope.isDescriptionFrozen = false
	$scope.repeatDaily = true

	$scope.thingToModel = function(thing) {
		var result = {}
		result.id = thing.id
		result.description = thing.name
		var room = db.rooms[thing.location]
		if (!room) {
			return null
		}
		result.room = room.name
		result.on = "true"
		return result
	}

	$scope.formatTime = function (date) {
	    var h = date.getHours();
	    var m = date.getMinutes();
	    var s = date.getSeconds();
	    var pad = function(d) { return d <= 9 ? '0'+d : d }
	    return ''+pad(h)+":"+pad(m)+":"+pad(s)
	}

	$scope.formatDate = function (date) {
	    var y = date.getFullYear();
	    var m = date.getMonth()+1;
	    var d = date.getDate();
	    var pad = function(d) { return d <= 9 ? '0'+d : d }
	    return ''+y+"-"+pad(m)+"-"+pad(d)
	}

	$scope.actionToModel = function(action) {
	    // {
        //    "action" : "turnOn",
        //    "thingID" : "94dc7d1a-aa67-11e4-803a-7c669d02a706",
        //     "type" : "thing-action"
        // }
        //
        // ==>
        //
	    // {
	    //	  "id": thing-id
        //    "description" : "thing description",
        //	  "room": "room description"
        //	  "on": true
        // }
        //

		var result = {}
		switch (action["action"]) {
		case "turnOn":
			result.on = "true"
			break
		case "turnOff":
			result.on = "false"
			break
		default:
			console.debug("bad action ", action)
			return null
		}

		if (action.type != "thing-action") {
			console.debug("bad type ", action.type)
			return null
		}

		if (!action.thingID) {
			console.debug("missing thing id")
			return null
		}

		result.id = action.thingID
		var thing = db.things[result.id]
		if (!thing) {
			console.debug("invalid thing id", result.id)
			return null
		}
		result.description = thing.name
		var room = db.rooms[thing.location]
		if (!room) {
			console.debug("invalid room id", thing.location)
			return null
		}
		result.room = room.name
		result.selected = true

		return result
	}

	$scope.task = function() {
		return db.tasks[$routeParams["id"]]
	}

	$scope.save = function() {
		$scope.message = ""
		var rule, param

		switch ($scope.timeOfDay) {
		case "dawn":
		case "dusk":
		case "sunrise":
		case "sunset":
			rule = $scope.timeOfDay
			param = ""
			break
		default:
			var
				now = new Date(),
				ts = new Date($scope.formatDate(now)+" "+$scope.timeOfDay)

			if (!ts.getFullYear()) {
				$scope.message = "enter a time of the form hh:mm:dd"
				return
			} else {
				if (ts < now) {
					ts.setDate(ts.getDate()+1)
				}
				if ($scope.repeatDaily) {
					rule = "time-of-day"
					param = $scope.formatTime(ts)
				} else {
					rule = "timestamp"
					param = $scope.formatDate(ts) + " " + $scope.formatTime(ts)
				}
			}
			break
		}

		var desc = (($scope.description == '') ? '@ '+$scope.timeOfDay : $scope.description )

		var open = (function() {
			var results = []
			angular.forEach($scope.actionModels, function(m) {
				if (m.selected) {
					results.push(
						{
							"type": "thing-action",
							"action": (m.on == "true" ? "turnOn" : "turnOff"),
							"thingID": m.id
						}
					)
				}
			})
			return results
		}())

		var task = {
			"id": $routeParams["id"],
			"description": desc,
			"tags": ["simple-ui"],
			"open": open,
			"close": [],
			"window": {
				"after": {
					"rule": rule,
					"param": param
				},
				"before": {
					"rule": "delay",
					"param": "00:01:00"
				}
			}
		}

		db.save(task).then(
			function() {
				$location.path('/list')
			}, function(m) {
				if (m && m.data) {
					$scope.message = m.data
				} else {
					$scope.message = "An error occurred."
				}
				console.debug(m)
			})
	}

	$scope.delete = function() {
		db.delete($routeParams["id"]).then(
			function() {
				$location.path('/list')
			}, function(m) {
				if (m && m.data) {
					$scope.message = m.data
				} else {
					$scope.message = "An error occurred."
				}
				console.debug(m)
			})
	}

	$scope.cancel = function() {
		$location.path('/list')
	}

	$scope.freezeDescription = function() {
		$scope.isDescriptionFrozen = true
	}

	$scope.toggleSelect = function(model) {
		model.selected = !model.selected
	}

	$scope.toggleActionState = function(model) {
		model.on = ! model.on
	}

	$scope.setRepeatDaily = function(value) {
		$scope.repeatDaily = value;
	}

	$scope.$watch('task()', function(task) {
		angular.forEach(db.things, function(t) {
			var m = $scope.thingToModel(t)
			if (m) {
				$scope.actionModels[t["id"]] = m
			}
		})

		$scope.isDescriptionFrozen = false
		$scope.timeOfDay = $scope.formatTime(new Date(new Date().valueOf()+(60*1000)))
		$scope.description = '@ '+$scope.timeOfDay
		$scope.repeatDaily = true

		if (!task) {
			return
		}

		$scope.isDescriptionFrozen = true
		$scope.description = task.description

		angular.forEach(task.open, function(action) {
			var model = $scope.actionToModel(action)
			if (model && $scope.actionModels[model.id]) {
				$scope.actionModels[model.id] = model
			} else {
				console.debug("found an action for a thing that no longer exists: ", action)
			}
		})

		switch (task.window.after.rule) {
		case "sunrise":
		case "sunset":
		case "dawn":
		case "dusk":
			$scope.timeOfDay = task.window.after.rule
			break;
		case "time-of-day":
			$scope.timeOfDay = task.window.after.param
			break;
		case "timestamp":
			var ts = new Date(task.window.after.param)
			if (ts.getFullYear()) {
				$scope.timeOfDay = $scope.formatTime(ts)
				$scope.repeatDaily = false
			}
			break;
		default:
			console.debug("can't edit rule of type: ", task.window.after.rule)
			return
		}

		// the description is frozen iff the generated description does not match the saved description
		$scope.isDescriptionFrozen = ($scope.description != '') && ($scope.description != $scope.generatedDescription())

	})

	$scope.generatedDescription = function() {
		return '@ '+$scope.timeOfDay
	}

	$scope.$watch('timeOfDay', function() {
		if (!$scope.isDescriptionFrozen) {
			$scope.description = $scope.generatedDescription()
		}
	});

}])
.controller('TaskList', ['$scope', 'db', '$rootScope', function($scope, db, $rootScope) {
	$scope.tasks = {}
	$scope.db = db
	$scope.$watch("db.tasks", function(tasks) {
		$scope.tasks = db.tasks
	})
}])

<|endoftext|>


<commit_before>(function($){
    $(function(){

        $('.button-collapse').sideNav();

    }); // end of document ready
})(jQuery); // end of jQuery name space

Vue.component('navbar', {
    template: `

          Pulp

Settings

Settings

          menu

    `
})

Vue.component('preference', {
    template: `

I want

I don't want

            <input type="checkbox" id="1" checked="checked" />
            $

            <input type="checkbox" id="2" checked="checked" />
            $$

            <input type="checkbox" id="3" checked="checked" />
            $$$

            <input type="checkbox" id="4" checked="checked" />
            $$$$

            <input id="walk" type="checkbox">

            Within walking distance

    `
})

Vue.component('card', {
    template: `

{{ name }}

                {{ rating }}

    `,
    props: ['image_url', 'name', 'rating']
})

Vue.component('results', {
    template: `

            <card
                v-bind:image_url="businesses[i-1].image_url"
                v-bind:name="businesses[i-1].name"
                v-bind:rating="businesses[i-1].rating">
            </card>

    `,
    props: ['businesses']
})

Vue.component('main-view', {
    template: `

            <preference></preference>
            <results v-bind:businesses="businesses"></results>

    `,
    props: ['businesses']
})

Vue.component('app', {
    template: `

      <navbar></navbar>

    `,
    props: ['businesses']
})

// create a root instance

const v = new Vue({
  el: '#root',
  data: {
    businesses: [{
        image_url: "https://s3-media2.fl.yelpcdn.com/bphoto/XwiV9HD8LRciCYCANDsUbA/o.jpg",
        name: "OMG RESTAURANT",
        rating: 3
    }]
  }
})

function queryServer(q) {
    var server_url = 'http://127.0.0.1:5000/query';
    // send query
    $.get(server_url, {query: JSON.stringify(q)}, function (response, status) {
        if (status === "success") {
            console.log(response);
            v.businesses = response.businesses;

            // update result cards

        } else {
            console.log("FAILED SERVER REQUEST")
            console.log(response);
        }
    }, 'json');
}

$(document).ready(function() {

    Materialize.updateTextFields();

    var query = {
        'lat': 37.868654,
        'long': -122.259153,
        'like': [],
        'dislike': [],
        'open_now': false,
        'price': ["1","2","3","4"],
        'radius': 1000
    };

    function savePosition(position) {
        query.lat = position.coords.latitude;
        query.long = position.coords.longitude;
    }
    navigator.geolocation.getCurrentPosition(savePosition);

    queryServer(query);

    var cuisine_options = {
        'thai': null,
        'chinese': null,
        'japanese': null,
        'indian': null,
        'fusion': null,
        'burmese': null,
        'italian': null,
        'french': null,
    };

    $('.chips-wlist').material_chip({
        autocompleteOptions: {
            placeholder: 'Enter a food/cuisine you like',
            data: cuisine_options,
            limit: 3,
            minLength: 1
        }
    });

    $('.chips-blist').material_chip({
        autocompleteOptions: {
            placeholder: 'Enter a food/cuisine you aren\'t feeling',
            data: cuisine_options,
            limit: Infinity,
            minLength: 1
        }
    });

    $('.chips-wlist').on('chip.add', function(e, chip){
        console.log("ADDED LIKE");
        console.log(chip);
        query.like.push(chip.tag);
        console.log(query);
        queryServer(query);
    });

    $('.chips-wlist').on('chip.delete', function(e, chip){
        query.like.splice(query.like.indexOf(chip.tag), 1);
        queryServer(query);
    });

    $('.chips-blist').on('chip.add', function(e, chip){
        query.dislike.push(chip.tag);
        queryServer(query);
    });

    $('.chips-blist').on('chip.delete', function(e, chip){
        query.dislike.splice(query.dislike.indexOf(chip.tag), 1);
        queryServer(query);
    });

    $('#walk').change(function () {
        query.radius = 900;
        queryServer(query);
    });

    $('#price').change(function(e) {
        if (e.originalEvent.target.checked) {
            query.price.push(e.originalEvent.target.id);
        } else {
            query.price.splice(query.price.indexOf(e.originalEvent.target.id), 1);
        }
        console.log(e.originalEvent.target.checked);
        console.log(query);

        queryServer(query);
    });

});<commit_msg>minor cleanup<commit_after>(function($){
    $(function(){

        $('.button-collapse').sideNav();

    }); // end of document ready
})(jQuery); // end of jQuery name space

Vue.component('navbar', {
    template: `

            Pulp

Settings

Settings

            menu

    `
})

Vue.component('preference', {
    template: `

I want

I don't want

                <input type="checkbox" id="1" checked="checked" />
                $

                <input type="checkbox" id="2" checked="checked" />
                $$

                <input type="checkbox" id="3" checked="checked" />
                $$$

                <input type="checkbox" id="4" checked="checked" />
                $$$$

                <input id="walk" type="checkbox">

                Within walking distance

    `
})

Vue.component('card', {
    // add yes / no
    template: `

{{ name }}

            {{ rating }}

    `,
    props: ['image_url', 'name', 'rating']
})

Vue.component('results', {
    template: `

            <card
                v-bind:image_url="businesses[i-1].image_url"
                v-bind:name="businesses[i-1].name"
                v-bind:rating="businesses[i-1].rating">
            </card>

    `,
    props: ['businesses']
})

Vue.component('main-view', {
    template: `

            <preference></preference>
            <results v-bind:businesses="businesses"></results>

    `,
    props: ['businesses']
})

Vue.component('app', {
    template: `

      <navbar></navbar>

    `,
    props: ['businesses']
})

// create a root instance

const v = new Vue({
    el: '#root',
    data: {
    businesses: [{
        image_url: "https://s3-media2.fl.yelpcdn.com/bphoto/XwiV9HD8LRciCYCANDsUbA/o.jpg",
        name: "OMG RESTAURANT",
        rating: 3
    }]
  }
})

function queryServer(q) {
    var server_url = 'http://127.0.0.1:5000/query';

    console.log(q);
    // send query
    $.get(server_url, {query: JSON.stringify(q)}, function (response, status) {
        if (status === "success") {
            console.log(response);

            // update result cards
            v.businesses = response.businesses;

        } else {
            console.log("FAILED SERVER REQUEST")
            console.log(response);
        }
    }, 'json');
}

$(document).ready(function() {

    Materialize.updateTextFields();

    var query = {
        'lat': 37.868654,
        'long': -122.259153,
        'like': [],
        'dislike': [],
        'open_now': false,
        'price': ["1","2","3","4"],
        'radius': 1000
    };

    function savePosition(position) {
        query.lat = position.coords.latitude;
        query.long = position.coords.longitude;
    }
    navigator.geolocation.getCurrentPosition(savePosition);

    queryServer(query);

    var cuisine_options = {
        'thai': null,
        'chinese': null,
        'japanese': null,
        'indian': null,
        'fusion': null,
        'burmese': null,
        'italian': null,
        'french': null,
    };

    $('.chips-wlist').material_chip({
        autocompleteOptions: {
            placeholder: 'Enter a food/cuisine you like',
            data: cuisine_options,
            limit: 3,
            minLength: 1
        }
    });

    $('.chips-blist').material_chip({
        autocompleteOptions: {
            placeholder: 'Enter a food/cuisine you aren\'t feeling',
            data: cuisine_options,
            limit: Infinity,
            minLength: 1
        }
    });

    $('.chips-wlist').on('chip.add', function(e, chip){
        query.like.push(chip.tag);
        queryServer(query);
    });

    $('.chips-wlist').on('chip.delete', function(e, chip){
        query.like.splice(query.like.indexOf(chip.tag), 1);
        queryServer(query);
        // add filter on v.businesses
    });

    $('.chips-blist').on('chip.add', function(e, chip){
        query.dislike.push(chip.tag);
        queryServer(query);
    });

    $('.chips-blist').on('chip.delete', function(e, chip){
        query.dislike.splice(query.dislike.indexOf(chip.tag), 1);
        queryServer(query);
    });

    $('#walk').change(function () {
        query.radius = 900;
        queryServer(query);
    });

    $('#price').change(function(e) {
        if (e.originalEvent.target.checked) {
            query.price.push(e.originalEvent.target.id);
        } else {
            query.price.splice(query.price.indexOf(e.originalEvent.target.id), 1);
        }
        console.log(e.originalEvent.target.checked);
        queryServer(query);
    });

});<|endoftext|>


<commit_before>/**
 * Copyright 2014 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

var HypervisorView = Backbone.View.extend({

    defaults: {
        margin: {
            top: 10,
            right: 10,
            bottom: 18,
            left: 25
        }
    },

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.defaults.url = this.collection.url;
        this.el = options.el;
        this.defaults.width = options.width;
        this.defaults.axisLabel = options.axisLabel;

        var ns = this.defaults;
        var self = this;

        this.collection.on('sync', this.update, this);

        this.on('selectorChanged', function(){
            console.log('HypervisorView heard change');
        });

        ns.mw = ns.width - ns.margin.left - ns.margin.right;
        ns.mh = ns.width * 2 - ns.margin.top - ns.margin.bottom;

        ns.x = d3.time.scale()
            .range([0, ns.mw]);

        ns.y = d3.scale.linear()
            .range([ns.mh, 0]);

        var colorArray = new GoldstoneColors().get('colorSets');
        ns.color = d3.scale.ordinal().range(colorArray.distinct[5]);

        ns.yAxis = d3.svg.axis()
            .scale(ns.y)
            .orient("left");

        ns.svg = d3.select(this.el).append("svg")
            .attr("width", ns.mw + ns.margin.left + ns.margin.right)
            .attr("height", ns.mh + ns.margin.top + ns.margin.bottom)
            .append("g")
            .attr("transform", "translate(" + ns.margin.left + "," + ns.margin.top + ")");

        // tooltip:
        // [top-offset, left-offset]
        ns.tooltip = d3.tip()
            .attr('class', 'd3-tip')
            .offset([-5, 0])
            .html(function(d) {
                d.name = d.name || 'No name reported';
                d.cores = d.y1 - d.y0 || 'No core count reported';

                return "vm: " + d.name + "
" +
                    d.cores + " " + ns.axisLabel;
            });

        ns.svg.call(ns.tooltip);

        // required in case spinner loading takes
        // longer than chart loading
        ns.spinnerDisplay = 'inline';

        $('').load(function() {
            $(this).appendTo(this.el).css({
                'position': 'relative',
                'margin-top': -(ns.mh / 2),
                'display': ns.spinnerDisplay

            });
        });

    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // sets css for spinner to hidden in case
        // spinner callback resolves
        // after chart data callback
        ns.spinnerDisplay = 'none';
        $(this.el).find('#spinner').hide();

        // default 10 second refresh interval
        setTimeout(function() {
            self.collection.fetch();
        }, 10000);

        var allthelogs = this.collection.toJSON();

        // If we didn't receive any valid files, append "No Data Returned"
        if (allthelogs.length === 0) {

            // if 'no data returned' already exists on page, don't reapply it
            if ($(this.el).find('#noDataReturned').length) {
                return;
            }

            $('
No
Data
Returned').appendTo(this.el)
                .css({
                    'position': 'relative',
                    'margin-left': $(this.el).width() / 2 - 14,
                    'top': -$(this.el).height() / 2
                });
            return;
        }

        // remove No Data Returned once data starts flowing again
        if ($(this.el).find('#noDataReturned').length) {
            $(this.el).find('#noDataReturned').remove();
        }

        var data = allthelogs;

        ns.color.domain(d3.keys(data[0]).filter(function(key) {
            return key !== "date";
        }));

        data.forEach(function(d) {
            var y0 = 0;
            d.cores = ns.color.domain().map(function(name) {
                return {
                    name: name,
                    y0: y0,
                    y1: y0 += +d[name]
                };
            });
            d.total = d.cores[d.cores.length - 1].y1;
        });

        data.sort(function(a, b) {
            return b.total - a.total;
        });

        ns.x.domain(d3.extent(data, function(d) {
            return d.date;
        }));

        ns.y.domain([0, d3.max(data, function(d) {
            return d.total;
        })]);

        ns.svg.selectAll('rect')
            .remove();

        ns.svg.selectAll("g").remove();

        ns.svg.append("g")
            .attr("class", "y axis")
            .call(ns.yAxis)
            .append("text")
            .attr("transform", "rotate(0)")
            .attr("x", 4)
            .attr("y", 0)
            .attr("dy", ".71em")
            .style("text-anchor", "beginning")
            .text("Total " + ns.axisLabel + ": " + ns.y.domain()[1]);

        var vmCore = ns.svg.selectAll(".vmCore")
            .data(data)
            .enter().append("g")
            .attr("class", "g");

        vmCore.selectAll("rect")
            .data(function(d) {
                return d.cores;
            })
            .enter().append("rect")
            .style("fill", "#f5f5f5")
            .attr("width", ns.mw)
            .attr("y", function(d) {
                return ns.y(d.y1);
            })
            .attr("height", function(d) {
                return ns.y(d.y0) - ns.y(d.y1);
            })
            .on("mouseover", ns.tooltip.show)
            .on("mouseout", function() {
                ns.tooltip.hide();
            })
            .transition()
            .style("fill", function(d) {
                if (d.name === "available") {
                    return 'none';
                }
                return ns.color(d.name);
            })
            .style("opacity", 0.8);

        var legend = ns.svg.selectAll(".legend")
            .data(data);

        legend
            .enter().append("g")
            .attr("class", "legend");

        legend.text('');

        legend.append("text")
            .attr("x", ns.mw / 2)
            .attr("y", ns.mh + 12)
            .attr("font-size", "11px")
            .attr("font-weight", "bold")
            .attr("text-anchor", "middle")
            .text(function(d) {
                return moment(d.date).calendar();
            });

    }

});
<commit_msg>Wires up hypervisorView viz to 'selectorChanged' trigger from nodeReport view.<commit_after>/**
 * Copyright 2014 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Alex Jacobs
 */

var HypervisorView = Backbone.View.extend({

    defaults: {
        margin: {
            top: 10,
            right: 10,
            bottom: 18,
            left: 25
        }
    },

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.defaults.url = this.collection.url;
        this.el = options.el;
        this.defaults.width = options.width;
        this.defaults.axisLabel = options.axisLabel;

        var ns = this.defaults;
        var self = this;

        this.collection.on('sync', this.update, this);

        this.on('selectorChanged', function() {
            self.collection.fetch();
        });

        ns.mw = ns.width - ns.margin.left - ns.margin.right;
        ns.mh = ns.width * 2 - ns.margin.top - ns.margin.bottom;

        ns.x = d3.time.scale()
            .range([0, ns.mw]);

        ns.y = d3.scale.linear()
            .range([ns.mh, 0]);

        var colorArray = new GoldstoneColors().get('colorSets');
        ns.color = d3.scale.ordinal().range(colorArray.distinct[5]);

        ns.yAxis = d3.svg.axis()
            .scale(ns.y)
            .orient("left");

        ns.svg = d3.select(this.el).append("svg")
            .attr("width", ns.mw + ns.margin.left + ns.margin.right)
            .attr("height", ns.mh + ns.margin.top + ns.margin.bottom)
            .append("g")
            .attr("transform", "translate(" + ns.margin.left + "," + ns.margin.top + ")");

        // tooltip:
        // [top-offset, left-offset]
        ns.tooltip = d3.tip()
            .attr('class', 'd3-tip')
            .offset([-5, 0])
            .html(function(d) {
                d.name = d.name || 'No name reported';
                d.cores = d.y1 - d.y0 || 'No core count reported';

                return "vm: " + d.name + "
" +
                    d.cores + " " + ns.axisLabel;
            });

        ns.svg.call(ns.tooltip);

        // required in case spinner loading takes
        // longer than chart loading
        ns.spinnerDisplay = 'inline';

        $('').load(function() {
            $(this).appendTo(this.el).css({
                'position': 'relative',
                'margin-top': -(ns.mh / 2),
                'display': ns.spinnerDisplay

            });
        });

    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // sets css for spinner to hidden in case
        // spinner callback resolves
        // after chart data callback
        ns.spinnerDisplay = 'none';
        $(this.el).find('#spinner').hide();

        var allthelogs = this.collection.toJSON();

        // If we didn't receive any valid files, append "No Data Returned"
        if (allthelogs.length === 0) {

            // if 'no data returned' already exists on page, don't reapply it
            if ($(this.el).find('#noDataReturned').length) {
                return;
            }

            $('
No
Data
Returned').appendTo(this.el)
                .css({
                    'position': 'relative',
                    'margin-left': $(this.el).width() / 2 - 14,
                    'top': -$(this.el).height() / 2
                });
            return;
        }

        // remove No Data Returned once data starts flowing again
        if ($(this.el).find('#noDataReturned').length) {
            $(this.el).find('#noDataReturned').remove();
        }

        var data = allthelogs;

        ns.color.domain(d3.keys(data[0]).filter(function(key) {
            return key !== "date";
        }));

        data.forEach(function(d) {
            var y0 = 0;
            d.cores = ns.color.domain().map(function(name) {
                return {
                    name: name,
                    y0: y0,
                    y1: y0 += +d[name]
                };
            });
            d.total = d.cores[d.cores.length - 1].y1;
        });

        data.sort(function(a, b) {
            return b.total - a.total;
        });

        ns.x.domain(d3.extent(data, function(d) {
            return d.date;
        }));

        ns.y.domain([0, d3.max(data, function(d) {
            return d.total;
        })]);

        ns.svg.selectAll('rect')
            .remove();

        ns.svg.selectAll("g").remove();

        ns.svg.append("g")
            .attr("class", "y axis")
            .call(ns.yAxis)
            .append("text")
            .attr("transform", "rotate(0)")
            .attr("x", 4)
            .attr("y", 0)
            .attr("dy", ".71em")
            .style("text-anchor", "beginning")
            .text("Total " + ns.axisLabel + ": " + ns.y.domain()[1]);

        var vmCore = ns.svg.selectAll(".vmCore")
            .data(data)
            .enter().append("g")
            .attr("class", "g");

        vmCore.selectAll("rect")
            .data(function(d) {
                return d.cores;
            })
            .enter().append("rect")
            .style("fill", "#f5f5f5")
            .attr("width", ns.mw)
            .attr("y", function(d) {
                return ns.y(d.y1);
            })
            .attr("height", function(d) {
                return ns.y(d.y0) - ns.y(d.y1);
            })
            .on("mouseover", ns.tooltip.show)
            .on("mouseout", function() {
                ns.tooltip.hide();
            })
            .transition()
            .style("fill", function(d) {
                if (d.name === "available") {
                    return 'none';
                }
                return ns.color(d.name);
            })
            .style("opacity", 0.8);

        var legend = ns.svg.selectAll(".legend")
            .data(data);

        legend
            .enter().append("g")
            .attr("class", "legend");

        legend.text('');

        legend.append("text")
            .attr("x", ns.mw / 2)
            .attr("y", ns.mh + 12)
            .attr("font-size", "11px")
            .attr("font-weight", "bold")
            .attr("text-anchor", "middle")
            .text(function(d) {
                return moment(d.date).calendar();
            });

    }

});
<|endoftext|>


<commit_before>slideApp.controller("slideController", ["$scope", "$cookies", "$location", "$http", "$interval", "$timeout", "$window", function ($scope, $cookies, $location, $http, $interval, $timeout, $window) {
    console.info("[load] mainController @" + Date.now());

    window.__$scope = $scope;

    $scope.session = {
        session: "",
        qr: "",
        bookmarkContent: "",
        type: "",
        latency: 0,
        info: {
            observer: false,
            host: false,
            remotes: 0
        }
    };
    $scope.socket = io()

    $scope.getFromServer = function (what) {
        $scope.socket.emit("_get", {what: what})
    };

    //TODO: disable some (most) settings for the host
    $scope.settings = {
        username: '',
        navigationType: 'swipe',
        vibration: true,
        laserCalibration: {
            center: {
                yaw: 0,
                pitch: 0
            },
            range: {
                yaw: 90,
                pitch: 90
            }
        },
        laserStyle: {
            color: "red",
            'font-size': 15,
            _icon: "circle"
        },
        laserTool: 'pointer',

        put: function (name, value) {
            $timeout(function () {
                $scope.settings[name] = value;
                localStorage.setItem("rs-settings", JSON.stringify($scope.settings));
            });
        },
        save: function (callback) {
            localStorage.setItem("rs-settings", JSON.stringify($scope.settings));
            console.info("[Settings] Saved!")
            if (callback) callback();
            if ($scope.settings.saveCallback)$scope.settings.saveCallback($scope.settings);

            _gaq.push(["_trackEvent", "settings", $scope.settings.navigationType, "navigationType"]);
            _gaq.push(["_trackEvent", "settings", $scope.settings.vibration, "vibration"]);
            _gaq.push(["_trackEvent", "settings", $scope.settings.laserTool, "laserTool"]);
            _gaq.push(["_trackEvent", "settings", $scope.settings.laserStyle.color, "laserStyle.color"]);
        },
        saveCallback: undefined
    };
    //TODO: track some settings, like navigationType, vibration and laserStyle via GoogleAnalytics

    // Load settings
    var storedSettings = JSON.parse(localStorage.getItem("rs-settings")) || {};
    $.extend($scope.settings, storedSettings);

    $scope.openSettings = function () {
        $("#settingsModal").modal("show");
    };
    $scope.closeSettings = function () {
        $("#settingsModal").modal("hide");
    };

    $scope.showConnectionInfo = function () {
        $scope.getFromServer("connectionInfo");
        $("#connectionInfoModal").modal("show")
    };

    // Non-settings stuff
    $scope.extension = {
        id: {
            chrome: "enmfbjneielhjagdgebkmcdhgbbionek"
        },
        installed: false
    };
    try {
        chrome.runtime.sendMessage($scope.extension.id.chrome, {ping: "hello"}, function (msg) {
            console.log(msg)
            if (msg && msg.pong && msg.pong == 'hello') {
                $scope.extension.installed = true;
            }
        });
    } catch (ignoerd) {
    }

    $scope.isMobile = function () {
        return window.mobilecheck();
    };
    $scope.isDesktop = function () {
        return !window.mobilecheck();
    };

    $scope.statusIcon = {
        type: 'question',
        color: 'orange',
        message: '',
        messageVisible: false,
        hideTimeout: undefined,
        showMessage: function (type, color, vibration, msg, timeout, doneCallback) {
            $timeout(function () {
                if (type)
                    $scope.statusIcon.type = type;
                if (color)
                    $scope.statusIcon.color = color;

                $scope.statusIcon.messageVisible = false;
                if (msg) {
                    $scope.statusIcon.message = msg;
                    $scope.statusIcon.messageVisible = true;
                }
                if ($scope.settings.vibration && vibration) {
                    window.navigator.vibrate(vibration);
                }

                if (timeout) {
                    $timeout.cancel($scope.statusIcon.hideTimeout);
                    $timeout(function () {
                        $scope.statusIcon.messageVisible = false;
                        if (doneCallback)
                            doneCallback();
                    }, timeout);
                } else {
                    if (doneCallback)
                        doneCallback();
                }
            });
        },
        hideMessage: function () {
            $scope.statusIcon.messageVisible = false;
        }
    };

    $scope.infoModal = {
        title: "",
        show: function (title, source) {
            $scope.infoModal.title = title;
            $("#infoModalContent").load(source, function () {
                $("#infoModal").modal("show");
            });
        },
        onClose: function () {
            $timeout(function () {
                $("#infoModalContent").empty();
            }, 500);
        }
    }

    //// Latency
    var startTime;
    setInterval(function () {
        startTime = Date.now();
        $scope.socket.emit('latency', {t: startTime, l: $scope.session.latency});
    }, 2000);
    $scope.socket.on('latency', function () {
        $timeout(function () {
            $scope.session.latency = Date.now() - startTime;
        });
    });
}]);
<commit_msg>use setCustomVar instead<commit_after>slideApp.controller("slideController", ["$scope", "$cookies", "$location", "$http", "$interval", "$timeout", "$window", function ($scope, $cookies, $location, $http, $interval, $timeout, $window) {
    console.info("[load] mainController @" + Date.now());

    window.__$scope = $scope;

    $scope.session = {
        session: "",
        qr: "",
        bookmarkContent: "",
        type: "",
        latency: 0,
        info: {
            observer: false,
            host: false,
            remotes: 0
        }
    };
    $scope.socket = io()

    $scope.getFromServer = function (what) {
        $scope.socket.emit("_get", {what: what})
    };

    //TODO: disable some (most) settings for the host
    $scope.settings = {
        username: '',
        navigationType: 'swipe',
        vibration: true,
        laserCalibration: {
            center: {
                yaw: 0,
                pitch: 0
            },
            range: {
                yaw: 90,
                pitch: 90
            }
        },
        laserStyle: {
            color: "red",
            'font-size': 15,
            _icon: "circle"
        },
        laserTool: 'pointer',

        put: function (name, value) {
            $timeout(function () {
                $scope.settings[name] = value;
                localStorage.setItem("rs-settings", JSON.stringify($scope.settings));
            });
        },
        save: function (callback) {
            localStorage.setItem("rs-settings", JSON.stringify($scope.settings));
            console.info("[Settings] Saved!")
            if (callback) callback();
            if ($scope.settings.saveCallback)$scope.settings.saveCallback($scope.settings);

            _gaq.push(["_setCustomVar", 1, "settings.navigationType", $scope.settings.navigationType]);
            _gaq.push(["_setCustomVar", 1, "settings.vibration", $scope.settings.vibration]);
            _gaq.push(["_setCustomVar", 1, "settings.laserTool", $scope.settings.laserTool]);
            _gaq.push(["_setCustomVar", 1, "settings.laserStyle.color", $scope.settings.laserStyle.color]);
        },
        saveCallback: undefined
    };
    //TODO: track some settings, like navigationType, vibration and laserStyle via GoogleAnalytics

    // Load settings
    var storedSettings = JSON.parse(localStorage.getItem("rs-settings")) || {};
    $.extend($scope.settings, storedSettings);

    $scope.openSettings = function () {
        $("#settingsModal").modal("show");
    };
    $scope.closeSettings = function () {
        $("#settingsModal").modal("hide");
    };

    $scope.showConnectionInfo = function () {
        $scope.getFromServer("connectionInfo");
        $("#connectionInfoModal").modal("show")
    };

    // Non-settings stuff
    $scope.extension = {
        id: {
            chrome: "enmfbjneielhjagdgebkmcdhgbbionek"
        },
        installed: false
    };
    try {
        chrome.runtime.sendMessage($scope.extension.id.chrome, {ping: "hello"}, function (msg) {
            console.log(msg)
            if (msg && msg.pong && msg.pong == 'hello') {
                $scope.extension.installed = true;
            }
        });
    } catch (ignoerd) {
    }

    $scope.isMobile = function () {
        return window.mobilecheck();
    };
    $scope.isDesktop = function () {
        return !window.mobilecheck();
    };

    $scope.statusIcon = {
        type: 'question',
        color: 'orange',
        message: '',
        messageVisible: false,
        hideTimeout: undefined,
        showMessage: function (type, color, vibration, msg, timeout, doneCallback) {
            $timeout(function () {
                if (type)
                    $scope.statusIcon.type = type;
                if (color)
                    $scope.statusIcon.color = color;

                $scope.statusIcon.messageVisible = false;
                if (msg) {
                    $scope.statusIcon.message = msg;
                    $scope.statusIcon.messageVisible = true;
                }
                if ($scope.settings.vibration && vibration) {
                    window.navigator.vibrate(vibration);
                }

                if (timeout) {
                    $timeout.cancel($scope.statusIcon.hideTimeout);
                    $timeout(function () {
                        $scope.statusIcon.messageVisible = false;
                        if (doneCallback)
                            doneCallback();
                    }, timeout);
                } else {
                    if (doneCallback)
                        doneCallback();
                }
            });
        },
        hideMessage: function () {
            $scope.statusIcon.messageVisible = false;
        }
    };

    $scope.infoModal = {
        title: "",
        show: function (title, source) {
            $scope.infoModal.title = title;
            $("#infoModalContent").load(source, function () {
                $("#infoModal").modal("show");
            });
        },
        onClose: function () {
            $timeout(function () {
                $("#infoModalContent").empty();
            }, 500);
        }
    }

    //// Latency
    var startTime;
    setInterval(function () {
        startTime = Date.now();
        $scope.socket.emit('latency', {t: startTime, l: $scope.session.latency});
    }, 2000);
    $scope.socket.on('latency', function () {
        $timeout(function () {
            $scope.session.latency = Date.now() - startTime;
        });
    });
}]);
<|endoftext|>