<commit_before>/*
 * NATSboard
 * Copyright (c) 2015 Fatih Cetinkaya (http://github.com/fatihcode/natsboard)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint  browser: true */
/* global document: false, $: false, console: false, SmoothieChart: false, TimeSeries: false */
'use strict';

// Init the module
var app = function app() {

  var PAGE_DASHBOARD = '/dashboard.html';

  // Init server
  var serverURL    = location.protocol + '//' + location.hostname + ':' + location.port,
      serverCharts = {};

  // Init websocket
  var wsHostname = location.hostname,
      wsPort     = parseInt(location.port) + 1,
      wsUrl      = 'ws://' + wsHostname + ':' + wsPort,
      wsConn     = new WebSocket(wsUrl);

  // Handler for open event
  wsConn.onopen = function onopen() {
    console.log('connection opened');
  };

  // Handler for close event
  wsConn.onclose = function onclose() {
    console.log('connection closed');
  };

  // Handler for error event
  wsConn.onerror = function onerror() {
    console.error('connection error');
  };

  // Handler for message event
  wsConn.onmessage = function(event) {

    // Split data by new line
    event.data.split('\n').forEach(function(line) {
      // Parse message from line
      var message;
      try {
        message = JSON.parse(line);
      }
      catch(e) {
        console.error('message could not be parsed');
      }
      processMessage(message);
    });
  };

  // Processes message
  var processMessage = function processMessage(message) {
    if(!message && typeof message !== 'object') {
      return false;
    }

    // If page is dashboard then
    if(location.pathname === PAGE_DASHBOARD) {
      // If message type is rates then
      if(message.type === 'rates') {
        // Iterate metrics
        [
          {type: 'varz', metric: 'connections', data: []},
          {type: 'varz', metric: 'mem', data: []},
          {type: 'varz', metric: 'cpu', data: [], chartOptions: {precision: 1}},
          {type: 'varz', metric: 'slow_consumers', data: []},
          {type: 'varz', metric: 'in_msgs', data: []},
          {type: 'varz', metric: 'out_msgs', data: []},
          {type: 'varz', metric: 'in_bytes', data: []},
          {type: 'varz', metric: 'out_bytes', data: []}
        ].forEach(function(metric) {
          if(message.rates[metric.type][metric.metric]) {
            message.rates[metric.type][metric.metric].forEach(function(rate) {
              metric.data.push({date: new Date(rate.timestamp), value: rate.value});
            });
          }
          renderChart(metric.metric, metric.data, metric.chartOptions);
        });
      }
    }
  };

  // Gets server url
  var getServerUrl = function getServerUrl() {
    return serverURL;
  };

  // Gets data
  var getData = function getData(url, cb) {
    $.getJSON(url)
    .done(function(data) {
      return cb(null, data);
    })
    .fail(function(jqxhr) {
      return cb(new Error('failed to fetch data: ' + jqxhr.status + ' - ' + jqxhr.statusText));
    });
  };

  // Renders table information
  var renderTableInfo = function renderTableInfo(type, data) {
    if(!data) {
      return false;
    }

    // Iterate data
    for(var key in data) {
      if(data.hasOwnProperty(key)) {

        var elem    = $('#' + type + '-info-row-' + key),
            val     = data[key],
            display = true;

        // If the key is not a private then
        if(key.indexOf('_') !== 0) {

          // If value it is an object / array then
          if(typeof val === 'object' || (val instanceof Array)) {
            display = false; // do not display
          }

          if(display) {
            // If element exists then
            if(elem.length) {
              // Update info
              elem.html(''+key+'	'+val+'	');
            }
            else {
              // Otherwise add info
              $('#' + type + '-info > tbody:last-child').append('
'+key+'	'+val+'
');
            }
          }
        }
      }
    }

    return true;
  };

  // Renders connections table
  var renderTableConns = function renderTableConns(data) {
    if(!data) {
      return false;
    }

    var content;

    // Iterate data
    for(var key in data.connections) {
      if(data.connections.hasOwnProperty(key)) {

        var val = data.connections[key];

        // Row
        content += '
';

        // Columns
        content += '' + val.cid + '	';
        content += '' + val.ip + ':' + val.port + '	';
        content += '' + val.lang + ' / ' + val.version + '	';
        content += '' + val.pending_bytes + '	';
        content += '' + val.in_msgs + ' / ' + val.out_msgs + '	';
        content += '' + val.in_bytes + ' / ' + val.out_bytes + '	';
        content += '' + val.subscriptions + '	';
        content += '' + val.subscriptions_list.join(', ') + '	';

        content += '
';
      }
    }

    // If content exists then
    if(content) {
      // Add into table
      $('#connz-list > tbody:last-child').append(content);

      // Update table value
      var elemVal = $('#connz-list-value');
      if(elemVal.length) {
        elemVal.text((data.num_connections || 0) + ' connections');
      }
    }

    return true;
  };

  // Renders routes table
  var renderTableRoutes = function renderTableRoutes(data) {
    if(!data) {
      return false;
    }

    var content;

    // Iterate data
    for(var key in data.routes) {
      if(data.routes.hasOwnProperty(key)) {

        var val = data.routes[key];

        // Row
        content += '
';

        // Columns
        content += '' + val.rid + '	';
        content += '' + val.ip + ':' + val.port + '	';
        content += '' + val.remote_id + '	';
        content += '' + val.did_solicit + '	';
        content += '' + val.pending_size + '	';
        content += '' + val.in_msgs + ' / ' + val.out_msgs + '	';
        content += '' + val.in_bytes + ' / ' + val.out_bytes + '	';
        content += '' + val.subscriptions + '	';
        content += '' + (val.subscriptions_list ? val.subscriptions_list.join(', ') : '') + '	';

        content += '
';
      }
    }

    // If content exists then
    if(content) {
      // Add into table
      $('#routez-list > tbody:last-child').append(content);

      // Update table value
      var elemVal = $('#routez-list-value');
      if(elemVal.length) {
        elemVal.text((data.num_routes || 0) + ' routes');
      }
    }

    return true;
  };

  // Renders chart
  var renderChart = function renderChart(type, data, chartOptions) {
    if(!data) {
      return false;
    }
    else if(typeof SmoothieChart !== 'function') {
      return false;
    }

    if(!chartOptions || typeof chartOptions !== 'object') {
      chartOptions = {};
    }

    // If the chart is not defined then
    if(!serverCharts[type]) {

      // Create chart
      var lastRate = {};

      // Custom range function
      var myYRangeFunction = function myYRangeFunction(range) {
        var min = !isNaN(range.min) ? range.min : 0,
            max = !isNaN(range.max) ? range.max : 1;

        min = (max > 1000) ? min : 0;
        max = max*1.1;

        return {min: min, max: max};
      };

      // Check chart options
      if(!chartOptions.grid) {
        chartOptions.grid = {};
      }
      if(!chartOptions.labels) {
        chartOptions.labels = {};
      }

      chartOptions.grid.fillStyle        = '#ffffff';
      chartOptions.grid.strokeStyle      = '#f5f5f5';
      chartOptions.grid.borderVisible    = true;
      chartOptions.grid.verticalSections = 5;
      chartOptions.grid.millisPerLine    = 1000;
      chartOptions.labels.fillStyle      = '#515151';
      chartOptions.labels.precision      = chartOptions.precision || 0;
      chartOptions.labels.disabled       = true;
      chartOptions.minValue              = 0;
      chartOptions.yRangeFunction        = myYRangeFunction;

      var chart  = new SmoothieChart(chartOptions),
          series = new TimeSeries();

      chart.addTimeSeries(series, {
        lineWidth:   3,
        strokeStyle: '#00bd9c',
        fillStyle:   'rgba(0,189,156,0.30)'
      });

      // Iterate data
      if(data instanceof Array) {
        for(var i = 0, len = data.length; i < len; i++) {
          if(data[i].date && typeof data[i].value !== 'undefined') {
            series.append(data[i].date, data[i].value); // append into series
            lastRate = data[i];
          }
        }
      }

      // Add it to charts
      serverCharts[type] = {chart: chart, series: [series]};
      // Start stream
      chart.streamTo(document.getElementById(type + '-chart'), 1000);
    }
    else {
      lastRate = data.slice(-1)[0];
      if(lastRate.date && typeof lastRate.value !== 'undefined') {
        serverCharts[type].series[0].append(lastRate.date, lastRate.value); // append into series
      }
    }

    // Update chart value
    var elemVal = $('#' + type + '-chart-value');
    if(elemVal.length) {
      elemVal.text((lastRate && lastRate.value) || 0);
    }

    return serverCharts[type];
  };

  // Handler for index page
  var indexHandler = function indexHandler(url) {
    // Get all data
    getData(url+'/nats/_all', function(err, data) {
      if(err) {
        console.error(err);
      }
      // Iterate information types
      ['varz', 'connz', 'subscriptionsz', 'routez'].forEach(function(type) {
        renderTableInfo(type, data[type] || null);
      });
    });

    return true;
  };

  // Handler for dashboard page
  var dashboardHandler = function dashboardHandler() {
    return true;
  };

  // Handler for connections page
  var connsHandler = function connsHandler(url) {
    // Get all data
    getData(url+'/nats/connz?subs=1', function(err, data) {
      if(err) {
        console.error(err);
      }
      renderTableConns(data);
    });

    return true;
  };

  // Handler for routes page
  var routesHandler = function routesHandler(url) {
    // Get all data
    getData(url+'/nats/routez?subs=1', function(err, data) {
      if(err) {
        console.error(err);
      }
      renderTableRoutes(data);
    });

    return true;
  };

  // Handles routes
  var routeHandler = function routeHandler(route) {
    switch(route) {
      case '/':
        // Index page
        indexHandler(getServerUrl());
        break;
      case '/dashboard.html':
        // Dashboard page
        dashboardHandler();
        break;
      case '/connections.html':
        // Connections page
        connsHandler(getServerUrl());
        break;
      case '/routes.html':
        // Routes page
        routesHandler(getServerUrl());
        break;
      // Default
      default:
        break;
    }
  };

  // Return
  return {
    routeHandler: routeHandler
  };
};

// Init app
var myApp = app();

// Run app
$(document).ready(function() {
  myApp.routeHandler(location.pathname);
});<commit_msg>Show connections without subscriptions (fixes #2 and #6)<commit_after>/*
 * NATSboard
 * Copyright (c) 2015 Fatih Cetinkaya (http://github.com/fatihcode/natsboard)
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint  browser: true */
/* global document: false, $: false, console: false, SmoothieChart: false, TimeSeries: false */
'use strict';

// Init the module
var app = function app() {

  var PAGE_DASHBOARD = '/dashboard.html';

  // Init server
  var serverURL    = location.protocol + '//' + location.hostname + ':' + location.port,
      serverCharts = {};

  // Init websocket
  var wsHostname = location.hostname,
      wsPort     = parseInt(location.port) + 1,
      wsUrl      = 'ws://' + wsHostname + ':' + wsPort,
      wsConn     = new WebSocket(wsUrl);

  // Handler for open event
  wsConn.onopen = function onopen() {
    console.log('connection opened');
  };

  // Handler for close event
  wsConn.onclose = function onclose() {
    console.log('connection closed');
  };

  // Handler for error event
  wsConn.onerror = function onerror() {
    console.error('connection error');
  };

  // Handler for message event
  wsConn.onmessage = function(event) {

    // Split data by new line
    event.data.split('\n').forEach(function(line) {
      // Parse message from line
      var message;
      try {
        message = JSON.parse(line);
      }
      catch(e) {
        console.error('message could not be parsed');
      }
      processMessage(message);
    });
  };

  // Processes message
  var processMessage = function processMessage(message) {
    if(!message && typeof message !== 'object') {
      return false;
    }

    // If page is dashboard then
    if(location.pathname === PAGE_DASHBOARD) {
      // If message type is rates then
      if(message.type === 'rates') {
        // Iterate metrics
        [
          {type: 'varz', metric: 'connections', data: []},
          {type: 'varz', metric: 'mem', data: []},
          {type: 'varz', metric: 'cpu', data: [], chartOptions: {precision: 1}},
          {type: 'varz', metric: 'slow_consumers', data: []},
          {type: 'varz', metric: 'in_msgs', data: []},
          {type: 'varz', metric: 'out_msgs', data: []},
          {type: 'varz', metric: 'in_bytes', data: []},
          {type: 'varz', metric: 'out_bytes', data: []}
        ].forEach(function(metric) {
          if(message.rates[metric.type][metric.metric]) {
            message.rates[metric.type][metric.metric].forEach(function(rate) {
              metric.data.push({date: new Date(rate.timestamp), value: rate.value});
            });
          }
          renderChart(metric.metric, metric.data, metric.chartOptions);
        });
      }
    }
  };

  // Gets server url
  var getServerUrl = function getServerUrl() {
    return serverURL;
  };

  // Gets data
  var getData = function getData(url, cb) {
    $.getJSON(url)
    .done(function(data) {
      return cb(null, data);
    })
    .fail(function(jqxhr) {
      return cb(new Error('failed to fetch data: ' + jqxhr.status + ' - ' + jqxhr.statusText));
    });
  };

  // Renders table information
  var renderTableInfo = function renderTableInfo(type, data) {
    if(!data) {
      return false;
    }

    // Iterate data
    for(var key in data) {
      if(data.hasOwnProperty(key)) {

        var elem    = $('#' + type + '-info-row-' + key),
            val     = data[key],
            display = true;

        // If the key is not a private then
        if(key.indexOf('_') !== 0) {

          // If value it is an object / array then
          if(typeof val === 'object' || (val instanceof Array)) {
            display = false; // do not display
          }

          if(display) {
            // If element exists then
            if(elem.length) {
              // Update info
              elem.html(''+key+'	'+val+'	');
            }
            else {
              // Otherwise add info
              $('#' + type + '-info > tbody:last-child').append('
'+key+'	'+val+'
');
            }
          }
        }
      }
    }

    return true;
  };

  // Renders connections table
  var renderTableConns = function renderTableConns(data) {
    if(!data) {
      return false;
    }

    var content;

    // Iterate data
    for(var key in data.connections) {
      if(data.connections.hasOwnProperty(key)) {

        var val = data.connections[key];

        // Row
        content += '
';

        // Columns
        content += '' + val.cid + '	';
        content += '' + val.ip + ':' + val.port + '	';
        content += '' + val.lang + ' / ' + val.version + '	';
        content += '' + val.pending_bytes + '	';
        content += '' + val.in_msgs + ' / ' + val.out_msgs + '	';
        content += '' + val.in_bytes + ' / ' + val.out_bytes + '	';
        content += '' + val.subscriptions + '	';
        content += '' + (val.subscriptions_list ? val.subscriptions_list.join(', ') : '') + '	';

        content += '
';
      }
    }

    // If content exists then
    if(content) {
      // Add into table
      $('#connz-list > tbody:last-child').append(content);

      // Update table value
      var elemVal = $('#connz-list-value');
      if(elemVal.length) {
        elemVal.text((data.num_connections || 0) + ' connections');
      }
    }

    return true;
  };

  // Renders routes table
  var renderTableRoutes = function renderTableRoutes(data) {
    if(!data) {
      return false;
    }

    var content;

    // Iterate data
    for(var key in data.routes) {
      if(data.routes.hasOwnProperty(key)) {

        var val = data.routes[key];

        // Row
        content += '
';

        // Columns
        content += '' + val.rid + '	';
        content += '' + val.ip + ':' + val.port + '	';
        content += '' + val.remote_id + '	';
        content += '' + val.did_solicit + '	';
        content += '' + val.pending_size + '	';
        content += '' + val.in_msgs + ' / ' + val.out_msgs + '	';
        content += '' + val.in_bytes + ' / ' + val.out_bytes + '	';
        content += '' + val.subscriptions + '	';
        content += '' + (val.subscriptions_list ? val.subscriptions_list.join(', ') : '') + '	';

        content += '
';
      }
    }

    // If content exists then
    if(content) {
      // Add into table
      $('#routez-list > tbody:last-child').append(content);

      // Update table value
      var elemVal = $('#routez-list-value');
      if(elemVal.length) {
        elemVal.text((data.num_routes || 0) + ' routes');
      }
    }

    return true;
  };

  // Renders chart
  var renderChart = function renderChart(type, data, chartOptions) {
    if(!data) {
      return false;
    }
    else if(typeof SmoothieChart !== 'function') {
      return false;
    }

    if(!chartOptions || typeof chartOptions !== 'object') {
      chartOptions = {};
    }

    // If the chart is not defined then
    if(!serverCharts[type]) {

      // Create chart
      var lastRate = {};

      // Custom range function
      var myYRangeFunction = function myYRangeFunction(range) {
        var min = !isNaN(range.min) ? range.min : 0,
            max = !isNaN(range.max) ? range.max : 1;

        min = (max > 1000) ? min : 0;
        max = max*1.1;

        return {min: min, max: max};
      };

      // Check chart options
      if(!chartOptions.grid) {
        chartOptions.grid = {};
      }
      if(!chartOptions.labels) {
        chartOptions.labels = {};
      }

      chartOptions.grid.fillStyle        = '#ffffff';
      chartOptions.grid.strokeStyle      = '#f5f5f5';
      chartOptions.grid.borderVisible    = true;
      chartOptions.grid.verticalSections = 5;
      chartOptions.grid.millisPerLine    = 1000;
      chartOptions.labels.fillStyle      = '#515151';
      chartOptions.labels.precision      = chartOptions.precision || 0;
      chartOptions.labels.disabled       = true;
      chartOptions.minValue              = 0;
      chartOptions.yRangeFunction        = myYRangeFunction;

      var chart  = new SmoothieChart(chartOptions),
          series = new TimeSeries();

      chart.addTimeSeries(series, {
        lineWidth:   3,
        strokeStyle: '#00bd9c',
        fillStyle:   'rgba(0,189,156,0.30)'
      });

      // Iterate data
      if(data instanceof Array) {
        for(var i = 0, len = data.length; i < len; i++) {
          if(data[i].date && typeof data[i].value !== 'undefined') {
            series.append(data[i].date, data[i].value); // append into series
            lastRate = data[i];
          }
        }
      }

      // Add it to charts
      serverCharts[type] = {chart: chart, series: [series]};
      // Start stream
      chart.streamTo(document.getElementById(type + '-chart'), 1000);
    }
    else {
      lastRate = data.slice(-1)[0];
      if(lastRate.date && typeof lastRate.value !== 'undefined') {
        serverCharts[type].series[0].append(lastRate.date, lastRate.value); // append into series
      }
    }

    // Update chart value
    var elemVal = $('#' + type + '-chart-value');
    if(elemVal.length) {
      elemVal.text((lastRate && lastRate.value) || 0);
    }

    return serverCharts[type];
  };

  // Handler for index page
  var indexHandler = function indexHandler(url) {
    // Get all data
    getData(url+'/nats/_all', function(err, data) {
      if(err) {
        console.error(err);
      }
      // Iterate information types
      ['varz', 'connz', 'subscriptionsz', 'routez'].forEach(function(type) {
        renderTableInfo(type, data[type] || null);
      });
    });

    return true;
  };

  // Handler for dashboard page
  var dashboardHandler = function dashboardHandler() {
    return true;
  };

  // Handler for connections page
  var connsHandler = function connsHandler(url) {
    // Get all data
    getData(url+'/nats/connz?subs=1', function(err, data) {
      if(err) {
        console.error(err);
      }
      renderTableConns(data);
    });

    return true;
  };

  // Handler for routes page
  var routesHandler = function routesHandler(url) {
    // Get all data
    getData(url+'/nats/routez?subs=1', function(err, data) {
      if(err) {
        console.error(err);
      }
      renderTableRoutes(data);
    });

    return true;
  };

  // Handles routes
  var routeHandler = function routeHandler(route) {
    switch(route) {
      case '/':
        // Index page
        indexHandler(getServerUrl());
        break;
      case '/dashboard.html':
        // Dashboard page
        dashboardHandler();
        break;
      case '/connections.html':
        // Connections page
        connsHandler(getServerUrl());
        break;
      case '/routes.html':
        // Routes page
        routesHandler(getServerUrl());
        break;
      // Default
      default:
        break;
    }
  };

  // Return
  return {
    routeHandler: routeHandler
  };
};

// Init app
var myApp = app();

// Run app
$(document).ready(function() {
  myApp.routeHandler(location.pathname);
});
<|endoftext|>


<commit_before>/*
 app.js
 */

(function( $ ) {

  var artists,
      geocoder,
      geoIp = {},
      jqueryMap = {
        $errorField : $('.error-msg').eq(0),
        mapElem : $('#map-canvas')[0],
        $overlay : $('.overlay').eq(0),
        $spinner : $('.spinner').eq(0)
      },
      map,
      mapOptions = {
        zoom : 8,
        mapTypeId : google.maps.MapTypeId.ROADMAP
      },
      marker,
      markerOptions = {
        strokeColor : '#FF0000',
        strokeOpacity : 0.8,
        strokeWeight : 2,
        fillColor : '#FF0000',
        fillOpacity : 0.35,
        radius : 60000 //TODO - this is not accurate
      },
      spinner = $('.csspinner');

  // Initialize Google Maps
  function initializeMap() {
    map = new google.maps.Map(jqueryMap.mapElem, mapOptions);

    if ( "geolocation" in navigator ) {
      navigator.geolocation.getCurrentPosition(function( position ) {
        setGeoIp(position.coords.latitude, position.coords.longitude);
        initialLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
        map.setCenter(initialLocation);

        // Set marker radius
        markerOptions.map = map;
        markerOptions.center = initialLocation;
        marker = new google.maps.Circle(markerOptions);
      });
    } else {
      console.log('geolocation not supported');
    }
  }

  // Save lat/long coordinates
  function setGeoIp( lat, long ) {
    geoIp.lat = lat;
    geoIp.long = long;
  }

  // Create new map with geocoded location
  function geocodeMap( address ) {
    geocoder = geocoder || new google.maps.Geocoder();
    geocoder.geocode({ 'address' : address}, function( results, status ) {
      if ( status == google.maps.GeocoderStatus.OK ) {
        // save results
        var latitude = results[0].geometry.location.lat();
        var longitude = results[0].geometry.location.lng();
        setGeoIp(latitude, longitude);

        // change map
        map.setCenter(results[0].geometry.location);
        markerOptions.map = map;
        markerOptions.center = results[0].geometry.location;
        marker.setMap(null); //clear previous marker
        marker = new google.maps.Circle(markerOptions);
      } else {
        jqueryMap.$errorField
          .empty()
          .append('We can\'t find this city. Try again');
      }
    });
  }

  function displayConcerts( concerts ) {

    var $events = $('.event-listings').eq(0);
    $events.removeClass('hide')
      .empty()
      .append(templatizer.event_listings({
        concerts : concerts,
        search_start : moment().format('MMMM Do'),
        search_end : moment().add('days', 7).format('MMMM Do' )
      }));
  }

  // Change map of displayed city
  function onChangeCity( event ) {
    event.preventDefault();
    var address = $('form.location').find('input[id="city"]').val();
    if ( validateForm(address) ) {
      geocodeMap(address);
    }
  }

  // Get list of songkick metro ids
  function onNextPage() {
    jqueryMap.$spinner.toggleClass('csspinner');
    jqueryMap.$overlay.toggleClass('hide');

    $.ajax({
      url : '/events',
      data : {
        user_coordinates : geoIp
      }
    }).done(function( concerts ) {
      jqueryMap.$overlay.toggleClass('hide');
      jqueryMap.$spinner.toggleClass('csspinner');
      console.log('concerts received', concerts);
      formatConcerts(concerts);
      displayConcerts(concerts);
    });
  }

  function formatConcerts( concerts ) {
    var i, origTime;
    for ( i = 0; i < concerts.length; i++ ) {
      concerts[i].formatted_start_date = moment(concerts[i].start.date).format('MMM DD');
      if ( concerts[i].start.time == null ) {
        concerts[i].start.time = 'Unavailable';
      } else {
        origTime = moment(concerts[i].start.time, 'HH');
        concerts[i].start.time = origTime.format('hh:mmA');
      }
    }
    return concerts;
  }

  // Validate city input form and display error messages
  function validateForm( address ) {
    var text = $.trim(address);
    if ( text == "" ) {
      jqueryMap.$errorField.empty()
        .append('Form cannot be blank');
    } else {
      return text;
    }
  }

  // Get list of user's artists from their Rdio collection
  function getArtists() {
    $.ajax({
      url : '/users/new'
    }).done(function( concerts ) {
      artists = concerts;
      console.log('concerts returned', concerts);
    })
  }

  // Initialize page
  function init() {
    getArtists();
    initializeMap();
  }

  // Event Handlers
  $('form.location').on('submit', onChangeCity);
  $('button[name="getEventsBtn"]').on('click', onNextPage);

  // Initialize the page
  init();

})(jQuery);<commit_msg>Handle cases when browser does not support geolocation or user denies permission.<commit_after>/*
 app.js
 */

(function( $ ) {

  var artists,
      geocoder,
      geoIp = {},
      jqueryMap = {
        $errorField : $('.error-msg').eq(0),
        mapElem : $('#map-canvas')[0],
        $overlay : $('.overlay').eq(0),
        $spinner : $('.spinner').eq(0)
      },
      map,
      mapOptions = {
        zoom : 8,
        mapTypeId : google.maps.MapTypeId.ROADMAP
      },
      marker,
      markerOptions = {
        strokeColor : '#FF0000',
        strokeOpacity : 0.8,
        strokeWeight : 2,
        fillColor : '#FF0000',
        fillOpacity : 0.35,
        radius : 60000 //TODO - this is not accurate
      },
      spinner = $('.csspinner');

  // Initialize Google Maps
  function initializeMap() {
    map = new google.maps.Map(jqueryMap.mapElem, mapOptions);

    if ( "geolocation" in navigator ) {
      navigator.geolocation.getCurrentPosition(function( position ) {
        setGeoIp(position.coords.latitude, position.coords.longitude);
        initialLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
        map.setCenter(initialLocation);

        // Set marker radius
        markerOptions.map = map;
        markerOptions.center = initialLocation;
        marker = new google.maps.Circle(markerOptions);
      });
    } else {
      jqueryMap.$errorField
        .empty()
        .append("We can't find your current location. Enter a city to get started.");
    }
  }

  // Save lat/long coordinates
  function setGeoIp( lat, long ) {
    geoIp.lat = lat;
    geoIp.long = long;
  }

  // Create new map with geocoded location
  function geocodeMap( address ) {
    geocoder = geocoder || new google.maps.Geocoder();
    geocoder.geocode({ 'address' : address}, function( results, status ) {
      if ( status == google.maps.GeocoderStatus.OK ) {
        // save results
        var latitude = results[0].geometry.location.lat();
        var longitude = results[0].geometry.location.lng();
        setGeoIp(latitude, longitude);

        // change map
        map.setCenter(results[0].geometry.location);
        markerOptions.map = map;
        markerOptions.center = results[0].geometry.location;
        if ( marker ) { marker.setMap(null); } //clear previous marker if it exists
        marker = new google.maps.Circle(markerOptions);
      } else {
        jqueryMap.$errorField
          .empty()
          .append('We can\'t find this city. Try again');
      }
    });
  }

  function displayConcerts( concerts ) {

    var $events = $('.event-listings').eq(0);
    $events.removeClass('hide')
      .empty()
      .append(templatizer.event_listings({
        concerts : concerts,
        search_start : moment().format('MMMM Do'),
        search_end : moment().add('days', 7).format('MMMM Do' )
      }));
  }

  // Change map of displayed city
  function onChangeCity( event ) {
    event.preventDefault();
    var address = $('form.location').find('input[id="city"]').val();
    if ( validateForm(address) ) {
      geocodeMap(address);
    }
  }

  // Get list of songkick metro ids
  function onGetEvents() {
    jqueryMap.$errorField
      .empty();

    if ( !hasUserLocation() ) {
      jqueryMap.$errorField
        .append('Please enter a city.');
      return;
    }

    jqueryMap.$spinner.toggleClass('csspinner');
    jqueryMap.$overlay.toggleClass('hide');

    $.ajax({
      url : '/events',
      data : {
        user_coordinates : geoIp
      }
    }).done(function( concerts ) {
      jqueryMap.$overlay.toggleClass('hide');
      jqueryMap.$spinner.toggleClass('csspinner');
      console.log('concerts received', concerts);
      formatConcerts(concerts);
      displayConcerts(concerts);
    });
  }

  function formatConcerts( concerts ) {
    var i, origTime;
    for ( i = 0; i < concerts.length; i++ ) {
      concerts[i].formatted_start_date = moment(concerts[i].start.date).format('MMM DD');
      if ( concerts[i].start.time == null ) {
        concerts[i].start.time = 'Unavailable';
      } else {
        origTime = moment(concerts[i].start.time, 'HH');
        concerts[i].start.time = origTime.format('hh:mmA');
      }
    }
    return concerts;
  }

  // Validate city input form and display error messages
  function validateForm( address ) {
    jqueryMap.$errorField.empty();

    var text = $.trim(address);
    if ( text == "" ) {
      jqueryMap.$errorField
        .append('Please enter a city.');
    } else {
      return text;
    }
  }

  // Get list of user's artists from their Rdio collection
  function getArtists() {
    $.ajax({
      url : '/users/new'
    }).done(function( concerts ) {
      artists = concerts;
      console.log('concerts returned', concerts);
    })
  }

  function hasUserLocation() {
    return geoIp.lat && geoIp.long;
  }

  // Initialize page
  function init() {
    getArtists();
    initializeMap();
  }

  // Event Handlers
  $('form.location').on('submit', onChangeCity);
  $('button[name="getEventsBtn"]').on('click', onGetEvents);

  // Initialize the page
  init();

})(jQuery);<|endoftext|>


<commit_before>$(function () {
    var $btn = $('#convert'),
        $form = $('form'),
        $tmpl = $('#template'),
        $url = $('#url'),
        $links = $('.links'),
        $ul = $('.links ul'),
        $tag = $('#tag'),
        $status = $('#status'),
        $nut = $('.logo .nut img'),
        links = 0,
        previous;

    var options = {
        host: window.location.origin + '/',
        taglineChange: 5,
        nutStart: 6,
        nutEnd: 8,
        submit: '/',
        delay: 350,
    };

    // Eventually this will be a 'loading' spinner or
    // something
    var loading = {
        target: document.getElementById('spinner'),
        spinOpts:  {
            lines: 13,
            length: 8,
            width: 3,
            radius: 5,
            corners: 1,
        },
        done: function () {
            if (loading.spinner) {
                loading.spinner.stop();
            }

            $url.attr('disabled', false);
            $btn.attr('disabled', false);
        },
        start: function () {
            loading.spinner = new Spinner(loading.spinOpts);
            loading.spinner.spin(loading.target);
            $url.attr('disabled', true);
            $btn.attr('disabled', true);
        },
    }, append = function (url) {
        return url.indexOf('http') === 0 ? url : 'http://' + url;
    }, ui = {
        results: function (url) {
            var $this = $(this),
                $clone = $tmpl.clone().attr('id', ''),
                $button = $clone.find('button'),
                $input = $clone.find('input');

            console.log('Submit button pressed');

            if (links === 0) {
                $status.text(nutshell.ask(links));
            } else {
                $status.fadeOut(400, function () {
                    $status.text(nutshell.ask(links));
                    $status.fadeIn(400);
                });
            }

            $links.removeClass('hide');

            $clone.addClass('link');
            $ul.prepend($clone);

            $input.attr('id', 'link' + links);
            $input.val(options.host + url);
            $input.focus().select();

            $button.attr('data-clipboard-text', options.host + url);

            var clip = new ZeroClipboard($button[0], {
                moviePath: "/lib/zeroclipboard/ZeroClipboard.swf"
            });

            clip.on('complete', function () {
                $(this).button('toggle')
                       .text('Copied')
                       .attr('disabled', true);
            });

            links += 1;
        }
    }, submit = {
        click: function () {
            $btn.trigger('click');
            return false;
        },
        press: function () {
            var url = append($url.val());

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            loading.start();

            $.ajax({
                url: options.submit,
                type: 'post',
                dataType: 'json',
                data: {
                    url: url,
                },
                success: submit.success,
                error: submit.error,
            });
        },
        success: function (data) {
            ui.results(data.url);
            loading.done();
        },
        error: function (data) {
            console.log('error in submit');
            loading.done();
        },
    }, silent = {
        search: function () {
            var url = append($url.val());

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            if (url !== previous) {
                // Reset everything
                $links.find('.link').remove();
                $status.text('');
                links = 0;
            }

            console.log('Silent search called');
            silent.finished = undefined;
            previous = url;

            $btn.unbind();
            $btn.click(silent.press);

            $.ajax({
                url: options.submit,
                type: 'post',
                dataType: 'json',
                data: {
                    url: url,
                },
                success: silent.success,
                error: silent.error,
            });
        },
        press: function (event) {
            var url = append($url.val());

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            console.log('intercepted button');
            if (silent.finished) {
                ui.results(silent.finished.url);
            } else {
                loading.start();
            }

            // Do regardless
            $btn.unbind();
            $btn.click(submit.press);
        },
        success: function (data) {
            silent.finished = data;
            loading.done();
        },
        error: function (data) {
            console.log('silent error');
        },
    }, tagline = function () {
        $tag.fadeOut(400, function () {
            $tag.text(nutshell.tag());
            $tag.fadeIn(400);
        });
    }, isUrl = function (url) {
		var regexp = /\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    	return regexp.test(url);
	}, shakeNuts = {
        start: function () {
            $nut.addClass('animated swing');
        },
        end: function () {
            $nut.removeClass('animated swing');
        }
    };
    //
    // Attach Event Handlers
    //

    $url.search({
        search: silent.search,
        delay: options.delay,
    });

    $btn.click(submit.press);
    $btn.click(submit.press);
    $form.submit(submit.click);

    //$nut.hover(shakeNuts);

    //
    // Setup Timeouts
    //

    setInterval(tagline, options.taglineChange * 1000);
    setInterval(shakeNuts.start, options.nutStart * 1000);
    setInterval(shakeNuts.end, options.nutEnd * 1000);

    //
    // Setup Copy & Paste
    //

    $url.focus();
});
<commit_msg>Fix button, again<commit_after>$(function () {
    var $btn = $('#convert'),
        $form = $('form'),
        $tmpl = $('#template'),
        $url = $('#url'),
        $links = $('.links'),
        $ul = $('.links ul'),
        $tag = $('#tag'),
        $status = $('#status'),
        $nut = $('.logo .nut img'),
        links = 0,
        previous;

    var options = {
        host: window.location.origin + '/',
        taglineChange: 5,
        nutStart: 6,
        nutEnd: 8,
        submit: '/',
        delay: 350,
    };

    // Eventually this will be a 'loading' spinner or
    // something
    var loading = {
        target: document.getElementById('spinner'),
        spinOpts:  {
            lines: 13,
            length: 8,
            width: 3,
            radius: 5,
            corners: 1,
        },
        done: function () {
            if (loading.spinner) {
                loading.spinner.stop();
            }

            $url.attr('disabled', false);
            $btn.attr('disabled', false);
        },
        start: function () {
            loading.spinner = new Spinner(loading.spinOpts);
            loading.spinner.spin(loading.target);
            $url.attr('disabled', true);
            $btn.attr('disabled', true);
        },
    }, append = function (url) {
        return url.indexOf('http') === 0 ? url : 'http://' + url;
    }, ui = {
        results: function (url) {
            var $this = $(this),
                $clone = $tmpl.clone().attr('id', ''),
                $button = $clone.find('button'),
                $input = $clone.find('input');

            console.log('Submit button pressed');

            if (links === 0) {
                $status.text(nutshell.ask(links));
            } else {
                $status.fadeOut(400, function () {
                    $status.text(nutshell.ask(links));
                    $status.fadeIn(400);
                });
            }

            $links.removeClass('hide');

            $clone.addClass('link');
            $ul.prepend($clone);

            $input.attr('id', 'link' + links);
            $input.val(options.host + url);
            $input.focus().select();

            $button.attr('data-clipboard-text', options.host + url);

            var clip = new ZeroClipboard($button[0], {
                moviePath: "/lib/zeroclipboard/ZeroClipboard.swf"
            });

            clip.on('complete', function () {
                $(this).button('toggle')
                       .text('Copied')
                       .attr('disabled', true);
            });

            links += 1;
        }
    }, submit = {
        click: function () {
            $btn.trigger('click');
            return false;
        },
        press: function () {
            var url = append($url.val());

            if (silent.loading) {
                loading.start();
                silent.show = true;
                return;
            }

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            loading.start();

            $.ajax({
                url: options.submit,
                type: 'post',
                dataType: 'json',
                data: {
                    url: url,
                },
                success: submit.success,
                error: submit.error,
            });
        },
        success: function (data) {
            silent.loading = false;
            ui.results(data.url);
            loading.done();
        },
        error: function (data) {
            console.log('error in submit');
            silent.loading = false;
            loading.done();
        },
    }, silent = {
        search: function () {
            var url = append($url.val());

            silent.loading = true;

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            if (url !== previous) {
                // Reset everything
                $links.find('.link').remove();
                $status.text('');
                links = 0;
            }

            console.log('Silent search called');
            silent.finished = undefined;
            previous = url;

            $btn.unbind();
            $btn.click(silent.press);

            $.ajax({
                url: options.submit,
                type: 'post',
                dataType: 'json',
                data: {
                    url: url,
                },
                success: silent.success,
                error: silent.error,
            });
        },
        press: function (event) {
            var url = append($url.val());

            if (silent.loading) {
                console.log('silent.show is true');
                silent.show = true;
                loading.start();
                return;
            }

            if (!isUrl(url)) {
                $status.text(nutshell.error());
                return;
            }

            console.log('intercepted button');
            if (silent.finished) {
                ui.results(silent.finished.url);
            } else {
                loading.start();
            }

            // Do regardless
            $btn.unbind();
            $btn.click(submit.press);
        },
        success: function (data) {
            console.log('Silent success, silent.show = ' + silent.show);
            if (silent.show) {
                ui.results(data.url);
            } else {
                silent.finished = data;
            }

            silent.show = false;
            silent.loading = false;
            loading.done();
        },
        error: function (data) {
            console.log('silent error');
            silent.loading = false;
            silent.show = false;
        },
    }, tagline = function () {
        $tag.fadeOut(400, function () {
            $tag.text(nutshell.tag());
            $tag.fadeIn(400);
        });
    }, isUrl = function (url) {
		var regexp = /\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    	return regexp.test(url);
	}, shakeNuts = {
        start: function () {
            $nut.addClass('animated swing');
        },
        end: function () {
            $nut.removeClass('animated swing');
        }
    };
    //
    // Attach Event Handlers
    //

    $url.search({
        search: silent.search,
        delay: options.delay,
    });

    $btn.click(submit.press);
    $btn.click(submit.press);
    $form.submit(submit.click);

    //$nut.hover(shakeNuts);

    //
    // Setup Timeouts
    //

    setInterval(tagline, options.taglineChange * 1000);
    setInterval(shakeNuts.start, options.nutStart * 1000);
    setInterval(shakeNuts.end, options.nutEnd * 1000);

    //
    // Setup Copy & Paste
    //

    $url.focus();
});
<|endoftext|>


<commit_before>/**
 * OffCanvas module.
 * @module foundation.offcanvas
 * @requires foundation.util.triggers
 * @requires foundation.util.motion
 */
!function($, Foundation) {

'use strict';

/**
 * Creates a new instance of an off-canvas wrapper.
 * @class
 * @fires OffCanvas#init
 * @param {Object} element - jQuery object to initialize.
 * @param {Object} options - Overrides to the default plugin settings.
 */
function OffCanvas(element, options) {
  this.$element = element;
  this.options = $.extend({}, OffCanvas.defaults, this.$element.data(), options);
  this.$lastTrigger = $();

  this._init();
  this._events();

  Foundation.registerPlugin(this);
}

OffCanvas.defaults = {
  /**
   * Allow the user to click outside of the menu to close it.
   * @option
   * @example true
   */
  closeOnClick: true,
  /**
   * Amount of time in ms the open and close transition requires. If none selected, pulls from body style.
   * @option
   * @example 500
   */
  transitionTime: 0,
  /**
   * Direction the offcanvas opens from. Determines class applied to body.
   * @option
   * @example left
   */
  position: 'left',
  /**
   * Force the page to scroll to top on open.
   * @option
   * @example false
   */
  forceTop: false,
  /**
   * Allow the offcanvas to be sticky while open. Does nothing if Sass option `$maincontent-prevent-scroll === true`.
   * Performance in Safari OSX/iOS is not great.
   * @option
   * @example false
   */
  isSticky: false,
  /**
   * Allow the offcanvas to remain open for certain breakpoints. Can be used with `isSticky`.
   * @option
   * @example false
   */
  isRevealed: false,
  /**
   * Breakpoint at which to reveal. JS will use a RegExp to target standard classes, if changing classnames, pass your class @`revealClass`.
   * @option
   * @example reveal-for-large
   */
  revealOn: null,
  /**
   * Force focus to the offcanvas on open. If true, will focus the opening trigger on close.
   * @option
   * @example true
   */
  autoFocus: true,
  /**
   * Class used to force an offcanvas to remain open. Foundation defaults for this are `reveal-for-large` & `reveal-for-medium`.
   * @option
   * TODO improve the regex testing for this.
   * @example reveal-for-large
   */
  revealClass: 'reveal-for-'
};

/**
 * Initializes the off-canvas wrapper by adding the exit overlay (if needed).
 * @function
 * @private
 */
OffCanvas.prototype._init = function() {
  var id = this.$element.attr('id');

  this.$element.attr('aria-hidden', 'true');

  // Find triggers that affect this element and add aria-expanded to them
  $(document)
    .find('[data-open="'+id+'"], [data-close="'+id+'"], [data-toggle="'+id+'"]')
    .attr('aria-expanded', 'false')
    .attr('aria-controls', id);

  // Add a close trigger over the body if necessary
  if (this.options.closeOnClick){
    if($('.js-off-canvas-exit').length){
      this.$exiter = $('.js-off-canvas-exit');
    }else{
      var exiter = document.createElement('div');
      exiter.setAttribute('class', 'js-off-canvas-exit');
      $('[data-off-canvas-content]').append(exiter);

      this.$exiter = $(exiter);
    }
  }

  this.options.isRevealed = this.options.isRevealed || new RegExp(this.options.revealClass, 'g').test(this.$element[0].className);

  if(this.options.isRevealed){
    this.options.revealOn = this.options.revealOn || this.$element[0].className.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2];
    this._setMQChecker();
  }
  if(!this.options.transitionTime){
    this.options.transitionTime = parseFloat(window.getComputedStyle($('[data-off-canvas-wrapper]')[0]).transitionDuration) * 1000;
  }
};

/**
 * Adds event handlers to the off-canvas wrapper and the exit overlay.
 * @function
 * @private
 */
OffCanvas.prototype._events = function() {
  this.$element.on({
    'open.zf.trigger': this.open.bind(this),
    'close.zf.trigger': this.close.bind(this),
    'toggle.zf.trigger': this.toggle.bind(this),
    'keydown.zf.offcanvas': this._handleKeyboard.bind(this)
  });

  if (this.$exiter.length) {
    var _this = this;
    this.$exiter.on({'click.zf.offcanvas': this.close.bind(this)});
  }
};
/**
 * Applies event listener for elements that will reveal at certain breakpoints.
 * @private
 */
OffCanvas.prototype._setMQChecker = function(){
  var _this = this;

  $(window).on('changed.zf.mediaquery', function(){
    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
      _this.reveal(true);
    }else{
      _this.reveal(false);
    }
  }).one('load.zf.offcanvas', function(){
    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
      _this.reveal(true);
    }
  });
};
/**
 * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
 * @param {Boolean} isRevealed - true if element should be revealed.
 * @function
 */
OffCanvas.prototype.reveal = function(isRevealed){
  var closer = this.$element.find('[data-close]');
  if(isRevealed){
    if(!this.options.forceTop){
      var scrollPos = parseInt(window.pageYOffset);
      this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
    }
    if(this.options.isSticky){ this._stick(); }
    if(closer.length){ closer.hide(); }
  }else{
    if(this.options.isSticky || !this.options.forceTop){
      this.$element[0].style.transform = '';
      $(window).off('scroll.zf.offcanvas');
    }
    if(closer.length){
      closer.show();
    }
  }
};

/**
 * Opens the off-canvas menu.
 * @function
 * @param {Object} event - Event object passed from listener.
 * @param {jQuery} trigger - element that triggered the off-canvas to open.
 * @fires OffCanvas#opened
 */
OffCanvas.prototype.open = function(event, trigger) {
  if (this.$element.hasClass('is-open')){ return; }
  var _this = this,
      $body = $(document.body);

  if(!this.options.forceTop){
    var scrollPos = parseInt(window.pageYOffset);
    this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
    if(this.$exiter.length){
      this.$exiter[0].style.transform = 'translate(0,' + scrollPos + 'px)';
    }
  }
  /**
   * Fires when the off-canvas menu opens.
   * @event OffCanvas#opened
   */
  Foundation.Move(this.options.transitionTime, this.$element, function(){
    $('[data-off-canvas-wrapper]').addClass('is-off-canvas-open is-open-'+ _this.options.position);

    _this.$element
      .addClass('is-open')
      .attr('aria-hidden', 'false')
      .trigger('opened.zf.offcanvas');

    if(_this.options.isSticky){
      _this._stick();
    }
  });
  if(trigger){
    this.$lastTrigger = trigger.attr('aria-expanded', 'true');
  }
  if(this.options.autoFocus){
    this.$element.one('finished.zf.animate', function(){
      _this.$element.find('a, button').eq(0).focus();
    });
  }
};
/**
 * Allows the offcanvas to appear sticky utilizing translate properties.
 * @private
 */
OffCanvas.prototype._stick = function(){
  var elStyle = this.$element[0].style;

  if(this.options.closeOnClick){
    var exitStyle = this.$exiter[0].style;
  }

  $(window).on('scroll.zf.offcanvas', function(e){
    console.log(e);
    var pageY = window.pageYOffset;
    elStyle.transform = 'translate(0,' + pageY + 'px)';
    if(exitStyle !== undefined){ exitStyle.transform = 'translate(0,' + pageY + 'px)'; }
  });
  // this.$element.trigger('stuck.zf.offcanvas');
};
/**
 * Closes the off-canvas menu.
 * @function
 * @fires OffCanvas#closed
 */
OffCanvas.prototype.close = function() {
  if(!this.$element.hasClass('is-open')){ return; }

  var _this = this;

   Foundation.Move(this.options.transitionTime, this.$element, function(){
    $('[data-off-canvas-wrapper]').removeClass('is-off-canvas-open is-open-'+_this.options.position);

    _this.$element.removeClass('is-open');
    // Foundation._reflow();
  });
  this.$element.attr('aria-hidden', 'true')
    /**
     * Fires when the off-canvas menu opens.
     * @event OffCanvas#closed
     */
      .trigger('closed.zf.offcanvas');
  if(_this.options.isSticky || !_this.options.forceTop){
    setTimeout(function(){
      _this.$element[0].style.transform = '';
      $(window).off('scroll.zf.offcanvas');
    }, this.options.transitionTime);
  }

  this.$lastTrigger.attr('aria-expanded', 'false');
};

/**
 * Toggles the off-canvas menu open or closed.
 * @function
 * @param {Object} event - Event object passed from listener.
 * @param {jQuery} trigger - element that triggered the off-canvas to open.
 */
OffCanvas.prototype.toggle = function(event, trigger) {
  if (this.$element.hasClass('is-open')) {
    this.close(event, trigger);
  }
  else {
    this.open(event, trigger);
  }
};

/**
 * Handles keyboard input when detected. When the escape key is pressed, the off-canvas menu closes, and focus is restored to the element that opened the menu.
 * @function
 * @private
 */
OffCanvas.prototype._handleKeyboard = function(event) {
  if (event.which !== 27) return;

  event.stopPropagation();
  event.preventDefault();
  this.close();
  this.$lastTrigger.focus();
};
/**
 * Destroys the offcanvas plugin.
 * @function
 */
OffCanvas.prototype.destroy = function(){
  //TODO make this...
};

Foundation.plugin(OffCanvas);

}(jQuery, Foundation);
<commit_msg>:( removes ability for offcanvas to be sticky and forces scrolltop on open... for now, mwahahahaha<commit_after>/**
 * OffCanvas module.
 * @module foundation.offcanvas
 * @requires foundation.util.triggers
 * @requires foundation.util.motion
 */
!function($, Foundation) {

'use strict';

/**
 * Creates a new instance of an off-canvas wrapper.
 * @class
 * @fires OffCanvas#init
 * @param {Object} element - jQuery object to initialize.
 * @param {Object} options - Overrides to the default plugin settings.
 */
function OffCanvas(element, options) {
  this.$element = element;
  this.options = $.extend({}, OffCanvas.defaults, this.$element.data(), options);
  this.$lastTrigger = $();

  this._init();
  this._events();

  Foundation.registerPlugin(this);
}

OffCanvas.defaults = {
  /**
   * Allow the user to click outside of the menu to close it.
   * @option
   * @example true
   */
  closeOnClick: true,
  /**
   * Amount of time in ms the open and close transition requires. If none selected, pulls from body style.
   * @option
   * @example 500
   */
  transitionTime: 0,
  /**
   * Direction the offcanvas opens from. Determines class applied to body.
   * @option
   * @example left
   */
  position: 'left',
  /**
   * Force the page to scroll to top on open.
   */
  forceTop: true,
  /**
   * Allow the offcanvas to be sticky while open. Does nothing if Sass option `$maincontent-prevent-scroll === true`.
   * Performance in Safari OSX/iOS is not great.
   */
  // isSticky: false,
  /**
   * Allow the offcanvas to remain open for certain breakpoints. Can be used with `isSticky`.
   * @option
   * @example false
   */
  isRevealed: false,
  /**
   * Breakpoint at which to reveal. JS will use a RegExp to target standard classes, if changing classnames, pass your class @`revealClass`.
   * @option
   * @example reveal-for-large
   */
  revealOn: null,
  /**
   * Force focus to the offcanvas on open. If true, will focus the opening trigger on close.
   * @option
   * @example true
   */
  autoFocus: true,
  /**
   * Class used to force an offcanvas to remain open. Foundation defaults for this are `reveal-for-large` & `reveal-for-medium`.
   * @option
   * TODO improve the regex testing for this.
   * @example reveal-for-large
   */
  revealClass: 'reveal-for-'
};

/**
 * Initializes the off-canvas wrapper by adding the exit overlay (if needed).
 * @function
 * @private
 */
OffCanvas.prototype._init = function() {
  var id = this.$element.attr('id');

  this.$element.attr('aria-hidden', 'true');

  // Find triggers that affect this element and add aria-expanded to them
  $(document)
    .find('[data-open="'+id+'"], [data-close="'+id+'"], [data-toggle="'+id+'"]')
    .attr('aria-expanded', 'false')
    .attr('aria-controls', id);

  // Add a close trigger over the body if necessary
  if (this.options.closeOnClick){
    if($('.js-off-canvas-exit').length){
      this.$exiter = $('.js-off-canvas-exit');
    }else{
      var exiter = document.createElement('div');
      exiter.setAttribute('class', 'js-off-canvas-exit');
      $('[data-off-canvas-content]').append(exiter);

      this.$exiter = $(exiter);
    }
  }

  this.options.isRevealed = this.options.isRevealed || new RegExp(this.options.revealClass, 'g').test(this.$element[0].className);

  if(this.options.isRevealed){
    this.options.revealOn = this.options.revealOn || this.$element[0].className.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2];
    this._setMQChecker();
  }
  if(!this.options.transitionTime){
    this.options.transitionTime = parseFloat(window.getComputedStyle($('[data-off-canvas-wrapper]')[0]).transitionDuration) * 1000;
  }
};

/**
 * Adds event handlers to the off-canvas wrapper and the exit overlay.
 * @function
 * @private
 */
OffCanvas.prototype._events = function() {
  this.$element.on({
    'open.zf.trigger': this.open.bind(this),
    'close.zf.trigger': this.close.bind(this),
    'toggle.zf.trigger': this.toggle.bind(this),
    'keydown.zf.offcanvas': this._handleKeyboard.bind(this)
  });

  if (this.$exiter.length) {
    var _this = this;
    this.$exiter.on({'click.zf.offcanvas': this.close.bind(this)});
  }
};
/**
 * Applies event listener for elements that will reveal at certain breakpoints.
 * @private
 */
OffCanvas.prototype._setMQChecker = function(){
  var _this = this;

  $(window).on('changed.zf.mediaquery', function(){
    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
      _this.reveal(true);
    }else{
      _this.reveal(false);
    }
  }).one('load.zf.offcanvas', function(){
    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
      _this.reveal(true);
    }
  });
};
/**
 * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
 * @param {Boolean} isRevealed - true if element should be revealed.
 * @function
 */
