<commit_before>var jsontr = require('./json-transform.js');

var items = require('./exhibit-pages.json');
var transform = {
  nid: {
    newName: 'id',
    props: {value:String}
  },
  uuid: {
    props: {value:String}
  },
  title: {
    props: {value: String}
  },
  body: {
    props: {value: String}
  },
  field_parent_page: {
    newName: "parentPage",
    props: {target_id:{newName:'id',type:Number},target_uuid:{newName:'uuid',type:String}}
  },
  field_path: {
    newName: "path",
    props: {value: String}
  },
  field_sidebar: {
    newName: "sidebar",
    props: {target_uuid: {newName:'uuid',type:String}}
  },
  field_subnav: {
    newName: "subnav",
    props: {target_uuid: {newName:'uuid',type:String}}
  },
  field_serve_via_iframe: {
    newName: "iframe",
    props: {value:Boolean}
  },
  field_book_tour: {
    newName: 'bookTour',
    props: {value:String}
  },
  field_category_id: {
    newName: "categoryId",
    props: {value:String}
  },
  field_end_date: {
    newName: "endDate",
    props: {value:String}
  },
  field_start_date: {
    newName: "startDate",
    props: {value:String}
  },
  field_exhibition_css: {
    newName: "exhibition_css",
    props: {value:String}
  },
  field_further_location_informati: {
    newName: "locationInfo",
    props: {value:String}
  },
  field_image: {
    newName: "image",
    props: {alt:String, width:Number, height:Number, url:String}
  },
  field_libcal_id: {
    newName: "libcalId",
    props: {value:String}
  },
  field_library: {
    newName: "library",
    props: {target_id:{newName:"id",type:Number}, target_uuid:{newName:"uuid",type:String}}
  },
  field_press_kit: {
    newName: "pressKit",
    props: {uri:String}
  },
  field_redirects_from_: {
    newName: "redirectsFrom",
    props: {uri:String}
  },
  field_subtitle: {
    newName: "subTitle",
    props: {value:String}
  },
  field_url_slug: {
    newName: "urlSlug",
    props: {value:String}
  },
  field_style_exhibit_header: {
    newName: "styleHeaderColor",
    props: {color:String}
  },
  field_style_exhibit_header_text_: {
    newName: "styleHeaderTextColor",
    props: {color:String}
  },
  field_style_pre_footer_backgroun: {
    newName: "stylePreFooterBackColor",
    props: {color:String}
  },
  field_news_item_source_text_colo: {
    newName: "styleNewsItemSourceTextColor",
    props: {color:String}
  },
  field_style_font_import: {
    newName: "styleFontImport",
    props: {value:String}
  },
  field_style_body_font: {
    newName: "styleFont",
    props: {value:String}
  },
  field_style_body_font_fallback: {
    newName: "styleFontFallback",
    props: {value:String}
  },
  field_style_headline_font: {
    newName: "styleHeadlineFont",
    props: {value:String}
  },
  field_style_headline_font_fallba: {
    newName: "styleHeadlineFontFallback",
    props: {value:String}
  }
};
var json = JSON.stringify(jsontr.transform(items,transform))
  .replace(/drupal\.lib\.virginia\.edu\/sites\/default\/files\//g,"www.library.virginia.edu/files/")
  .replace(/\/sites\/default\/files\//g,"https://www.library.virginia.edu/files/")
console.log( json );
<commit_msg>fix for custom exhibit font format<commit_after>var jsontr = require('./json-transform.js');

var items = require('./exhibit-pages.json');
var transform = {
  nid: {
    newName: 'id',
    props: {value:String}
  },
  uuid: {
    props: {value:String}
  },
  title: {
    props: {value: String}
  },
  body: {
    props: {value: String}
  },
  field_parent_page: {
    newName: "parentPage",
    props: {target_id:{newName:'id',type:Number},target_uuid:{newName:'uuid',type:String}}
  },
  field_path: {
    newName: "path",
    props: {value: String}
  },
  field_sidebar: {
    newName: "sidebar",
    props: {target_uuid: {newName:'uuid',type:String}}
  },
  field_subnav: {
    newName: "subnav",
    props: {target_uuid: {newName:'uuid',type:String}}
  },
  field_serve_via_iframe: {
    newName: "iframe",
    props: {value:Boolean}
  },
  field_book_tour: {
    newName: 'bookTour',
    props: {value:String}
  },
  field_category_id: {
    newName: "categoryId",
    props: {value:String}
  },
  field_end_date: {
    newName: "endDate",
    props: {value:String}
  },
  field_start_date: {
    newName: "startDate",
    props: {value:String}
  },
  field_exhibition_css: {
    newName: "exhibition_css",
    props: {value:String}
  },
  field_further_location_informati: {
    newName: "locationInfo",
    props: {value:String}
  },
  field_image: {
    newName: "image",
    props: {alt:String, width:Number, height:Number, url:String}
  },
  field_libcal_id: {
    newName: "libcalId",
    props: {value:String}
  },
  field_library: {
    newName: "library",
    props: {target_id:{newName:"id",type:Number}, target_uuid:{newName:"uuid",type:String}}
  },
  field_press_kit: {
    newName: "pressKit",
    props: {uri:String}
  },
  field_redirects_from_: {
    newName: "redirectsFrom",
    props: {uri:String}
  },
  field_subtitle: {
    newName: "subTitle",
    props: {value:String}
  },
  field_url_slug: {
    newName: "urlSlug",
    props: {value:String}
  },
  field_style_exhibit_header: {
    newName: "styleHeaderColor",
    props: {color:String}
  },
  field_style_exhibit_header_text_: {
    newName: "styleHeaderTextColor",
    props: {color:String}
  },
  field_style_pre_footer_backgroun: {
    newName: "stylePreFooterBackColor",
    props: {color:String}
  },
  field_news_item_source_text_colo: {
    newName: "styleNewsItemSourceTextColor",
    props: {color:String}
  },
  field_style_font_import: {
    newName: "styleFontImport",
    props: {value:String}
  },
  field_style_body_font: {
    newName: "styleFont",
    props: {value:String}
  },
  field_style_body_font_fallback: {
    newName: "styleFontFallback",
    props: {value:String}
  },
  field_style_headline_font: {
    newName: "styleHeadlineFont",
    props: {value:String}
  },
  field_style_headline_font_fallba: {
    newName: "styleHeadlineFontFallback",
    props: {value:String}
  }
};

var items = jsontr.transform(items,transform).map(exh=>{
  if (exh.styleFontImport && exh.styleFontImport.indexOf("@import")==-1) exh.styleFontImport = "@import url('"+exh.styleFontImport+"');";
  return exh;
});

var json = JSON.stringify(items)
  .replace(/drupal\.lib\.virginia\.edu\/sites\/default\/files\//g,"www.library.virginia.edu/files/")
  .replace(/\/sites\/default\/files\//g,"https://www.library.virginia.edu/files/")
console.log( json );
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Fix Twitter links
// @description Fix Twitter links from your timeline and say goodbye to its URL shortener!
// @author      lucio-martinez
// @license     MIT
// @supportURL  https://github.com/luciomartinez/fix-twitter-links/issues
// @namespace	  https://github.com/luciomartinez/fix-twitter-links
// @version     0.5
// @grant       none
// @match	      https://twitter.com/*
// @match       https://tweetdeck.twitter.com/*
// ==/UserScript==

fixLinks();

function fixLinks() {
  const domain = window.location.hostname;
  if (domain === 'tweetdeck.twitter.com') {
    fixLinksInTweetDeck();
  } else {
    fixLinksInTwitter();
  }
}

function fixLinksInTwitter() {
  startWatchWithCleaner(parseLinksAtTwitter);
}

function fixLinksInTweetDeck() {
  startWatchWithCleaner(parseLinksAtTweetDeck);
}

function startWatchWithCleaner(cleaner) {
  startWatch(cleaner);
}

function startWatch(cleaner) {
  const watchedNode = document.getElementById('react-root');
  watchForChangesOnNodeAndRunCleaner(watchedNode, cleaner);
}

function watchForChangesOnNodeAndRunCleaner(targetNode, cleaner) {
  const observer = createObserverWithCallbackBounced(cleaner);
  observer.observe(targetNode, { subtree: true, childList: true });
}

function createObserverWithCallbackBounced(callback) {
  const callbackDebounced = debounce(callback);
  return new MutationObserver(callbackDebounced);
}

/**
 * Thanks to Chris Boakes!
 * https://chrisboakes.com/how-a-javascript-debounce-function-works/
 * @param {function} callback - Function to be debounced
 * @param {number} [wait=250] - Specifies the milliseconds to debounce
 * @returns {function} Debounced function
 */
function debounce(callback, wait = 250) {
  let timeout;
  return (...args) => {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => callback.apply(context, args), wait);
  };
}

function parseLinksAtTwitter() {
  const selectShortLinks = 'a[href^="https://t.co/"][title]:not([data-link-fixed])';
  const links = document.querySelectorAll(selectShortLinks);
  links.forEach((link) => {
    updateLinkAndMarkAsFixed(link, link.title);
  });
}

function parseLinksAtTweetDeck() {
  const selectFromTweets = '.tweet-text > .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed], .is-vishidden)';
  const selectFromQuotedTweets = '.quoted-tweet .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed])';
  const selectFromTweetDetail = '.tweet-detail .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed])';
  const multipleSelects = `${selectFromTweets}, ${selectFromQuotedTweets}, ${selectFromTweetDetail}`;
  const links = document.querySelectorAll(multipleSelects);
  links.forEach((link) => {
    updateLinkAndMarkAsFixed(link, link.dataset.fullUrl);
  });
}

function updateLinkAndMarkAsFixed(anchorEl, fixedUrl) {
  console.info('Fix Twitter links: URL parsed', fixedUrl);
  setHrefTo(anchorEl, fixedUrl);
  markAsFixed(anchorEl);
}

function setHrefTo(el, url) {
  el.href = url;
}

function markAsFixed(el) {
  el.dataset.linkFixed = '1';
}
<commit_msg>Add `@copyright` to the metadata block<commit_after>// ==UserScript==
// @name        Fix Twitter links
// @description Fix Twitter links from your timeline and say goodbye to its URL shortener!
// @author      lucio-martinez
// @license     MIT
// @copyright   2020, Lucio Martinez
// @supportURL  https://github.com/luciomartinez/fix-twitter-links/issues
// @namespace	  https://github.com/luciomartinez/fix-twitter-links
// @version     0.5
// @grant       none
// @match	      https://twitter.com/*
// @match       https://tweetdeck.twitter.com/*
// ==/UserScript==

fixLinks();

function fixLinks() {
  const domain = window.location.hostname;
  if (domain === 'tweetdeck.twitter.com') {
    fixLinksInTweetDeck();
  } else {
    fixLinksInTwitter();
  }
}

function fixLinksInTwitter() {
  startWatchWithCleaner(parseLinksAtTwitter);
}

function fixLinksInTweetDeck() {
  startWatchWithCleaner(parseLinksAtTweetDeck);
}

function startWatchWithCleaner(cleaner) {
  startWatch(cleaner);
}

function startWatch(cleaner) {
  const watchedNode = document.getElementById('react-root');
  watchForChangesOnNodeAndRunCleaner(watchedNode, cleaner);
}

function watchForChangesOnNodeAndRunCleaner(targetNode, cleaner) {
  const observer = createObserverWithCallbackBounced(cleaner);
  observer.observe(targetNode, { subtree: true, childList: true });
}

function createObserverWithCallbackBounced(callback) {
  const callbackDebounced = debounce(callback);
  return new MutationObserver(callbackDebounced);
}

/**
 * Thanks to Chris Boakes!
 * https://chrisboakes.com/how-a-javascript-debounce-function-works/
 * @param {function} callback - Function to be debounced
 * @param {number} [wait=250] - Specifies the milliseconds to debounce
 * @returns {function} Debounced function
 */
function debounce(callback, wait = 250) {
  let timeout;
  return (...args) => {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => callback.apply(context, args), wait);
  };
}

function parseLinksAtTwitter() {
  const selectShortLinks = 'a[href^="https://t.co/"][title]:not([data-link-fixed])';
  const links = document.querySelectorAll(selectShortLinks);
  links.forEach((link) => {
    updateLinkAndMarkAsFixed(link, link.title);
  });
}

function parseLinksAtTweetDeck() {
  const selectFromTweets = '.tweet-text > .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed], .is-vishidden)';
  const selectFromQuotedTweets = '.quoted-tweet .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed])';
  const selectFromTweetDetail = '.tweet-detail .url-ext[href^="https://t.co/"][data-full-url]:not([data-link-fixed])';
  const multipleSelects = `${selectFromTweets}, ${selectFromQuotedTweets}, ${selectFromTweetDetail}`;
  const links = document.querySelectorAll(multipleSelects);
  links.forEach((link) => {
    updateLinkAndMarkAsFixed(link, link.dataset.fullUrl);
  });
}

function updateLinkAndMarkAsFixed(anchorEl, fixedUrl) {
  console.info('Fix Twitter links: URL parsed', fixedUrl);
  setHrefTo(anchorEl, fixedUrl);
  markAsFixed(anchorEl);
}

function setHrefTo(el, url) {
  el.href = url;
}

function markAsFixed(el) {
  el.dataset.linkFixed = '1';
}
<|endoftext|>


<commit_before>'use strict';
import ax from 'axios';
import {keys, zip} from 'lodash';
import fs from 'fs';
import Promise from 'bluebird';

/**
 * backup function
 * Creates the list of firebase collections if it wasn't passed or if the all argument was specified.
 * Creates the destination directory structure.
 * Loops over the collections and call collectionToJSONFile on each one.
 * @param  {[type]} {                         firebase         =             ''            [description]
 * @param  {[type]} secret      =             ''               [description]
 * @param  {[type]} collections =             []               [description]
 * @param  {[type]} all         =             false            [description]
 * @param  {[type]} destination =             `./backups/${new Date(         [description]
 * @return {[type]}             [description]
 */
export default async function backup({
  firebase = '',
  secret = '',
  collections = [],
  all = false,
  destination = `./backups/${new Date().getFullYear()}/${new Date().getMonth() + 1}/${new Date().getDate()}/${new Date().getHours()}`
} = {}) {

  // Populate the collections array if it is not supplied or the all argument is true.
  if(collections.length === 0 && all) {
    const result = await ax.get(`https://${firebase}.firebaseio.com/.json`, {
      params: {
        format: 'export',
        auth: secret,
        shallow: true
      }
    });
    collections = Object.keys(result.data);
  }

  // Create the destination directory if it doesn't exist.
  let dirs = destination.split('/'), currentDir = '';
  while(dirs.length > 0) {
    currentDir += dirs.shift() + '/'
    if (!fs.existsSync(currentDir)) fs.mkdirSync(currentDir);
  }

  // Loop over the collections, creating a file for each one via collectionToJSONFile function.
  // Using a while loop to prevent multiple collectionToCSVFile functions from firing concurrently.
  while (collections.length > 0) {
    const collection = collections.pop();
    const filename = `${destination}/${collection}.csv`;
    await collectionToCSVFile({ firebase, collection, filename, secret });
  }
}

export async function collectionToCSVFile({ firebase, collection, filename, secret } = {}) {

  console.log(`starting: ${collection}`);

  const nextPaths = [collection],
    params = {
      shallow: true,
      auth: secret,
      format: 'export'
    };

  let store = {}, totalPathsStored = 0;

  // Function takes all store contents, convert them to CSV format, and write to file.
  function storeToFile() {
    const paths = Object.keys(store);
    const csvLines = paths.map(path => `"${path}", "${store[path]}"`);
    fs.appendFileSync(filename, csvLines.join('\n'), 'utf8');
    store = {};
  }

  // First line of the CSV
  fs.writeFileSync(filename, `"path", "value"`, 'utf8');

  while(nextPaths.length > 0) {
    console.log(collection, nextPaths.length);
    // Take the first 100 paths to make requests.
    // Create an array of requests and execute them.
    const paths = nextPaths.splice(0, 100);
    const requests = paths.map(path => ax.get(`https://${firebase}.firebaseio.com/${path}.json`, { params }));
    const results = await Promise.all(requests);

    // Iterate over the corresponding paths and requests
    // If the resulting data is an object, concatenate each of those keys onto its
    // corresponding path and push that onto nextPaths. Otherwise, it is a piece
    // of data that should be stored with its path.
    zip(paths, results).forEach(pair => {
      const path = pair[0], data = pair[1].data;
      if (typeof data === 'object') {
        keys(data).forEach(key => nextPaths.push(`${path}/${key}`));
      } else {
        store[path] = data;
        totalPathsStored += 1;
      }
    });

    // When the store contain more than a 1000 entries, empty it into a file.
    if (keys(store).length > 1000) {
      storeToFile();
    }
  }

  // Store the remaining data.
  storeToFile();

  console.log(`complete: ${collection}, stored ${totalPathsStored} paths`);

};
<commit_msg>adding a poor-mans profiler<commit_after>'use strict';
import ax from 'axios';
import {keys, zip} from 'lodash';
import fs from 'fs';
import Promise from 'bluebird';

/**
 * backup function
 * Creates the list of firebase collections if it wasn't passed or if the all argument was specified.
 * Creates the destination directory structure.
 * Loops over the collections and call collectionToJSONFile on each one.
 * @param  {[type]} {                         firebase         =             ''            [description]
 * @param  {[type]} secret      =             ''               [description]
 * @param  {[type]} collections =             []               [description]
 * @param  {[type]} all         =             false            [description]
 * @param  {[type]} destination =             `./backups/${new Date(         [description]
 * @return {[type]}             [description]
 */
export default async function backup({
  firebase = '',
  secret = '',
  collections = [],
  all = false,
  destination = `./backups/${new Date().getFullYear()}/${new Date().getMonth() + 1}/${new Date().getDate()}/${new Date().getHours()}`
} = {}) {

  // Populate the collections array if it is not supplied or the all argument is true.
  if(collections.length === 0 && all) {
    const result = await ax.get(`https://${firebase}.firebaseio.com/.json`, {
      params: {
        format: 'export',
        auth: secret,
        shallow: true
      }
    });
    collections = Object.keys(result.data);
  }

  // Create the destination directory if it doesn't exist.
  let dirs = destination.split('/'), currentDir = '';
  while(dirs.length > 0) {
    currentDir += dirs.shift() + '/'
    if (!fs.existsSync(currentDir)) fs.mkdirSync(currentDir);
  }

  // Loop over the collections, creating a file for each one via collectionToJSONFile function.
  // Using a while loop to prevent multiple collectionToCSVFile functions from firing concurrently.
  while (collections.length > 0) {
    const collection = collections.pop();
    const filename = `${destination}/${collection}.csv`;
    await collectionToCSVFile({ firebase, collection, filename, secret });
  }
}

export async function collectionToCSVFile({ firebase, collection, filename, secret } = {}) {

  console.log(`starting: ${collection}`);

  const nextPaths = [collection],
    params = {
      shallow: true,
      auth: secret,
      format: 'export'
    };

  let store = {}, totalPathsStored = 0, totalRequestTime = 0;

  // Function takes all store contents, convert them to CSV format, and write to file.
  function storeToFile() {
    const paths = Object.keys(store);
    const csvLines = paths.map(path => `"${path}", "${store[path]}"`);
    fs.appendFileSync(filename, csvLines.join('\n'), 'utf8');
    store = {};
  }

  // First line of the CSV
  fs.writeFileSync(filename, `"path", "value"`, 'utf8');

  while(nextPaths.length > 0) {
    console.log(collection, nextPaths.length);
    // Take the first 100 paths to make requests.
    // Create an array of requests and execute them.
    const paths = nextPaths.splice(0, 100);
    const requests = paths.map(path => ax.get(`https://${firebase}.firebaseio.com/${path}.json`, { params }));
    const t = new Date().getTime();
    const results = await Promise.all(requests);
    totalRequestTime += (new Date().getTime() - t);
    console.log(totalRequestTime / 1000);

    // Iterate over the corresponding paths and requests
    // If the resulting data is an object, concatenate each of those keys onto its
    // corresponding path and push that onto nextPaths. Otherwise, it is a piece
    // of data that should be stored with its path.
    zip(paths, results).forEach(pair => {
      const path = pair[0], data = pair[1].data;
      if (typeof data === 'object') {
        keys(data).forEach(key => nextPaths.push(`${path}/${key}`));
      } else {
        store[path] = data;
        totalPathsStored += 1;
      }
    });

    // When the store contain more than a 1000 entries, empty it into a file.
    if (keys(store).length > 1000) {
      storeToFile();
    }
  }

  // Store the remaining data.
  storeToFile();

  console.log(`complete: ${collection}, paths stored: ${totalPathsStored}, total request time: ${totalRequestTime / 1000}`);

};
<|endoftext|>


<commit_before>jQuery.noConflict()
var WDMMG = WDMMG || {}

WDMMG.PopupBox = function () {
  var $ = jQuery, self = this

  this.$elem = $('

')

  // Init
  ;(function () {
    self.$elem.appendTo(document.body)
    // Draggable from title bar
    self.$elem.draggable({ handle: '.head' })
    // Close on click of cross
    self.$elem.delegate('.close', 'click', function () {
      self.hide()
      return false
    })
  })()

  this.setTitle = function (title) {
    self.$elem.find('h3').text(title)
  }

  this.setContent = function (html) {
    self.$elem.find('.content').html(html)
  }

  this.setPos = function (x, y) {
    self.$elem.css({
      left: x,
      top: y
    })
  }

  this.setPosViewportCentre = function () {
    self.setPos( ($(window).width() - self.$elem.width()) / 2
               , ($(window).height() - self.$elem.height()) / 2 + $(window).scrollTop() )
  }

  this.show = function () { self.$elem.show() }
  this.hide = function () { self.$elem.hide() }

  return this
}

// Dashboard
WDMMG.Dashboard = function () {
  var $ = jQuery, self = this

  this.helperFileLocation = '/_dashboard' // Assumed to be an absolute path.

  // Init: set up dashboard.
  this.init = function () {
    if (self.helperFileLocation) {
      self.helperFileLocation = 'http://' + document.domain + self.helperFileLocation
    } else {
      self.helperFileLocation = self.currentLocation()
    }

    self.swf = swfobject.getObjectById("wdmmg")

    self.swf.removeHeader()
    self.swf.removeFooter()
    self.swf.disableUrls()

    self.$menu         = $('#dashboard-menu')
    self.$title        = $('#dashboard-title')
    self.$intro        = $('#dashboard-intro')

    self.$menu.find('a').click(function () {
      self.changeView(this.id)
      return false
    })
  }

  // Choose view from URL params, if available.
  this.ready = function () {
    var urlParams = getViewParameters()
    var viewName = urlParams.view

    delete urlParams.view

    self.changeView(viewName || "uk-bubble-chart", urlParams)
  }

  // Change view and update navigation, title.
  this.changeView = function (viewName, params) {
    self.$menu
      .find('a').removeClass('active').end()
      .find('a#' + viewName).addClass('active')

    self.introText( self.$menu.find('.active')[0].id )

    params = params || {}

    self.swf.changeView(viewName, params)
    self.updateEmbed($.param(params))
  }

  this.currentLocation = function () {
    var loc = window.location.href

    var qs = loc.indexOf("?")
    if (qs !== -1) {
      loc = loc.slice(0, qs)
    }

    var hs = loc.indexOf("#")
    if (hs !== -1) {
      loc = loc.slice(0, hs)
    }

    if (loc.slice(-1) === "/") {
      loc = loc.slice(0,-1)
    }

    return loc
  }

  // Called when visualisation parameters change.
  this.visCallback = function (page, params) {
    params.view = page
    var uid = $.param(params)

    window.location.hash = uid
    self.updateEmbed(uid)

    //reload iframe with comments
    $('#commentframe').attr('src', self.helperFileLocation + "/comments.html?" + uid)
  }

  // Called when "more info" is clicked
  this.infoboxCallback = function(code, classificationName, link) {
    if (!("infobox" in self)) {
      self.infobox = new WDMMG.PopupBox()
      self.infobox.$elem.addClass('wdmmg-infobox')
      self.infobox.setPosViewportCentre()
    }

    self.infobox.setTitle("A title")
    // TODO: get sensible content
    self.infobox.setContent(code + ":" + classificationName + ":" + link + link + link + link + link + link + link + link + link + link + link + link + link + link + link + link)
    self.infobox.show()

    return false
  }

  // Called when help button is clicked
  this.helpCallback = function(id, params) {
    console.log("Help: ", arguments)

    if (!("helpbox" in self)) {
      self.helpbox = new WDMMG.PopupBox()
      self.helpbox.$elem.addClass('wdmmg-helpbox')
      self.helpbox.setPosViewportCentre()
    }

    self.helpbox.setTitle("Help")
    // TODO: get sensible content
    self.helpbox.setContent("You need some help?")
    self.helpbox.show()

    return false
  }

  this.updateEmbed = function (uid) {
    $('#iframecode').val("")
  }

  this.introText = function(id) {
    var text = $($('script[type=text/dashboard-intro]#dashboard-intro-'+id)[0]).text() || ''
    $(self.$intro[0])[0].innerHTML = text
  }

  return this
}

// For temporary backwards compatibility, bring these functions into the
// global namespace
var changeView, wdmmgReady, wdmmgCallback,  wdmmgInfobox, wdmmgHelp

var wdmmgInit = function () {
  var d = new WDMMG.Dashboard()
  d.init()

  changeView         = d.changeView
  wdmmgReady         = d.ready
  wdmmgCallback      = d.visCallback
  wdmmgInfobox       = d.infoboxCallback
  wdmmgHelp          = d.helpCallback
}

var adjustIframeHeight = function() {
  var $cf = jQuery('#commentframe')
  $cf.height(($cf[0].scrollHeight + 10).toString() + "px")
}

// Get URL parameters:
// - works for hash urls and querystrings
function getViewParameters() {
  var location = window.location.href
  var query_string = ''

  if (location.indexOf("?") > -1) {
    query_string = location.substr(location.indexOf("?")).toLowerCase()
  } else if (location.indexOf("#") > -1) {
    query_string = window.location.hash
  }

  var get_params = {}

  if (query_string) {
    query_string = query_string.substr(1)
    var params = query_string.split("&")

    for (var i=0, len=params.length; i<len; ++i){
      var p = params[i].split("=")
      get_params[p[0]] = p[1]
    }
  }

  return get_params
}

<commit_msg>Make boxes resizable<commit_after>jQuery.noConflict()
var WDMMG = WDMMG || {}

WDMMG.PopupBox = function () {
  var $ = jQuery, self = this

  this.$elem = $('

')

  // Init
  ;(function () {
    self.$elem.appendTo(document.body)
    // Draggable from title bar
    self.$elem.draggable({ handle: '.head' })
    // Resizable
    self.$elem.resizable()
    // Close on click of cross
    self.$elem.delegate('.close', 'click', function () {
      self.hide()
      return false
    })
  })()

  this.setTitle = function (title) {
    self.$elem.find('h3').text(title)
  }

  this.setContent = function (html) {
    self.$elem.find('.content').html(html)
  }

  this.setPos = function (x, y) {
    self.$elem.css({
      left: x,
      top: y
    })
  }

  this.setPosViewportCentre = function () {
    self.setPos( ($(window).width() - self.$elem.width()) / 2
               , ($(window).height() - self.$elem.height()) / 2 + $(window).scrollTop() )
  }

  this.show = function () { self.$elem.show() }
  this.hide = function () { self.$elem.hide() }

  return this
}

// Dashboard
WDMMG.Dashboard = function () {
  var $ = jQuery, self = this

  this.helperFileLocation = '/_dashboard' // Assumed to be an absolute path.

  // Init: set up dashboard.
  this.init = function () {
    if (self.helperFileLocation) {
      self.helperFileLocation = 'http://' + document.domain + self.helperFileLocation
    } else {
      self.helperFileLocation = self.currentLocation()
    }

    self.swf = swfobject.getObjectById("wdmmg")

    self.swf.removeHeader()
    self.swf.removeFooter()
    self.swf.disableUrls()

    self.$menu         = $('#dashboard-menu')
    self.$title        = $('#dashboard-title')
    self.$intro        = $('#dashboard-intro')

    self.$menu.find('a').click(function () {
      self.changeView(this.id)
      return false
    })
  }

  // Choose view from URL params, if available.
  this.ready = function () {
    var urlParams = getViewParameters()
    var viewName = urlParams.view

    delete urlParams.view

    self.changeView(viewName || "uk-bubble-chart", urlParams)
  }

  // Change view and update navigation, title.
  this.changeView = function (viewName, params) {
    self.$menu
      .find('a').removeClass('active').end()
      .find('a#' + viewName).addClass('active')

    self.introText( self.$menu.find('.active')[0].id )

    params = params || {}

    self.swf.changeView(viewName, params)
    self.updateEmbed($.param(params))
  }

  this.currentLocation = function () {
    var loc = window.location.href

    var qs = loc.indexOf("?")
    if (qs !== -1) {
      loc = loc.slice(0, qs)
    }

    var hs = loc.indexOf("#")
    if (hs !== -1) {
      loc = loc.slice(0, hs)
    }

    if (loc.slice(-1) === "/") {
      loc = loc.slice(0,-1)
    }

    return loc
  }

  // Called when visualisation parameters change.
  this.visCallback = function (page, params) {
    params.view = page
    var uid = $.param(params)

    window.location.hash = uid
    self.updateEmbed(uid)

    //reload iframe with comments
    $('#commentframe').attr('src', self.helperFileLocation + "/comments.html?" + uid)
  }

  // Called when "more info" is clicked
  this.infoboxCallback = function(code, classificationName, link) {
    if (!("infobox" in self)) {
      self.infobox = new WDMMG.PopupBox()
      self.infobox.$elem.addClass('wdmmg-infobox')
      self.infobox.setPosViewportCentre()
    }

    self.infobox.setTitle("A title")
    // TODO: get sensible content
    self.infobox.setContent(code + ":" + classificationName + ":" + link + link + link + link + link + link + link + link + link + link + link + link + link + link + link + link)
    self.infobox.show()

    return false
  }

  // Called when help button is clicked
  this.helpCallback = function(id, params) {
    console.log("Help: ", arguments)

    if (!("helpbox" in self)) {
      self.helpbox = new WDMMG.PopupBox()
      self.helpbox.$elem.addClass('wdmmg-helpbox')
      self.helpbox.setPosViewportCentre()
    }

    self.helpbox.setTitle("Help")
    // TODO: get sensible content
    self.helpbox.setContent("You need some help?")
    self.helpbox.show()

    return false
  }

  this.updateEmbed = function (uid) {
    $('#iframecode').val("")
  }

  this.introText = function(id) {
    var text = $($('script[type=text/dashboard-intro]#dashboard-intro-'+id)[0]).text() || ''
    $(self.$intro[0])[0].innerHTML = text
  }

  return this
}

// For temporary backwards compatibility, bring these functions into the
// global namespace
var changeView, wdmmgReady, wdmmgCallback,  wdmmgInfobox, wdmmgHelp

var wdmmgInit = function () {
  var d = new WDMMG.Dashboard()
  d.init()

  changeView         = d.changeView
  wdmmgReady         = d.ready
  wdmmgCallback      = d.visCallback
  wdmmgInfobox       = d.infoboxCallback
  wdmmgHelp          = d.helpCallback
}

var adjustIframeHeight = function() {
  var $cf = jQuery('#commentframe')
  $cf.height(($cf[0].scrollHeight + 10).toString() + "px")
}

// Get URL parameters:
// - works for hash urls and querystrings
function getViewParameters() {
  var location = window.location.href
  var query_string = ''

  if (location.indexOf("?") > -1) {
    query_string = location.substr(location.indexOf("?")).toLowerCase()
  } else if (location.indexOf("#") > -1) {
    query_string = window.location.hash
  }

  var get_params = {}

  if (query_string) {
    query_string = query_string.substr(1)
    var params = query_string.split("&")

    for (var i=0, len=params.length; i<len; ++i){
      var p = params[i].split("=")
      get_params[p[0]] = p[1]
    }
  }

  return get_params
}

<|endoftext|>


<commit_before>// --
// Core.UI.Autocomplete.js - Autocomplete functions
// Copyright (C) 2001-2012 OTRS AG, http://otrs.org/
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var Core = Core || {};
Core.UI = Core.UI || {};

/**
 * @namespace
 * @exports TargetNS as Core.UI.Autocomplete
 * @description
 *      This namespace contains autocomplete specific functions.
 */
Core.UI.Autocomplete = (function (TargetNS) {
    var ConfigFallback = {
            AutoCompleteActive: 1,
            MinQueryLength: 2,
            QueryDelay: 100,
            MaxResultsDisplayed: 20
        },
        ConfigElements = ['AutoCompleteActive', 'MinQueryLength', 'QueryDelay', 'MaxResultsDisplayed', 'ButtonText'],
        Config = {};

    function InitConfig(Type, Options) {
        var TypeConfig = Core.Config.Get('Autocomplete');

        $.each(ConfigElements, function (ConfigKey, ConfigElement) {
            // Option, Type, Fallback
            if (Options && typeof Options[ConfigElement] !== 'undefined') {
                Config[ConfigElement] = Options[ConfigElement];
            }
            else if (Type && TypeConfig[Type]) {
                Config[ConfigElement] = TypeConfig[Type][ConfigElement];
            }
            else {
                Config[ConfigElement] = ConfigFallback[ConfigElement];
            }
        });

        // if button should be shown, set minlength to an unreachable value
        if (!Config.AutoCompleteActive) {
            Config.MinQueryLength = 500;
        }

        return Config;
    }

    TargetNS.GetConfig = function(Key) {
        return Config[Key];
    };

    /**
     * @function
     * @description
     *      This function initializes autocomplete on an input element.
     * @param {jQueryObject} $Element which gets autocomplete infos.
     * @param {Function} SourceFunction defines the source data for the autocomplete
     *              Two params: Request and Response
     *                      Request: the data provided by the autocomplete (e.g. the entered text)
     *                      Response: the function defined by the autocomplete to call with the selected data
     * @param {Function} SelectFunction is executed, if an entry is selected
     *              Two params: Event and UI
     *                      Event: the original browser event object
     *                      UI: the data given from the UI (e.g. UI.item is the selected autocomplete list item)
     * @param {String} Type of autocompletion, e.g. "CustomerSearch" etc.
     * @param {Object} Options object data with autocomplete plugin options
     * @return nothing
     */
    TargetNS.Init = function ($Element, SourceFunction, SelectFunction, Type, Options) {
        var Config;

        // Only start autocompletion, if $Element is valid element
        if (!isJQueryObject($Element) || !$Element.length) {
            return;
        }

        Config = InitConfig(Type, Options);

        $Element.autocomplete({
            minLength: Config.MinQueryLength,
            delay: Config.QueryDelay,
            open: function() {
                // force a higher z-index than the overlay/dialog
                $Element.autocomplete('widget').addClass('ui-overlay-autocomplete');
                return false;
            },
            source: function (Request, Response) {
                // if an old ajax request is already running, stop the old request and start the new one
                if ($Element.data('AutoCompleteXHR')) {
                    $Element.data('AutoCompleteXHR').abort();
                    $Element.removeData('AutoCompleteXHR');
                    // run the response function to hide the request animation
                    Response({});
                }

                if (SourceFunction) {
                    SourceFunction(Request, Response);
                }

                /*
                 * Your SourceFunction must return an array of the data you collected.
                 * Every array element is an object with the following keys:
                 * label: the string displayed in the suggestion menu
                 * value: the string to be inserted in the input field
                 * You can add more keys, if you want to transport data to the select function
                 * all keys will be available there via the UI.item object.
                 *
                 * If you use an ajax function inside your source, make sure to
                 * save the XHRObject in a data-value, to control the xhr automatically:
                 * $Element.data('AutoCompleteXHR', YourCallHere());
                 * This makes sure, that we can abort old ajax request, if new searches are triggered.
                 *
                 * The input value can be found in Request.term. Don't forget to send the MaxResult config
                 * to the server in your ajax request.
                 */
            },
            select: function (Event, UI) {
                if (SelectFunction) {
                    SelectFunction(Event, UI);
                }

                Event.preventDefault();
                return false;
            }
        });

        if (!Config.AutoCompleteActive) {
            $Element.after('<button id="' + Core.App.EscapeSelector($Element.attr('id')) + 'Search" type="button">' + Config.ButtonText + '</button>');
            $('#' + Core.App.EscapeSelector($Element.attr('id')) + 'Search').click(function () {
                $Element.autocomplete("option", "minLength", 0);
                $Element.autocomplete("search");
                $Element.autocomplete("option", "minLength", 500);
            });
        }
    };

    return TargetNS;
}(Core.UI.Autocomplete || {}));
<commit_msg>Fixed autocompletion config setting.<commit_after>// --
// Core.UI.Autocomplete.js - Autocomplete functions
// Copyright (C) 2001-2012 OTRS AG, http://otrs.org/
// --
// This software comes with ABSOLUTELY NO WARRANTY. For details, see
// the enclosed file COPYING for license information (AGPL). If you
// did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
// --

"use strict";

var Core = Core || {};
Core.UI = Core.UI || {};

/**
 * @namespace
 * @exports TargetNS as Core.UI.Autocomplete
 * @description
 *      This namespace contains autocomplete specific functions.
 */
Core.UI.Autocomplete = (function (TargetNS) {
    var ConfigFallback = {
            AutoCompleteActive: 1,
            MinQueryLength: 2,
            QueryDelay: 100,
            MaxResultsDisplayed: 20
        },
        ConfigElements = ['AutoCompleteActive', 'MinQueryLength', 'QueryDelay', 'MaxResultsDisplayed', 'ButtonText'],
        Config = {};

    function InitConfig(Type, Options) {
        var TypeConfig = Core.Config.Get('Autocomplete');

        $.each(ConfigElements, function (ConfigKey, ConfigElement) {
            // Option, Type, Fallback
            if (Options && typeof Options[ConfigElement] !== 'undefined') {
                Config[ConfigElement] = Options[ConfigElement];
            }
            else if (Type && TypeConfig[Type]) {
                Config[ConfigElement] = TypeConfig[Type][ConfigElement];
            }
            else {
                Config[ConfigElement] = ConfigFallback[ConfigElement];
            }
        });

        // make sure, that the value auf AutoCompleteActive is casted to int
        Config.AutoCompleteActive = parseInt(Config.AutoCompleteActive, 10);

        // if button should be shown, set minlength to an unreachable value
        if (!Config.AutoCompleteActive) {
            Config.MinQueryLength = 500;
        }

        return Config;
    }

    TargetNS.GetConfig = function(Key) {
        return Config[Key];
    };

    /**
     * @function
     * @description
     *      This function initializes autocomplete on an input element.
     * @param {jQueryObject} $Element which gets autocomplete infos.
     * @param {Function} SourceFunction defines the source data for the autocomplete
     *              Two params: Request and Response
     *                      Request: the data provided by the autocomplete (e.g. the entered text)
     *                      Response: the function defined by the autocomplete to call with the selected data
     * @param {Function} SelectFunction is executed, if an entry is selected
     *              Two params: Event and UI
     *                      Event: the original browser event object
     *                      UI: the data given from the UI (e.g. UI.item is the selected autocomplete list item)
     * @param {String} Type of autocompletion, e.g. "CustomerSearch" etc.
     * @param {Object} Options object data with autocomplete plugin options
     * @return nothing
     */
    TargetNS.Init = function ($Element, SourceFunction, SelectFunction, Type, Options) {
        var Config;

        // Only start autocompletion, if $Element is valid element
        if (!isJQueryObject($Element) || !$Element.length) {
            return;
        }

        Config = InitConfig(Type, Options);

        $Element.autocomplete({
            minLength: Config.MinQueryLength,
            delay: Config.QueryDelay,
            open: function() {
                // force a higher z-index than the overlay/dialog
                $Element.autocomplete('widget').addClass('ui-overlay-autocomplete');
                return false;
            },
            source: function (Request, Response) {
                // if an old ajax request is already running, stop the old request and start the new one
                if ($Element.data('AutoCompleteXHR')) {
                    $Element.data('AutoCompleteXHR').abort();
                    $Element.removeData('AutoCompleteXHR');
                    // run the response function to hide the request animation
                    Response({});
                }

                if (SourceFunction) {
                    SourceFunction(Request, Response);
                }

                /*
                 * Your SourceFunction must return an array of the data you collected.
                 * Every array element is an object with the following keys:
                 * label: the string displayed in the suggestion menu
                 * value: the string to be inserted in the input field
                 * You can add more keys, if you want to transport data to the select function
                 * all keys will be available there via the UI.item object.
                 *
                 * If you use an ajax function inside your source, make sure to
                 * save the XHRObject in a data-value, to control the xhr automatically:
                 * $Element.data('AutoCompleteXHR', YourCallHere());
                 * This makes sure, that we can abort old ajax request, if new searches are triggered.
                 *
                 * The input value can be found in Request.term. Don't forget to send the MaxResult config
                 * to the server in your ajax request.
                 */
            },
            select: function (Event, UI) {
                if (SelectFunction) {
                    SelectFunction(Event, UI);
                }

                Event.preventDefault();
                return false;
            }
        });

        if (!Config.AutoCompleteActive) {
            $Element.after('<button id="' + Core.App.EscapeSelector($Element.attr('id')) + 'Search" type="button">' + Config.ButtonText + '</button>');
            $('#' + Core.App.EscapeSelector($Element.attr('id')) + 'Search').click(function () {
                $Element.autocomplete("option", "minLength", 0);
                $Element.autocomplete("search");
                $Element.autocomplete("option", "minLength", 500);
            });
        }
    };

    return TargetNS;
}(Core.UI.Autocomplete || {}));
<|endoftext|>


<commit_before>/* -- Modules -- */
// Essential
var eventEmmiter = require('events').EventEmitter;
var util         = require("util");

// Core
var outbound = require('./outbound');

// Foundations
var schedule = require('node-schedule');
var colors = require('colors'); // Better looking error handling
var moment = require('moment');
var _ = require('lodash');
/* -- ------- -- */

colors.setTheme({
	silly: 'rainbow',
	input: 'grey',
	verbose: 'cyan',
	prompt: 'grey',
	success: 'green',
	data: 'grey',
	info: 'cyan',
	warn: 'yellow',
	debug: 'grey',
	bgWhite: 'bgWhite',
	bold: 'bold',
	error: 'red'
});

/* Initiate outbound queue. */
var Queue = function(port, callback){
	console.log("Queue created".success);
	eventEmmiter.call(this);
}

util.inherits(Queue, eventEmmiter);

var queueStart = function (databaseConnection) {
	console.log("Queue started".success);

	var maxConcurrent = 10;
	var outbox = databaseConnection.collections.queue;

	databaseConnection.collections.queue.count({state:'transit'}).exec(function (err, count){
		if(error) return console.log(error.error);
		console.log(count.info);
	  // Bit of a callback hell here
	  if(count <= maxConcurrent){
		  outbox.find().where({ or: [{ status: 'pending' }, { status: 'denied' }] }).limit(10).exec(function(err, models){
			  _.forEach(models, function(mail) {
					databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'transit' }).exec(function(error, mail) {
						if(error) console.log(error.error);

						var OUTBOUND = new outbound();
						OUTBOUND.createTransporter(transporter, function(error, transporter){
							if(error) console.log(error.error);

							OUTBOUND.send(mail, function(error, response){
								if(error){
									databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'denied' }).exec(function(error, mail) {
										if(!error) console.log("Message ".error + mail.subject + " denied".error);
									});
								}else{
									databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'sent' }).exec(function(error, mail) {
										if(!error) console.log("Message ".success + mail.subject + " sent".success);
									});
								}
							})
						});
					});
			  });
		  });
	  }
	});
}

var queueAdd = function (databaseConnection, mail, options, callback) {
	var _this = this;

	// Humane programming
	if((options.constructor !== Object)&&(!callback)) callback = options;

	databaseConnection.collections.queue.create({
		sender: mail.from,
		to: mail.to,
		schedule: { attempted: moment() , scheduled: moment() },
		attemtps: 0,
		subject: mail.subject,
		text: mail.text,
		html: mail.html,

		// STRING ENUM: ['pending', 'transit', 'sent', 'denied']
		state: 'pending'
	}, function(error, model){
		if(error) console.log(error.error);

		console.log(colors.info(model));
		// Start queue
		queueStart(databaseConnection);

		_this.emit('queued', error, model, databaseConnection);
		if(callback) callback(error, model);
	});
};

Queue.prototype.start = queueStart;
Queue.prototype.add = queueAdd;

module.exports = Queue;
<commit_msg>Fix #4<commit_after>/* -- Modules -- */
// Essential
var eventEmmiter = require('events').EventEmitter;
var util         = require("util");

// Core
var outbound = require('./outbound');

// Foundations
var schedule = require('node-schedule');
var colors = require('colors'); // Better looking error handling
var moment = require('moment');
var _ = require('lodash');
/* -- ------- -- */

colors.setTheme({
	silly: 'rainbow',
	input: 'grey',
	verbose: 'cyan',
	prompt: 'grey',
	success: 'green',
	data: 'grey',
	info: 'cyan',
	warn: 'yellow',
	debug: 'grey',
	bgWhite: 'bgWhite',
	bold: 'bold',
	error: 'red'
});