OffCanvas.prototype.reveal = function(isRevealed){
  var $closer = this.$element.find('[data-close]');
  if(isRevealed){
    // if(!this.options.forceTop){
    //   var scrollPos = parseInt(window.pageYOffset);
    //   this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
    // }
    // if(this.options.isSticky){ this._stick(); }
    if($closer.length){ $closer.hide(); }
  }else{
    // if(this.options.isSticky || !this.options.forceTop){
    //   this.$element[0].style.transform = '';
    //   $(window).off('scroll.zf.offcanvas');
    // }
    if($closer.length){
      $closer.show();
    }
  }
};

/**
 * Opens the off-canvas menu.
 * @function
 * @param {Object} event - Event object passed from listener.
 * @param {jQuery} trigger - element that triggered the off-canvas to open.
 * @fires OffCanvas#opened
 */
OffCanvas.prototype.open = function(event, trigger) {
  if (this.$element.hasClass('is-open')){ return; }
  var _this = this,
      $body = $(document.body);
  $('body').scrollTop(0);
  // window.pageYOffset = 0;

  // if(!this.options.forceTop){
  //   var scrollPos = parseInt(window.pageYOffset);
  //   this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
  //   if(this.$exiter.length){
  //     this.$exiter[0].style.transform = 'translate(0,' + scrollPos + 'px)';
  //   }
  // }
  /**
   * Fires when the off-canvas menu opens.
   * @event OffCanvas#opened
   */
  Foundation.Move(this.options.transitionTime, this.$element, function(){
    $('[data-off-canvas-wrapper]').addClass('is-off-canvas-open is-open-'+ _this.options.position);

    _this.$element
      .addClass('is-open')
      .attr('aria-hidden', 'false')
      .trigger('opened.zf.offcanvas');

    // if(_this.options.isSticky){
    //   _this._stick();
    // }
  });
  if(trigger){
    this.$lastTrigger = trigger.attr('aria-expanded', 'true');
  }
  if(this.options.autoFocus){
    this.$element.one('finished.zf.animate', function(){
      _this.$element.find('a, button').eq(0).focus();
    });
  }
};
/**
 * Allows the offcanvas to appear sticky utilizing translate properties.
 * @private
 */
// OffCanvas.prototype._stick = function(){
//   var elStyle = this.$element[0].style;
//
//   if(this.options.closeOnClick){
//     var exitStyle = this.$exiter[0].style;
//   }
//
//   $(window).on('scroll.zf.offcanvas', function(e){
//     console.log(e);
//     var pageY = window.pageYOffset;
//     elStyle.transform = 'translate(0,' + pageY + 'px)';
//     if(exitStyle !== undefined){ exitStyle.transform = 'translate(0,' + pageY + 'px)'; }
//   });
//   // this.$element.trigger('stuck.zf.offcanvas');
// };
/**
 * Closes the off-canvas menu.
 * @function
 * @fires OffCanvas#closed
 */
OffCanvas.prototype.close = function() {
  if(!this.$element.hasClass('is-open')){ return; }

  var _this = this;

   Foundation.Move(this.options.transitionTime, this.$element, function(){
    $('[data-off-canvas-wrapper]').removeClass('is-off-canvas-open is-open-'+_this.options.position);

    _this.$element.removeClass('is-open');
    // Foundation._reflow();
  });
  this.$element.attr('aria-hidden', 'true')
    /**
     * Fires when the off-canvas menu opens.
     * @event OffCanvas#closed
     */
      .trigger('closed.zf.offcanvas');
  // if(_this.options.isSticky || !_this.options.forceTop){
  //   setTimeout(function(){
  //     _this.$element[0].style.transform = '';
  //     $(window).off('scroll.zf.offcanvas');
  //   }, this.options.transitionTime);
  // }

  this.$lastTrigger.attr('aria-expanded', 'false');
};