/* Initiate outbound queue. */
var Queue = function(port, callback){
	console.log("Queue created".success);
	eventEmmiter.call(this);
}

util.inherits(Queue, eventEmmiter);

var queueStart = function (databaseConnection) {
	console.log("Queue started".success);

	var maxConcurrent = 10;
	var outbox = databaseConnection.collections.queue;

	databaseConnection.collections.queue.count({state:'transit'}).exec(function (err, count){
		if(error) return console.log(colors.error(error));
		console.log(colors.info(count));
	  // Bit of a callback hell here
	  if(count <= maxConcurrent){
		  outbox.find().where({ or: [{ status: 'pending' }, { status: 'denied' }] }).limit(10).exec(function(err, models){
			  _.forEach(models, function(mail) {
					databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'transit' }).exec(function(error, mail) {
						if(error) console.log(error.error);

						var OUTBOUND = new outbound();
						OUTBOUND.createTransporter(transporter, function(error, transporter){
							if(error) console.log(error.error);

							OUTBOUND.send(mail, function(error, response){
								if(error){
									databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'denied' }).exec(function(error, mail) {
										if(!error) console.log("Message ".error + mail.subject + " denied".error);
									});
								}else{
									databaseConnection.collections.outbox.update({ eID: mail.eID }, { state: 'sent' }).exec(function(error, mail) {
										if(!error) console.log("Message ".success + mail.subject + " sent".success);
									});
								}
							})
						});
					});
			  });
		  });
	  }
	});
}