/**
 * Toggles the off-canvas menu open or closed.
 * @function
 * @param {Object} event - Event object passed from listener.
 * @param {jQuery} trigger - element that triggered the off-canvas to open.
 */
OffCanvas.prototype.toggle = function(event, trigger) {
  if (this.$element.hasClass('is-open')) {
    this.close(event, trigger);
  }
  else {
    this.open(event, trigger);
  }
};

/**
 * Handles keyboard input when detected. When the escape key is pressed, the off-canvas menu closes, and focus is restored to the element that opened the menu.
 * @function
 * @private
 */
OffCanvas.prototype._handleKeyboard = function(event) {
  if (event.which !== 27) return;

  event.stopPropagation();
  event.preventDefault();
  this.close();
  this.$lastTrigger.focus();
};
/**
 * Destroys the offcanvas plugin.
 * @function
 */
OffCanvas.prototype.destroy = function(){
  //TODO make this...
};

Foundation.plugin(OffCanvas);

}(jQuery, Foundation);
<|endoftext|>


<commit_before>'use strict';

function fakeNgModel(initValue){
	return {
		$setViewValue: function(value){
			this.$viewValue = value;
		},
		$viewValue: initValue
	};
}

angular.module('luegg.directives', []).directive('scrollGlue', function() {
	return {
		priority: 1,
		require: ['?ngModel'],
		restrict: 'A',
		link: function(scope, $el, attrs, ctrls) {
			var el = $el[0],
				ngModel = ctrls[0] || fakeNgModel(true);

			var lastPos = 0;
			var scrollToBottom = function() {
				el.scrollTop = el.scrollHeight;
			};

			var shouldActivateAutoScroll = function() {
				// + 1 catches off by one errors in chrome
				return el.scrollTop + el.clientHeight + 1 >= el.scrollHeight;
			};

			scope.$watch(function() {
				if(ngModel.$viewValue){
					scrollToBottom();
				}
			});

			$el.bind('scroll', function() {
				var activate = shouldActivateAutoScroll();
				if(activate !== ngModel.$viewValue){
					scope.$apply(ngModel.$setViewValue.bind(ngModel, activate));
				}
			});
		}
	};
});

function loadCss(url) {
	if(document.getElementById(url)) { console.log(url); return false; }
	var link = document.createElement("link");
	link.type = "text/css";
	link.rel = "stylesheet";
	link.href = url;
	link.id = url;
	document.getElementsByTagName("head")[0].appendChild(link);
}

/* App Module */
var Kamato = angular.module('Kamato', [
	'ngRoute',
	'KamatoControllers',
	'socket-io',
	'google-maps',
	'luegg.directives'
]);

Kamato.config(function($controllerProvider, $compileProvider, $filterProvider, $routeProvider, $provide) {
	Kamato.register = {
		controller: $controllerProvider.register,
		directive: $compileProvider.directive,
		filter: $filterProvider.register,
		route: $routeProvider,
		factory: $provide.factory,
		service: $provide.service
	};

	$routeProvider.when('/', {
		templateUrl: './widgets/login/template.html',
		resolve: {
			load: function($q, $route, $rootScope) {
				var deferred = $q.defer();
				var dependencies = ['./widgets/login/controller.js'];

				loadCss('./widgets/login/style.css');

				$script(dependencies, function () {
					$rootScope.$apply(function() {
						deferred.resolve();
					});
				});

				return deferred.promise;
			}
		}
	}).
	when('/:widget', {
		templateUrl: function(path) {
			return './widgets/' + path.widget + '/template.html';
		},

		resolve: {
			load: function($q, $route, $rootScope) {
				var deferred = $q.defer();
				var dependencies = ['./widgets/' + $route.current.params.widget + '/controller.js'];

				loadCss('./widgets/' + $route.current.params.widget + '/style.css');

				$script(dependencies, function () {
					$rootScope.$apply(function() {
						deferred.resolve();
					});
				});

				return deferred.promise;
			}
		}
	}).
	when('/platform/:widget', {
		templateUrl: function(path) {
			return './widgets/platform/platform_' + path.widget + '/template.html';
		},

		resolve: {
			load: function($q, $route, $rootScope) {
				var deferred = $q.defer();
				var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

				loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

				$script(dependencies, function () {
					$rootScope.$apply(function() {
						deferred.resolve();
					});
				});

				return deferred.promise;
			}
		}
	}).
    when('/platform/APP/:APP/:widget', {
        templateUrl: function (path) {
            return './widgets/platform/platform_' + path.widget + '/template.html';
        },

        resolve: {
            load: function ($q, $route, $rootScope) {
                var deferred = $q.defer();
                var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

                loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

                $script(dependencies, function () {
                    $rootScope.$apply(function () {
                        deferred.resolve();
                    });
                });

                return deferred.promise;
            }
        }
    }).
	when('/APP/:widget', {
		templateUrl: function(path) {
			return './widgets/platform/platform_' + path.widget + '/template.html';
		},

		resolve: {
			load: function($q, $route, $rootScope) {
				var deferred = $q.defer();
				var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

				loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

				$script(dependencies, function () {
					$rootScope.$apply(function() {
						deferred.resolve();
					});
				});

				return deferred.promise;
			}
		}
	}).
	otherwise({
		redirectTo: '/'
	});;
});