var queueAdd = function (databaseConnection, mail, options, callback) {
	var _this = this;

	// Humane programming
	if((options.constructor !== Object)&&(!callback)) callback = options;

	databaseConnection.collections.queue.create({
		sender: mail.from,
		to: mail.to,
		schedule: { attempted: moment() , scheduled: moment() },
		attemtps: 0,
		subject: mail.subject,
		text: mail.text,
		html: mail.html,

		// STRING ENUM: ['pending', 'transit', 'sent', 'denied']
		state: 'pending'
	}, function(error, model){
		if(error) console.log(error.error);

		console.log(colors.info(model));
		// Start queue
		queueStart(databaseConnection);

		_this.emit('queued', error, model, databaseConnection);
		if(callback) callback(error, model);
	});
};

Queue.prototype.start = queueStart;
Queue.prototype.add = queueAdd;

module.exports = Queue;
<|endoftext|>


<commit_before>var huepi = require('../huepi');

var MyHue = new huepi();
var HeartbeatInterval;

ConnectMyHue();

function consoleTlog(string) {
  console.log(new Date() + ': ' + string);
}

function ConnectMyHue() {
  consoleTlog('Discovering hue Bridge via hue Portal');
  MyHue.PortalDiscoverLocalBridges().then(function BridgesDiscovered() {
    consoleTlog('Bridge IP: ' + MyHue.BridgeIP);
    MyHue.BridgeGetConfig().then(function BridgeConfigReceived() {
      consoleTlog('Bridge ID: ' + MyHue.BridgeID);
      consoleTlog('Bridge Name: ' + MyHue.BridgeName);
      MyHue.BridgeGetData().then(function BridgeDataReceived() {
        consoleTlog('Bridge Username: ' + MyHue.Username);
        StartHeartbeat();
      }, function UnableToRetreiveBridgeData() {
        consoleTlog('Please press connect button on the Bridge');
        MyHue.BridgeCreateUser().then(function BridegeUserCreated() {
          consoleTlog('Bridge Username Created: ' + MyHue.Username);
          StartHeartbeat();
        }, function UnableToCreateUseronBridge() {
          consoleTlog('.Please press connect button on the Bridge.');
          setTimeout(ConnectMyHue, 1000);
        });
      });
    }, function UnableToRetreiveBridgeConfiguration() {
      consoleTlog('Unable to Retreive Bridge Configuration');
      setTimeout(ConnectMyHue, 1000);
    });
  }, function UnableToDiscoverLocalBridgesViaPortal() {
    consoleTlog('Unable to find Local Bridge via hue Portal');
    setTimeout(ConnectMyHue, 3000);
  });
}

function StartHeartbeat() {
  MyHue.GroupAlertSelect(0);
  HeartbeatInterval = setInterval(StatusHeartbeat, 2500);
}

function StatusHeartbeat() {
  var PrevHueLights = MyHue.Lights; // Store Previous State of Lights
  MyHue.LightsGetData().then(function CheckLightSwitches() {
    // Triggers on Reachable which actually means Powered On/Off in my case ;-)
    LightNr = 1;
    while (MyHue.Lights[MyHue.LightGetId(LightNr)] !== undefined) {
      if ((MyHue.Lights[MyHue.LightGetId(LightNr)].state.reachable) !== (PrevHueLights[MyHue.LightGetId(LightNr)].state.reachable)) {
        if (MyHue.Lights[MyHue.LightGetId(LightNr)].state.reachable) {
          onLightSwitchOn(MyHue.LightGetId(LightNr));
        } else {
          onLightSwitchOff(MyHue.LightGetId(LightNr));
        }
      }
      LightNr++;
    }
  }, function BridgetHeartbeatGetFailed() {
    consoleTlog('StatusHeartbeat BridgeGet Failed');
    clearInterval(HeartbeatInterval);
    ConnectMyHue();
  });
}

function onLightSwitchOn(LightNr) {
  consoleTlog('LightSwitch ' +LightNr+ ' On  - ' +MyHue.Lights[MyHue.LightGetId(LightNr)].name);
  MyHue.GroupOn(0);
  MyHue.GroupSetCT(0, 467);
  MyHue.GroupSetBrightness(0, 144);
}

function onLightSwitchOff(LightNr) {
  consoleTlog('LightSwitch ' +LightNr+ ' Off - ' +MyHue.Lights[MyHue.LightGetId(LightNr)].name);
  MyHue.GroupOff(0);
}

<commit_msg>Act on Group 1<commit_after>var huepi = require('../huepi');

var MyHue = new huepi();
var HeartbeatInterval;

ConnectMyHue();

function consoleTlog(string) {
  console.log(new Date() + ': ' + string);
}

function ConnectMyHue() {
  consoleTlog('Discovering hue Bridge via hue Portal');
  MyHue.PortalDiscoverLocalBridges().then(function BridgesDiscovered() {
    consoleTlog('Bridge IP: ' + MyHue.BridgeIP);
    MyHue.BridgeGetConfig().then(function BridgeConfigReceived() {
      consoleTlog('Bridge ID: ' + MyHue.BridgeID);
      consoleTlog('Bridge Name: ' + MyHue.BridgeName);
      MyHue.BridgeGetData().then(function BridgeDataReceived() {
        consoleTlog('Bridge Username: ' + MyHue.Username);
        StartHeartbeat();
      }, function UnableToRetreiveBridgeData() {
        consoleTlog('Please press connect button on the Bridge');
        MyHue.BridgeCreateUser().then(function BridegeUserCreated() {
          consoleTlog('Bridge Username Created: ' + MyHue.Username);
          StartHeartbeat();
        }, function UnableToCreateUseronBridge() {
          consoleTlog('.Please press connect button on the Bridge.');
          setTimeout(ConnectMyHue, 1000);
        });
      });
    }, function UnableToRetreiveBridgeConfiguration() {
      consoleTlog('Unable to Retreive Bridge Configuration');
      setTimeout(ConnectMyHue, 1000);
    });
  }, function UnableToDiscoverLocalBridgesViaPortal() {
    consoleTlog('Unable to find Local Bridge via hue Portal');
    setTimeout(ConnectMyHue, 3000);
  });
}

function StartHeartbeat() {
  MyHue.GroupAlertSelect(1);
  HeartbeatInterval = setInterval(StatusHeartbeat, 2500);
}

function StatusHeartbeat() {
  var PrevHueLights = MyHue.Lights; // Store Previous State of Lights
  MyHue.LightsGetData().then(function CheckLightSwitches() {
    // Triggers on Reachable which actually means Powered On/Off in my case ;-)
    LightNr = 1;
    while (MyHue.Lights[MyHue.LightGetId(LightNr)] !== undefined) {
      if ((MyHue.Lights[MyHue.LightGetId(LightNr)].state.reachable) !== (PrevHueLights[MyHue.LightGetId(LightNr)].state.reachable)) {
        if (MyHue.Lights[MyHue.LightGetId(LightNr)].state.reachable) {
          onLightSwitchOn(MyHue.LightGetId(LightNr));
        } else {
          onLightSwitchOff(MyHue.LightGetId(LightNr));
        }
      }
      LightNr++;
    }
  }, function BridgetHeartbeatGetFailed() {
    consoleTlog('StatusHeartbeat BridgeGet Failed');
    clearInterval(HeartbeatInterval);
    ConnectMyHue();
  });
}