Kamato.controller('TemplateCtrl',  function($scope){

	$scope.profile_menu =[
		{'title':'My Profile'},
		{'title': 'Messages'},
		{'title': 'Settings'},
		{'title': 'Sign Out'}
	];

	$scope.sidebar= [
		{'name': 'APP管理', 'link': '#/platform/APP', 'icon': 'sa-side-app'},
		{'name': 'EasyDB Manager', 'link': '#/platform/easydb', 'icon': 'sa-side-db'},
		{'name': 'Channel Manager', 'link': '#/platform/channel', 'icon': 'sa-side-channel'},
		{'name': 'API Manager', 'link': '#/platform/api', 'icon': 'sa-side-auth'},
		{'name': '外部資源託管', 'link': '#/platform/resource', 'icon': 'sa-side-resource'}
	];
	$scope.selected = undefined;
	$scope.active = function(menu) {
		$scope.selected = menu.name;
	}
});

Kamato.directive('row', function($compile) {
	return {
		restrict: 'E',
		replace: true,
		templateUrl: 'widgets/platform/template-row.html'
	}
});

Kamato.directive('chart', function() {
	return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            var rawdata = scope[attrs.id];
            var chart = null;

            var options = {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 1,
                        fill: 0.25,
                    },
                    color: 'rgba(255,255,255,0.7)',
                    shadowSize: 0,
                    points: {
                        show: true,
                    }
                },

                yaxis: {
                    // min: 0,
                    // max: 20,
                    tickColor: 'rgba(255,255,255,0.15)',
                    tickDecimals: 0,
                    font :{
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    },
                    shadowSize: 0,
                },

                xaxis: {
				    mode: "time",
               		timeformat:"%H:%M:%S",
               		timezone: "browser",
                	tickSize: [5, "second"], // 顯示一筆資料的間隔
                	// min: 1,
                	// max: 30,
                	// tickSize: 1,
                	// tickColor: 'red',
                    // tickColor: 'rgba(255,255,255,0)', //文字顏色
                    // tickDecimals: 0, //小數點
                    font :{
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    }
                },
                grid: {
                    borderWidth: 1,
                    borderColor: 'rgba(255,255,255,0.25)',
                    labelMargin:10,
                    hoverable: true,
                    clickable: true,
                    mouseActiveRadius:6,
                },
                legend: {
                    show: false,  //顯示資料方塊

                }
            }

            var label;
            if( attrs.id == "connectors"){
            	label = 'Connectors';
            }
            else if(attrs.id == "msgs"){
            	label = 'Messages';
            }
            else{
            	label = attrs.id;
            }

        	chart = $.plot(elem, [{data: rawdata,}], options);
            elem.show();

    		var pre_point = null;
            elem.bind("plothover", function(event, pos, item){
				if(item){
					if(pre_point != item.dataIndex){
						pre_point = item.dataIndex;   //檢查滑鼠是否停留在同一點
						var x = item.datapoint[0];
						var y = item.datapoint[1];
						var date = new Date(x);
						 var div = document.createElement("div");
						 var class_name = document.createAttribute("class");
						 var style = document.createAttribute("style");
						 class_name.value = "toolTip";
						 if( item.pageY <76 || item.pageX < 110){
						 	style.value = "top:"+(item.pageY)+"px; left:"+(item.pageX)+"px";
						 }
						 else{
						 	style.value = "top:"+(item.pageY)+"px; left:"+(item.pageX-110)+"px";
						 }
   						 div.innerHTML = '
Time: '+date.toLocaleTimeString()+'

'+label+': '+y+'
';
						 div.setAttributeNode(style);
						 div.setAttributeNode(class_name);
						 document.body.appendChild(div);
					}
				}
				//toolTip不是第一次出現
				else if((document.getElementsByClassName("toolTip").length)>0){
					pre_point = null;
					var tooltip = document.getElementsByClassName("toolTip");
					for( var i = 0 ; i < tooltip.length ; i++){
						tooltip[i].remove();
					}
				}
            });
		}
	};
});