function onLightSwitchOn(LightNr) {
  consoleTlog('LightSwitch ' +LightNr+ ' On  - ' +MyHue.Lights[MyHue.LightGetId(LightNr)].name);
  MyHue.GroupOn(1);
  MyHue.GroupSetCT(1, 467);
  MyHue.GroupSetBrightness(1, 144);
}

function onLightSwitchOff(LightNr) {
  consoleTlog('LightSwitch ' +LightNr+ ' Off - ' +MyHue.Lights[MyHue.LightGetId(LightNr)].name);
  MyHue.GroupOff(1);
}

<|endoftext|>


<commit_before>var HeaderView = function(title) {
  var Palette = require('ui/common/components/Palette');
  var SeparatorView = require('ui/common/components/SeparatorView');

  var coloredPadding = new SeparatorView(Palette.PRIMARY_COLOR, '10dip');

  var osname = Ti.Platform.osname, version = Ti.Platform.version, height = Ti.Platform.displayCaps.platformHeight, width = Ti.Platform.displayCaps.platformWidth;
  var isTablet = osname === 'ipad' || (osname === 'android' && (width > 899 || height > 899));

  var self = Ti.UI.createView({
    backgroundColor : Palette.PRIMARY_COLOR,
    height : '60dip',
    layout : 'vertical',
    bottom : '10dip',
    top : '0dip'
  });

  var appNameContainer = Ti.UI.createView({
    top : '10dip',
    height : Ti.UI.SIZE,
  });

  var loggedIn = require('helpers/LoginHelper').loggedIn;

  var logo = Ti.UI.createImageView({
    image : '/images/logo.png',
    left : '5dip',
    width : '50dip',
    height : '50dip',
    bottom : '5dip',
    height : Ti.UI.FILL
  });

  var appName = Ti.UI.createLabel({
    color : Palette.SECONDARY_COLOR_LIGHT,
    font : {
      fontSize : '25dip',
      fontWeight : 'bold'
    },
    left : '60dip',
    text : title,
    height : Ti.UI.SIZE
  });

  var usernameView = Ti.UI.createLabel({
    color : Palette.GRAY_LIGHT,
    right : '10dip',
    text : "",
    font : {
      fontSize : '15dip',
    },
    height : Ti.UI.SIZE
  });
  if (isTablet) {
    appNameContainer.add(usernameView);
  }

  appNameContainer.add(logo);
  appNameContainer.add(appName);
  self.add(appNameContainer);

  self.updateUserName = function() {
    Ti.API.info("HGASD");
    Ti.API.info(loggedIn());

    //var loggedIn = require('helpers/LoginHelper').loggedIn;
    if (loggedIn()) {
      usernameView.text = "Hi, " + Ti.App.Properties.getString('username') + "!";
    } else {
      usernameView.text = "";
    }
  };

  return self;
};

module.exports = HeaderView;
<commit_msg>Show a 'Not Logged In' message on a tablet<commit_after>var HeaderView = function(title) {
  var Palette = require('ui/common/components/Palette');
  var SeparatorView = require('ui/common/components/SeparatorView');

  var coloredPadding = new SeparatorView(Palette.PRIMARY_COLOR, '10dip');

  var osname = Ti.Platform.osname, version = Ti.Platform.version, height = Ti.Platform.displayCaps.platformHeight, width = Ti.Platform.displayCaps.platformWidth;
  var isTablet = osname === 'ipad' || (osname === 'android' && (width > 899 || height > 899));

  var self = Ti.UI.createView({
    backgroundColor : Palette.PRIMARY_COLOR,
    height : '60dip',
    layout : 'vertical',
    bottom : '10dip',
    top : '0dip'
  });

  var appNameContainer = Ti.UI.createView({
    top : '10dip',
    height : Ti.UI.SIZE,
  });

  var loggedIn = require('helpers/LoginHelper').loggedIn;

  var logo = Ti.UI.createImageView({
    image : '/images/logo.png',
    left : '5dip',
    width : '50dip',
    height : '50dip',
    bottom : '5dip',
    height : Ti.UI.FILL
  });

  var appName = Ti.UI.createLabel({
    color : Palette.SECONDARY_COLOR_LIGHT,
    font : {
      fontSize : '25dip',
      fontWeight : 'bold'
    },
    left : '60dip',
    text : title,
    height : Ti.UI.SIZE
  });

  var usernameView = Ti.UI.createLabel({
    color : Palette.GRAY_LIGHT,
    right : '10dip',
    text : "",
    font : {
      fontSize : '15dip',
    },
    height : Ti.UI.SIZE
  });
  if (isTablet) {
    appNameContainer.add(usernameView);
  }

  appNameContainer.add(logo);
  appNameContainer.add(appName);
  self.add(appNameContainer);

  self.updateUserName = function() {
    //var loggedIn = require('helpers/LoginHelper').loggedIn;
    if (loggedIn()) {
      usernameView.text = "Logged in as " + Ti.App.Properties.getString('username');
    } else {
      usernameView.text = "Not logged in";
    }
  };

  return self;
};

module.exports = HeaderView;
<|endoftext|>


<commit_before>//DOM classes for the main context, front and back
var MapptViewer_Class = "mappt-viewer-main";
var MapptViewer_Class_Front = MapptViewer_Class + "-front";
var MapptViewer_Class_Back = MapptViewer_Class + "-back";

MapptViewer = function(context_id, context_width, context_height) {

    //The id of the DIV element for our top-level container
    this.context_id = context_id;
    //The width of our Raphael paper
    this.context_width = context_width;
    //The height of our Raphael paper
    this.context_height = context_height;

    this.context_id_background = this.context_id + "-back";
    this.context_id_foreground = this.context_id + "-front";

    //The SVG width and height values
    this.context_svg_width = null;
    this.context_svg_height = null;

    //our current context
    this.contextObj = $("#" + context_id);

    //check if the given ID exists
    (this.contextObj.length) ||
	log("ERROR: Provided ID does not match any element within the DOM");

    $(this.contextObj).css({
	"position" : "relative",
	"top" : "0px",
	"left" : "0px",
	"width" : this.context_width,
	"height" : this.context_height,
	"overflow" : "hidden",
    });

    //apply a special class to our container
    $(this.contextObj).addClass(MapptViewer_Class);

    //create our background div and foreground divs
    //the background div will hold our SVG image
    this.contextObj_back = document.createElement("div");
    this.contextObj_back.setAttribute("id", this.context_id_background);
    $(this.contextObj_back).addClass(MapptViewer_Class_Back);
    $(this.contextObj_back).css({
	"position" : "absolute",
	"z-index" : 0,
	"width" : "100%",
	"height" : "100%",
	"overflow" : "hidden"
    });
    $(this.contextObj).append(this.contextObj_back);

    //create our foreground div
    //the foreground div will hold our raphael paper
    this.contextObj_front = document.createElement("div");
    this.contextObj_front.setAttribute("id", this.context_id_foreground);
    $(this.contextObj_front).addClass(MapptViewer_Class_Front);
    $(this.contextObj_front).css({
	"position" : "absolute",
	"z-index" : 1,
	"width" : "100%",
	"height" : "100%",
	"overflow" : "hidden",
    });
    $(this.contextObj).append(this.contextObj_front);

    //contains the image URL full path
    this.imageURL = null;
    //contains the name of the image
    this.imageName = null;

    //get the offset of our context in order to draw on the paper correctly
    this.contextOffset = this.contextObj.offset();

    //contains the paper for our Raphael
    this.context_paper = null;
    //contains the image for our paper
    this.context_image = null;

    //contains the svg for our paper
    this.context_svg = null;

    this.currentView = {
	x : 0.0,
	y : 0.0,
	w : this.context_width,
	h : this.context_height
    };

    //contains a reference to the current path on the paper
    this.currentPath = [];

}

MapptViewer.prototype.init = function() {
    var mapptViewer = this;

    this.currentView = {
    };
    return this;
};

MapptViewer.prototype.setMap = function(imageName) {
    this.imageName = imageName;
    this.imageURL = Mappt_Layout_Folder + this.imageName;

    $(this.contextObj_back).empty();

    jQuery.ajax({
	type: 'GET',
	url: this.imageURL, //this is the svg file loaded as xml
	dataType: 'xml',
	cache: false,
	success: function(svgXML) {
	    this.context_svg = svgXML.getElementsByTagName('svg')[0];

	    //regex to find a number in a string
	    var r = /\d+/;

	    //removing the aspect ratio preservation.
	    this.context_svg.setAttribute("preserveAspectRatio", "none");

	    //the svg width and height
	    this.context_svg_width = this.context_svg.getAttribute('width').match(r)[0];
	    this.context_svg_height = this.context_svg.getAttribute('height').match(r)[0];

	    //the original svg width and height
	    this.svg_original_width = this.context_svg_width;
	    this.svg_original_height = this.context_svg_height;

	    var newWidth, newHeight;
	    //make the width and height of our SVG change to fit well within our context
	    if (this.context_width / this.context_height >  //context wider than svg
		this.context_svg_width / this.context_svg_height) {
		newWidth = this.context_width;
		newHeight = this.context_svg_height / this.context_svg_width * newWidth;
	    }
	    else {
		newHeight = this.context_height;
		newWidth = this.context_svg_width / this.context_svg_height * newHeight;
	    }

	    this.context_svg_width = newWidth;
	    this.context_svg_height = newHeight;
	    this.context_svg.setAttribute("width", newWidth);
	    this.context_svg.setAttribute("height", newHeight);

	    //to simulate panning and zooming of our SVG, a DIV is
	    //resized and translated behind the main paper context.
	    //change our background context to reflect the changes.
	    $(this.contextObj_back).css({
		"width" : this.context_width,
		"height" : this.context_height,
		//"width" : newWidth,
		//"height" : newHeight,
		"top" : 0,
		"left" : 0,
		"overflow" : "hidden",
	    });

	    //setup our paper in the front div container
	    this.context_paper = Raphael(this.context_id_foreground,
					 this.context_svg_width,
					 this.context_svg_height);

	    //setup our svg map in the back div container
	    $(this.contextObj_back).append(this.context_svg);

	    //add a rectangle in front of the svg to allow clicks. It
	    // is added infront because we have no way to make our
	    // links and nodes to appear infront of it.
	    /*
	    this.context_image = this.context_paper.rect(0,0,
							 this.context_svg_width,
							 this.context_svg_height);
	    this.context_image.attr({
		fill: "#ffffff",
		'fill-opacity': 0.0,
		stroke: "#000000",
	//	"stroke-opacity" : 0.0,
	    });
	    */
	    //The view decides on the current position within our paper
	    // Allows for easier panning and zooming functionality
	    // the currentView is used by several functions order to ensure proper
	    // translations
	    this.currentView = {
		x : 0.0,
		y : 0.0,
		w : this.context_svg_width,
		h : this.context_svg_height
	    };

	    this.setViewBox(
		this.currentView.x, this.currentView.y,
		this.currentView.w, this.currentView.h
	    );

	    this.fitScreen();

	}.bind(this),
	error: function (errObj, errString) {
	    log("Error: ", errString);
	},
	async: false
    });

    //set our data up within the viewer
    this.dataURL = Mappt_Data_Folder + this.imageName + ".json";

    if(UrlExists(this.dataURL)) {
	this.routeData = getJSON(this.dataURL);
    }
    else {
	log(this.dataURL, " does not exist");
    }
    return this;
};

//Used to set the position of the paper an exact place on the screen
MapptViewer.prototype.setViewBox = function(x,y,w,h) {
    this.context_paper.setViewBox(x, y, w, h);

    //fix for our background SVG image if it isn't the same size as
    //our raphael paper. Calculate the ratio of the raphael paper
    var viewString = "";
    viewString += String(x) + " ";
    viewString += String(y) + " ";
    viewString += String(w) + " ";
    viewString += String(h);

    //Now that we have a separate svg element, we need to also change
    //the view of our svg
    this.context_svg.setAttribute("viewBox", viewString);

    return this;
};

//used to translate the screen for panning and zooming
MapptViewer.prototype.translatePaper = function(x, y, s) {
    this.currentView.x = this.currentView.x - x;
    this.currentView.y = this.currentView.y - y;
    this.currentView.w = this.currentView.w / s;
    this.currentView.h = this.currentView.h / s;
    this.setViewBox(
	this.currentView.x,
	this.currentView.y,
	this.currentView.w,
	this.currentView.h
    );

    return this;
};

MapptViewer.prototype.fitScreen = function() {

    var fixedAspect = this.correctAspect(this.svg_original_width,
					 this.svg_original_height,
					 0,0);

    this.currentView.x = 0;
    this.currentView.y = 0;
    this.currentView.w = fixedAspect.width;
    this.currentView.h = fixedAspect.height;

    this.setViewBox(
	this.currentView.x,
	this.currentView.y,
	this.currentView.w,
	this.currentView.h
    );

    return this;
};

//from the list of node IDs, works out the best way to display our
//path within the mappt viewer.
MapptViewer.prototype.fitPathToScreen = function(pathList, padding) {
    if (_.isUndefined(padding)) {
	padding = 10;
    }

    var nodeList = _.map(pathList, function(elem) {
	return _.find(this.routeData.PointInfoList, function(elemData) {
	    return elemData.id == elem;
	}.bind(this));
    }.bind(this));

    console.log("Node List", nodeList);

    //from our path list, work out the smallest rectangular dimensions
    //required to show the entire path
    var minX = _.min(nodeList, function(elem) {
	return elem.px;
    }).px;

    var maxX = _.max(nodeList, function(elem) {
	return elem.px;
    }).px;

    var minY = _.min(nodeList, function(elem) {
	return elem.py;
    }).py;

    var maxY = _.max(nodeList, function(elem) {
	return elem.py;
    }).py;

    //our final rectangular dimensions
    var areaWidth = maxX - minX + padding*2;
    var areaHeight = maxY - minY + padding*2;
    var areaXOffset = minX - padding;
    var areaYOffset = minY - padding;

    console.log("path rectangle before", areaWidth, areaHeight,
		areaXOffset, areaYOffset);

    //temporaries
    var tempWidth = areaWidth;
    var tempHeight = areaHeight;
    //before we send it into "correctAspect", we need to... correct the aspect...
    //if the rectangle is wider than our context, correct our height
    if (areaWidth / areaHeight >
	this.svg_original_width / this.svg_original_height) {
	areaHeight = this.svg_original_height / this.svg_original_width * areaWidth;
	//fix our offset to place the area in the center
	areaYOffset -= (areaHeight - tempHeight) / 2;

    }
    else { //if the rectangle is narrower than our context, correct width
	areaWidth = this.svg_original_width / this.svg_original_height * areaHeight;
	//fix our offset to place our area in the center
	areaXOffset -= (areaWidth - tempWidth) / 2;
    }

    console.log("path rectangle after", areaWidth, areaHeight,
		areaXOffset, areaYOffset);

    //get the corrected aspect
    var fixedAspect = this.correctAspect(areaWidth, areaHeight,
					 areaXOffset, areaYOffset);
    console.log(fixedAspect);

    this.setViewBox(fixedAspect.width_offset, fixedAspect.height_offset,
		    fixedAspect.width, fixedAspect.height);
}

MapptViewer.prototype.getPaperScale = function() {
    var scale = this.context_width / this.currentView.w;
    return scale;
};

MapptViewer.prototype.correctAspect = function(width, height,
					       xOffset, yOffset) {

    //temporaries
    var newContextWidth;
    var newContextHeight;

    //our corrected values
    var width_aspect;
    var height_aspect;
    var svgWidth_offset;
    var svgHeight_offset;

    //if the svg is wider than the context
    if ((this.context_width / this.context_height) <
       (width / height)) {
       newContextWidth = this.context_svg_width / this.context_width * width;
       newContextHeight = newContextWidth / width * height;
       svgHeight_offset = yOffset;
       svgWidth_offset = xOffset;
    }
    else {//if the svg is narrower than the context
       newContextHeight = this.context_svg_height / this.context_height * height;
       newContextWidth = width * newContextHeight / height;
       svgWidth_offset = xOffset;
       svgHeight_offset = yOffset;

    }

    var correctedAspect = {
	width: newContextWidth,
        height: newContextHeight,
        width_offset: svgWidth_offset,
        height_offset: svgHeight_offset
    };

    return correctedAspect;
};

//looks through the point list and finds all of the IDs with the given
//attributes and descriptors
MapptViewer.prototype.getPoints = function(attr, desc) {
    var pointList = _.filter(this.routeData.PointInfoList, function(elem) {
	//check if our attributes match up
	for (k in attr) {
	    if (_.isUndefined(elem[k]) ||
		elem[k] != attr[k]) {
		return false;
	    }
	}
	//check if our descriptors match up
	return true;
    });
    return pointList;
}

//Gets the route between the starting ID and the ending ID provided
MapptViewer.prototype.getMapRoute = function(startingID, endingID, routeData) {
    //first, get the route between the two destinations
    var theRoute = getRoute_djikstra(startingID, endingID, routeData);

    return theRoute;
}

//pass a list of IDs --> list of points. This will
//draw the route on the currently set map.
MapptViewer.prototype.drawRoute = function(pathList) {
    //form our path on the map
    var firstPoint = this.getPoints({id:pathList[0]})[0];
    var pathString = "M" + firstPoint.px + " " + firstPoint.py;
    _.map(pathList, function(elem) {
	//get our point positions
	var elemPos = this.getPoints({id:elem})[0];

	//figure out the offset
	pathString += "L" +  elemPos.px + " " + elemPos.py;

    }.bind(this));

    this.currentPath.push(this.context_paper.path(pathString));
    this.currentPath[this.currentPath.length-1].attr({
	stroke: "#D11141",
	"stroke-linecap": "round",
	"stroke-linejoin" : "round",
    });
    return this;
}

//shortcut function to get and draw a route for the current map
MapptViewer.prototype.showRoute = function(startingID, endingID) {
    var theRoute = this.getMapRoute(startingID, endingID, this.routeData);
    this.drawRoute(theRoute.data);
    return this;
}

//clears the route on the map
MapptViewer.prototype.clearRoute = function() {
    _.map(this.currentPath, function(elem) {
	elem.remove();
    });
}
<commit_msg>fully working fitPathToScreen function<commit_after>//DOM classes for the main context, front and back
var MapptViewer_Class = "mappt-viewer-main";
var MapptViewer_Class_Front = MapptViewer_Class + "-front";
var MapptViewer_Class_Back = MapptViewer_Class + "-back";

MapptViewer = function(context_id, context_width, context_height) {

    //The id of the DIV element for our top-level container
    this.context_id = context_id;
    //The width of our Raphael paper
    this.context_width = context_width;
    //The height of our Raphael paper
    this.context_height = context_height;

    this.context_id_background = this.context_id + "-back";
    this.context_id_foreground = this.context_id + "-front";

    //The SVG width and height values
    this.context_svg_width = null;
    this.context_svg_height = null;

    //our current context
    this.contextObj = $("#" + context_id);

    //check if the given ID exists
    (this.contextObj.length) ||
	log("ERROR: Provided ID does not match any element within the DOM");

    $(this.contextObj).css({
	"position" : "relative",
	"top" : "0px",
	"left" : "0px",
	"width" : this.context_width,
	"height" : this.context_height,
	"overflow" : "hidden",
    });

    //apply a special class to our container
    $(this.contextObj).addClass(MapptViewer_Class);

    //create our background div and foreground divs
    //the background div will hold our SVG image
    this.contextObj_back = document.createElement("div");
    this.contextObj_back.setAttribute("id", this.context_id_background);
    $(this.contextObj_back).addClass(MapptViewer_Class_Back);
    $(this.contextObj_back).css({
	"position" : "absolute",
	"z-index" : 0,
	"width" : "100%",
	"height" : "100%",
	"overflow" : "hidden"
    });
    $(this.contextObj).append(this.contextObj_back);

    //create our foreground div
    //the foreground div will hold our raphael paper
    this.contextObj_front = document.createElement("div");
    this.contextObj_front.setAttribute("id", this.context_id_foreground);
    $(this.contextObj_front).addClass(MapptViewer_Class_Front);
    $(this.contextObj_front).css({
	"position" : "absolute",
	"z-index" : 1,
	"width" : "100%",
	"height" : "100%",
	"overflow" : "hidden",
    });
    $(this.contextObj).append(this.contextObj_front);

    //contains the image URL full path
    this.imageURL = null;
    //contains the name of the image
    this.imageName = null;

    //get the offset of our context in order to draw on the paper correctly
    this.contextOffset = this.contextObj.offset();

    //contains the paper for our Raphael
    this.context_paper = null;
    //contains the image for our paper
    this.context_image = null;

    //contains the svg for our paper
    this.context_svg = null;

    this.currentView = {
	x : 0.0,
	y : 0.0,
	w : this.context_width,
	h : this.context_height
    };

    //contains a reference to the current path on the paper
    this.currentPath = [];

}

MapptViewer.prototype.init = function() {
    var mapptViewer = this;

    this.currentView = {
    };
    return this;
};