Kamato.directive('ngEnter', function() {
	return function (scope, element, attrs) {
		element.bind("keydown keypress", function(event) {
			if(event.which === 13) {
				scope.$apply(function() {
					scope.$eval(attrs.ngEnter);
				});

				event.preventDefault();
			}
		});
	};
});

Kamato.directive('activitychart', function() {
	return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            var rawdata = scope[attrs.id];
            var chart = null;
            var options = {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 1,
                        fill: 0.25,
                    },
                    color: 'rgba(255,255,255,0.7)',
                    shadowSize: 0,
                    points: {
                        show: true,
                    }
                },

                yaxis: {
                    // min: 0,
                    // max: 20,
                    tickColor: 'rgba(255,255,255,0.15)',
                    tickDecimals: 0,
                    font :{
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    },
                    shadowSize: 0,
                },

                xaxis: {
				    mode: "time",
               		timeformat:"%Y/%m/%d",
               		timezone: "browser",
                	tickSize: [5, "day"], // 顯示每一筆資料彼此間隔的差距
                	// min: 1,
                	// max: 30,
                	// tickSize: 1,
                	// tickColor: 'red',
                    // tickColor: 'rgba(255,255,255,0)', //文字顏色
                    // tickDecimals: 0, //小數點
                    font :{
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    }
                },
                grid: {
                    borderWidth: 1,
                    borderColor: 'rgba(255,255,255,0.25)',
                    labelMargin:10,
                    hoverable: true,
                    clickable: true,
                    mouseActiveRadius:6,
                },
                legend: {
                    show: false,  //顯示資料方塊

                }
            }

            var label;
            	label = attrs.id;

        	chart = $.plot(elem, [{data: rawdata,}], options);
            elem.show();

    		var pre_point = null;
            elem.bind("plothover", function(event, pos, item){
				if(item){
					if(pre_point != item.dataIndex){
						pre_point = item.dataIndex;   //檢查滑鼠是否停留在同一點
						var x = item.datapoint[0];
						var y = item.datapoint[1];
						var date = new Date(x);
						 var div = document.createElement("div");
						 var class_name = document.createAttribute("class");
						 var style = document.createAttribute("style");
						 class_name.value = "toolTip";
						 if( item.pageY <76 || item.pageX < 110){
						 	style.value = "top:"+(item.pageY)+"px; left:"+(item.pageX)+"px";
						 }
						 else{
						 	style.value = "top:"+(item.pageY)+"px; left:"+(item.pageX-110)+"px";
						 }
   						 div.innerHTML = '
Date: '+date.toLocaleDateString()+'

'+label+': '+y+'
';
						 div.setAttributeNode(style);
						 div.setAttributeNode(class_name);
						 document.body.appendChild(div);
					}
				}
				//toolTip不是第一次出現
				else if((document.getElementsByClassName("toolTip").length)>0){
					pre_point = null;
					var tooltip = document.getElementsByClassName("toolTip");
					for( var i = 0 ; i < tooltip.length ; i++){
						tooltip[i].remove();
					}
				}
            });
		}
	};
});<commit_msg>Fix APP templateUrl<commit_after>'use strict';

function fakeNgModel(initValue) {
    return {
        $setViewValue: function (value) {
            this.$viewValue = value;
        },
        $viewValue: initValue
    };
}

angular.module('luegg.directives', []).directive('scrollGlue', function () {
    return {
        priority: 1,
        require: ['?ngModel'],
        restrict: 'A',
        link: function (scope, $el, attrs, ctrls) {
            var el = $el[0],
				ngModel = ctrls[0] || fakeNgModel(true);

            var lastPos = 0;
            var scrollToBottom = function () {
                el.scrollTop = el.scrollHeight;
            };

            var shouldActivateAutoScroll = function () {
                // + 1 catches off by one errors in chrome
                return el.scrollTop + el.clientHeight + 1 >= el.scrollHeight;
            };

            scope.$watch(function () {
                if (ngModel.$viewValue) {
                    scrollToBottom();
                }
            });

            $el.bind('scroll', function () {
                var activate = shouldActivateAutoScroll();
                if (activate !== ngModel.$viewValue) {
                    scope.$apply(ngModel.$setViewValue.bind(ngModel, activate));
                }
            });
        }
    };
});

function loadCss(url) {
    if (document.getElementById(url)) { console.log(url); return false; }
    var link = document.createElement("link");
    link.type = "text/css";
    link.rel = "stylesheet";
    link.href = url;
    link.id = url;
    document.getElementsByTagName("head")[0].appendChild(link);
}

/* App Module */
var Kamato = angular.module('Kamato', [
	'ngRoute',
	'KamatoControllers',
	'socket-io',
	'google-maps',
	'luegg.directives'
]);

Kamato.config(function ($controllerProvider, $compileProvider, $filterProvider, $routeProvider, $provide) {
    Kamato.register = {
        controller: $controllerProvider.register,
        directive: $compileProvider.directive,
        filter: $filterProvider.register,
        route: $routeProvider,
        factory: $provide.factory,
        service: $provide.service
    };

    $routeProvider.when('/', {
        templateUrl: './widgets/login/template.html',
        resolve: {
            load: function ($q, $route, $rootScope) {
                var deferred = $q.defer();
                var dependencies = ['./widgets/login/controller.js'];

                loadCss('./widgets/login/style.css');

                $script(dependencies, function () {
                    $rootScope.$apply(function () {
                        deferred.resolve();
                    });
                });

                return deferred.promise;
            }
        }
    }).
	when('/:widget', {
	    templateUrl: function (path) {
	        return './widgets/' + path.widget + '/template.html';
	    },

	    resolve: {
	        load: function ($q, $route, $rootScope) {
	            var deferred = $q.defer();
	            var dependencies = ['./widgets/' + $route.current.params.widget + '/controller.js'];

	            loadCss('./widgets/' + $route.current.params.widget + '/style.css');

	            $script(dependencies, function () {
	                $rootScope.$apply(function () {
	                    deferred.resolve();
	                });
	            });

	            return deferred.promise;
	        }
	    }
	}).
	when('/platform/:widget', {
	    templateUrl: function (path) {
	        return './widgets/platform/platform_' + path.widget + '/template.html';
	    },

	    resolve: {
	        load: function ($q, $route, $rootScope) {
	            var deferred = $q.defer();
	            var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

	            loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

	            $script(dependencies, function () {
	                $rootScope.$apply(function () {
	                    deferred.resolve();
	                });
	            });

	            return deferred.promise;
	        }
	    }
	}).
    when('/platform/APP/:APP/:widget', {
        templateUrl: function (path) {
            return './widgets/platform/platform_' + path.widget + '/template.html';
        },

        resolve: {
            load: function ($q, $route, $rootScope) {
                var deferred = $q.defer();
                var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

                loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

                $script(dependencies, function () {
                    $rootScope.$apply(function () {
                        deferred.resolve();
                    });
                });

                return deferred.promise;
            }
        }
    }).
	when('/APP/:widget', {
	    templateUrl: function (path) {
	        return './widgets/platform/platform_' + path.widget + '/template.html';
	    },

	    resolve: {
	        load: function ($q, $route, $rootScope) {
	            var deferred = $q.defer();
	            var dependencies = ['./widgets/platform/platform_' + $route.current.params.widget + '/controller.js'];

	            loadCss('./widgets/platform/platform_' + $route.current.params.widget + '/style.css');

	            $script(dependencies, function () {
	                $rootScope.$apply(function () {
	                    deferred.resolve();
	                });
	            });

	            return deferred.promise;
	        }
	    }
	}).
	otherwise({
	    redirectTo: '/'
	});;
});

Kamato.controller('TemplateCtrl', function ($scope) {

    $scope.profile_menu = [
		{ 'title': 'My Profile' },
		{ 'title': 'Messages' },
		{ 'title': 'Settings' },
		{ 'title': 'Sign Out' }
    ];

    $scope.sidebar = [
		{ 'name': 'APP管理', 'link': '#/platform/APP', 'icon': 'sa-side-app' },
		{ 'name': 'EasyDB Manager', 'link': '#/platform/easydb', 'icon': 'sa-side-db' },
		{ 'name': 'Channel Manager', 'link': '#/platform/channel', 'icon': 'sa-side-channel' },
		{ 'name': 'API Manager', 'link': '#/platform/api', 'icon': 'sa-side-auth' },
		{ 'name': '外部資源託管', 'link': '#/platform/resource', 'icon': 'sa-side-resource' }
    ];
    $scope.selected = undefined;
    $scope.active = function (menu) {
        $scope.selected = menu.name;
    }
});

Kamato.directive('row', function ($compile) {
    return {
        restrict: 'E',
        replace: true,
        templateUrl: 'widgets/platform/template-row.html'
    }
});

Kamato.directive('chart', function () {
    return {
        restrict: 'E',
        link: function (scope, elem, attrs) {
            var rawdata = scope[attrs.id];
            var chart = null;

            var options = {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 1,
                        fill: 0.25,
                    },
                    color: 'rgba(255,255,255,0.7)',
                    shadowSize: 0,
                    points: {
                        show: true,
                    }
                },

                yaxis: {
                    // min: 0,
                    // max: 20,
                    tickColor: 'rgba(255,255,255,0.15)',
                    tickDecimals: 0,
                    font: {
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    },
                    shadowSize: 0,
                },

                xaxis: {
                    mode: "time",
                    timeformat: "%H:%M:%S",
                    timezone: "browser",
                    tickSize: [5, "second"], // 顯示一筆資料的間隔
                    // min: 1,
                    // max: 30,
                    // tickSize: 1,
                    // tickColor: 'red',
                    // tickColor: 'rgba(255,255,255,0)', //文字顏色
                    // tickDecimals: 0, //小數點
                    font: {
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    }
                },
                grid: {
                    borderWidth: 1,
                    borderColor: 'rgba(255,255,255,0.25)',
                    labelMargin: 10,
                    hoverable: true,
                    clickable: true,
                    mouseActiveRadius: 6,
                },
                legend: {
                    show: false,  //顯示資料方塊

                }
            }

            var label;
            if (attrs.id == "connectors") {
                label = 'Connectors';
            }
            else if (attrs.id == "msgs") {
                label = 'Messages';
            }
            else {
                label = attrs.id;
            }

            chart = $.plot(elem, [{ data: rawdata, }], options);
            elem.show();

            var pre_point = null;
            elem.bind("plothover", function (event, pos, item) {
                if (item) {
                    if (pre_point != item.dataIndex) {
                        pre_point = item.dataIndex;   //檢查滑鼠是否停留在同一點
                        var x = item.datapoint[0];
                        var y = item.datapoint[1];
                        var date = new Date(x);
                        var div = document.createElement("div");
                        var class_name = document.createAttribute("class");
                        var style = document.createAttribute("style");
                        class_name.value = "toolTip";
                        if (item.pageY < 76 || item.pageX < 110) {
                            style.value = "top:" + (item.pageY) + "px; left:" + (item.pageX) + "px";
                        }
                        else {
                            style.value = "top:" + (item.pageY) + "px; left:" + (item.pageX - 110) + "px";
                        }
                        div.innerHTML = '
Time: ' + date.toLocaleTimeString() + '

' + label + ': ' + y + '
';
                        div.setAttributeNode(style);
                        div.setAttributeNode(class_name);
                        document.body.appendChild(div);
                    }
                }
                    //toolTip不是第一次出現
                else if ((document.getElementsByClassName("toolTip").length) > 0) {
                    pre_point = null;
                    var tooltip = document.getElementsByClassName("toolTip");
                    for (var i = 0 ; i < tooltip.length ; i++) {
                        tooltip[i].remove();
                    }
                }
            });
        }
    };
});

Kamato.directive('ngEnter', function () {
    return function (scope, element, attrs) {
        element.bind("keydown keypress", function (event) {
            if (event.which === 13) {
                scope.$apply(function () {
                    scope.$eval(attrs.ngEnter);
                });

                event.preventDefault();
            }
        });
    };
});

Kamato.directive('activitychart', function () {
    return {
        restrict: 'E',
        link: function (scope, elem, attrs) {
            var rawdata = scope[attrs.id];
            var chart = null;
            var options = {
                series: {
                    lines: {
                        show: true,
                        lineWidth: 1,
                        fill: 0.25,
                    },
                    color: 'rgba(255,255,255,0.7)',
                    shadowSize: 0,
                    points: {
                        show: true,
                    }
                },

                yaxis: {
                    // min: 0,
                    // max: 20,
                    tickColor: 'rgba(255,255,255,0.15)',
                    tickDecimals: 0,
                    font: {
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    },
                    shadowSize: 0,
                },

                xaxis: {
                    mode: "time",
                    timeformat: "%Y/%m/%d",
                    timezone: "browser",
                    tickSize: [5, "day"], // 顯示每一筆資料彼此間隔的差距
                    // min: 1,
                    // max: 30,
                    // tickSize: 1,
                    // tickColor: 'red',
                    // tickColor: 'rgba(255,255,255,0)', //文字顏色
                    // tickDecimals: 0, //小數點
                    font: {
                        lineHeight: 13,
                        style: "normal",
                        color: "rgba(255,255,255,0.8)",
                    }
                },
                grid: {
                    borderWidth: 1,
                    borderColor: 'rgba(255,255,255,0.25)',
                    labelMargin: 10,
                    hoverable: true,
                    clickable: true,
                    mouseActiveRadius: 6,
                },
                legend: {
                    show: false,  //顯示資料方塊

                }
            }

            var label;
            label = attrs.id;

            chart = $.plot(elem, [{ data: rawdata, }], options);
            elem.show();

            var pre_point = null;
            elem.bind("plothover", function (event, pos, item) {
                if (item) {
                    if (pre_point != item.dataIndex) {
                        pre_point = item.dataIndex;   //檢查滑鼠是否停留在同一點
                        var x = item.datapoint[0];
                        var y = item.datapoint[1];
                        var date = new Date(x);
                        var div = document.createElement("div");
                        var class_name = document.createAttribute("class");
                        var style = document.createAttribute("style");
                        class_name.value = "toolTip";
                        if (item.pageY < 76 || item.pageX < 110) {
                            style.value = "top:" + (item.pageY) + "px; left:" + (item.pageX) + "px";
                        }
                        else {
                            style.value = "top:" + (item.pageY) + "px; left:" + (item.pageX - 110) + "px";
                        }
                        div.innerHTML = '
Date: ' + date.toLocaleDateString() + '

' + label + ': ' + y + '
';
                        div.setAttributeNode(style);
                        div.setAttributeNode(class_name);
                        document.body.appendChild(div);
                    }
                }
                    //toolTip不是第一次出現
                else if ((document.getElementsByClassName("toolTip").length) > 0) {
                    pre_point = null;
                    var tooltip = document.getElementsByClassName("toolTip");
                    for (var i = 0 ; i < tooltip.length ; i++) {
                        tooltip[i].remove();
                    }
                }
            });
        }
    };
});<|endoftext|>