MapptViewer.prototype.setMap = function(imageName) {
    this.imageName = imageName;
    this.imageURL = Mappt_Layout_Folder + this.imageName;

    $(this.contextObj_back).empty();

    jQuery.ajax({
	type: 'GET',
	url: this.imageURL, //this is the svg file loaded as xml
	dataType: 'xml',
	cache: false,
	success: function(svgXML) {
	    this.context_svg = svgXML.getElementsByTagName('svg')[0];

	    //regex to find a number in a string
	    var r = /\d+/;

	    //removing the aspect ratio preservation.
	    this.context_svg.setAttribute("preserveAspectRatio", "none");

	    //the svg width and height
	    this.context_svg_width = this.context_svg.getAttribute('width').match(r)[0];
	    this.context_svg_height = this.context_svg.getAttribute('height').match(r)[0];

	    //the original svg width and height
	    this.svg_original_width = this.context_svg_width;
	    this.svg_original_height = this.context_svg_height;

	    var newWidth, newHeight;
	    //make the width and height of our SVG change to fit well within our context
	    if (this.context_width / this.context_height >  //context wider than svg
		this.context_svg_width / this.context_svg_height) {
		newWidth = this.context_width;
		newHeight = this.context_svg_height / this.context_svg_width * newWidth;
	    }
	    else {
		newHeight = this.context_height;
		newWidth = this.context_svg_width / this.context_svg_height * newHeight;
	    }

	    this.context_svg_width = newWidth;
	    this.context_svg_height = newHeight;
	    this.context_svg.setAttribute("width", newWidth);
	    this.context_svg.setAttribute("height", newHeight);

	    //to simulate panning and zooming of our SVG, a DIV is
	    //resized and translated behind the main paper context.
	    //change our background context to reflect the changes.
	    $(this.contextObj_back).css({
		"width" : this.context_width,
		"height" : this.context_height,
		//"width" : newWidth,
		//"height" : newHeight,
		"top" : 0,
		"left" : 0,
		"overflow" : "hidden",
	    });

	    //setup our paper in the front div container
	    this.context_paper = Raphael(this.context_id_foreground,
					 this.context_svg_width,
					 this.context_svg_height);

	    //setup our svg map in the back div container
	    $(this.contextObj_back).append(this.context_svg);

	    //add a rectangle in front of the svg to allow clicks. It
	    // is added infront because we have no way to make our
	    // links and nodes to appear infront of it.
	    /*
	    this.context_image = this.context_paper.rect(0,0,
							 this.context_svg_width,
							 this.context_svg_height);
	    this.context_image.attr({
		fill: "#ffffff",
		'fill-opacity': 0.0,
		stroke: "#000000",
	//	"stroke-opacity" : 0.0,
	    });
	    */
	    //The view decides on the current position within our paper
	    // Allows for easier panning and zooming functionality
	    // the currentView is used by several functions order to ensure proper
	    // translations
	    this.currentView = {
		x : 0.0,
		y : 0.0,
		w : this.context_svg_width,
		h : this.context_svg_height
	    };

	    this.setViewBox(
		this.currentView.x, this.currentView.y,
		this.currentView.w, this.currentView.h
	    );

	    this.fitScreen();

	}.bind(this),
	error: function (errObj, errString) {
	    log("Error: ", errString);
	},
	async: false
    });

    //set our data up within the viewer
    this.dataURL = Mappt_Data_Folder + this.imageName + ".json";

    if(UrlExists(this.dataURL)) {
	this.routeData = getJSON(this.dataURL);
    }
    else {
	log(this.dataURL, " does not exist");
    }
    return this;
};

//Used to set the position of the paper an exact place on the screen
MapptViewer.prototype.setViewBox = function(x,y,w,h) {
    this.context_paper.setViewBox(x, y, w, h);

    //fix for our background SVG image if it isn't the same size as
    //our raphael paper. Calculate the ratio of the raphael paper
    var viewString = "";
    viewString += String(x) + " ";
    viewString += String(y) + " ";
    viewString += String(w) + " ";
    viewString += String(h);

    //Now that we have a separate svg element, we need to also change
    //the view of our svg
    this.context_svg.setAttribute("viewBox", viewString);

    return this;
};

//used to translate the screen for panning and zooming
MapptViewer.prototype.translatePaper = function(x, y, s) {
    this.currentView.x = this.currentView.x - x;
    this.currentView.y = this.currentView.y - y;
    this.currentView.w = this.currentView.w / s;
    this.currentView.h = this.currentView.h / s;
    this.setViewBox(
	this.currentView.x,
	this.currentView.y,
	this.currentView.w,
	this.currentView.h
    );

    return this;
};

MapptViewer.prototype.fitScreen = function() {

    var fixedAspect = this.correctAspect(this.svg_original_width,
					 this.svg_original_height,
					 0,0);

    this.currentView.x = 0;
    this.currentView.y = 0;
    this.currentView.w = fixedAspect.width;
    this.currentView.h = fixedAspect.height;

    this.setViewBox(
	this.currentView.x,
	this.currentView.y,
	this.currentView.w,
	this.currentView.h
    );

    return this;
};

//from the list of node IDs, works out the best way to display our
//path within the mappt viewer.
MapptViewer.prototype.fitPathToScreen = function(pathList, padding) {
    if (_.isUndefined(padding)) {
	padding = 10;
    }

    var nodeList = _.map(pathList, function(elem) {
	return _.find(this.routeData.PointInfoList, function(elemData) {
	    return elemData.id == elem;
	}.bind(this));
    }.bind(this));

    console.log("Node List", nodeList);

    //from our path list, work out the smallest rectangular dimensions
    //required to show the entire path
    var minX = _.min(nodeList, function(elem) {
	return elem.px;
    }).px;

    var maxX = _.max(nodeList, function(elem) {
	return elem.px;
    }).px;

    var minY = _.min(nodeList, function(elem) {
	return elem.py;
    }).py;

    var maxY = _.max(nodeList, function(elem) {
	return elem.py;
    }).py;

    //our final rectangular dimensions
    var areaWidth = maxX - minX + padding*2;
    var areaHeight = maxY - minY + padding*2;
    var areaXOffset = minX - padding;
    var areaYOffset = minY - padding;

    console.log("path rectangle before", areaWidth, areaHeight,
		areaXOffset, areaYOffset);

    //temporaries
    var tempWidth = areaWidth;
    var tempHeight = areaHeight;
    //before we send it into "correctAspect", we need to... correct the aspect...
    //if the rectangle is wider than our context, correct our height
    if (areaWidth / areaHeight >
	this.svg_original_width / this.svg_original_height) {
	areaHeight = this.svg_original_height / this.svg_original_width * areaWidth;
	//fix our offset to place the area in the center
	areaYOffset -= (areaHeight - tempHeight) / 2;

    }
    else { //if the rectangle is narrower than our context, correct width
	areaWidth = this.svg_original_width / this.svg_original_height * areaHeight;
	//fix our offset to place our area in the center
	areaXOffset -= (areaWidth - tempWidth) / 2;
    }

    console.log("path rectangle after", areaWidth, areaHeight,
		areaXOffset, areaYOffset);

    //get the corrected aspect
    var fixedAspect = this.correctAspect(areaWidth, areaHeight,
					 areaXOffset, areaYOffset);
    console.log(fixedAspect);

    this.setViewBox(fixedAspect.width_offset, fixedAspect.height_offset,
		    fixedAspect.width, fixedAspect.height);
}

MapptViewer.prototype.getPaperScale = function() {
    var scale = this.context_width / this.currentView.w;
    return scale;
};

//corrects the aspect with respect to the context.  NOTE: this
//function only works if the provided width and height are of the same
//aspect as the SVG
//TODO: fix the NOTE and put the fit to path code in here.
MapptViewer.prototype.correctAspect = function(width, height,
					       xOffset, yOffset) {

    //temporaries
    var newContextWidth;
    var newContextHeight;

    //our corrected values
    var width_aspect;
    var height_aspect;
    var svgWidth_offset;
    var svgHeight_offset;

    console.log("before offset", xOffset, yOffset);

    //if the svg is wider than the context
    if ((this.context_width / this.context_height) <
       (width / height)) {
	newContextWidth = this.context_svg_width / this.context_width * width;
	newContextHeight = newContextWidth / width * height;
	svgWidth_offset = xOffset * (this.context_svg_width / this.context_width);
	svgWidth_offset = xOffset;
	svgHeight_offset = yOffset - (height - newContextHeight) / 2;
    }
    else {//if the svg is narrower than the context
	newContextHeight = this.context_svg_height / this.context_height * height;
	newContextWidth = width * newContextHeight / height;
	svgHeight_offset = yOffset * (this.context_svg_height / this.context_height);
	svgHeight_offset = yOffset;
	svgWidth_offset = xOffset + (width - newContextWidth) / 2;
    }

    var correctedAspect = {
	width: newContextWidth,
        height: newContextHeight,
        width_offset: svgWidth_offset,
        height_offset: svgHeight_offset
    };

    console.log("after offset", svgWidth_offset, svgHeight_offset);

    return correctedAspect;
};

//looks through the point list and finds all of the IDs with the given
//attributes and descriptors
MapptViewer.prototype.getPoints = function(attr, desc) {
    var pointList = _.filter(this.routeData.PointInfoList, function(elem) {
	//check if our attributes match up
	for (k in attr) {
	    if (_.isUndefined(elem[k]) ||
		elem[k] != attr[k]) {
		return false;
	    }
	}
	//check if our descriptors match up
	return true;
    });
    return pointList;
}

//Gets the route between the starting ID and the ending ID provided
MapptViewer.prototype.getMapRoute = function(startingID, endingID, routeData) {
    //first, get the route between the two destinations
    var theRoute = getRoute_djikstra(startingID, endingID, routeData);

    return theRoute;
}

//pass a list of IDs --> list of points. This will
//draw the route on the currently set map.
MapptViewer.prototype.drawRoute = function(pathList) {
    //form our path on the map
    var firstPoint = this.getPoints({id:pathList[0]})[0];
    var pathString = "M" + firstPoint.px + " " + firstPoint.py;
    _.map(pathList, function(elem) {
	//get our point positions
	var elemPos = this.getPoints({id:elem})[0];

	//figure out the offset
	pathString += "L" +  elemPos.px + " " + elemPos.py;

    }.bind(this));

    this.currentPath.push(this.context_paper.path(pathString));
    this.currentPath[this.currentPath.length-1].attr({
	stroke: "#D11141",
	"stroke-linecap": "round",
	"stroke-linejoin" : "round",
    });
    return this;
}

//shortcut function to get and draw a route for the current map
MapptViewer.prototype.showRoute = function(startingID, endingID) {
    var theRoute = this.getMapRoute(startingID, endingID, this.routeData);
    this.drawRoute(theRoute.data);
    return this;
}

//clears the route on the map
MapptViewer.prototype.clearRoute = function() {
    _.map(this.currentPath, function(elem) {
	elem.remove();
    });
}
<|endoftext|>