<commit_before>angular.module('controllers', [])

.controller('PlaylistsCtrl', function($scope, $ionicPlatform, $cordovaOauth, Spotify) {
  console.log('playlist control instantiated')

  let CLIENT_ID = 'd3fe3362f8634a1b82b89ab344238891'
  let SCOPE = ['user-read-private', 'playlist-read-private', 'playlist-modify-public', 'playlist-modify-private']

  $scope.playlists = []

  $ionicPlatform.ready(function() {
    let storedToken = window.localStorage.getItem('spotify-token')
    // let storedToken = null
    console.log('checking for stored token')
    if(storedToken) {
      Spotify.setAuthToken(storedToken)
      $scope.updateInfo()
      console.log('Stored token found')
    } else {
      $scope.performLogin()
    }
  })

  $scope.performLogin = function() {
    $cordovaOauth.spotify(CLIENT_ID, SCOPE).then(function(result) {
      console.log("login successful")
      window.localStorage.setItem('spotify-token', result.access_token)
      Spotify.setAuthToken(result.access_token)
      $scope.updateInfo()
    }, function(error) {
      console.log('Error -> ' + error)
    })
  }

  $scope.updateInfo = function() {
    Spotify.getCurrentUser().then(function (data) {
      $scope.getUserPlaylists(data.id)
    }, function(error) {
      $scope.performLogin()
    })
  }

  $scope.getUserPlaylists = function(userid) {
    Spotify.getUserPlaylists(userid).then(function (data) {
      $scope.playlists = data.items;
    })
  }
})

.controller('PlaylistDetailCtrl', function($scope, $state, $stateParams, Spotify, $ionicNavBarDelegate, $ionicPopup, $ionicPlatform, $ionicHistory, $cordovaToast) {
  console.log('playlist detail control instantiated')

  // Grab variables from route paramaters
  // This is how I pass the information from PlaylistCtrl
  let listid = $stateParams.listid
  let userid = $stateParams.userid
  $scope.playlistTitle = $stateParams.listTitle

  $scope.editMode = false // toggled on and off
  $scope.changesMade = false // turned to true when first edit made
  $scope.orderCriteria = "none"
  $scope.descending = false

  $scope.tracks = []

  // Load in tracks when controller is instantiated
  getTracks()

  /**
   * Get tracks for playlist usig playlist id and user id
   */
  function getTracks() {
    Spotify.getPlaylist(userid, listid)
      .then(data => {
        $scope.tracks = data.tracks.items
      }).catch(error => {
        console.dir(error)
      })
  }

  function toggleEditMode() {
    $scope.editMode = !$scope.editMode

    // Reset these values when entering/exiting edit mode
    $scope.orderCriteria = 'none'
    $scope.descending = false
  }

  function showPlaylistSavedToast() {
    $cordovaToast.showWithOptions({
      message: "Playlist saved",
      duration: "short", // which is 2000 ms. "long" is 4000. Or specify the nr of ms yourself.
      position: "bottom",
      addPixelsY: -175  // added a negative value to move it up a bit (default 0)
    }).then(success => {
      console.log(success)
    }).catch(error => {
      console.log(error)
    })
  }

  // Called whenever "order by" is changed or "descnding" is changed
  // Sorts based on order criteria and whether user wants them to descend or not
  function orderSongs() {
    console.log("$scope.orderCriteria", $scope.orderCriteria)
    console.log("$scope.descending", $scope.descending)

    let returnVal
    if($scope.descending === false) returnVal = -1
    else returnVal = 1

    switch($scope.orderCriteria) {
      case 'song':
        $scope.tracks.sort((a, b) => {
          if (a.track.name < b.track.name) return returnVal;
          if (a.track.name > b.track.name) return -returnVal;
          return 0;
        })
        break
      case 'artist':
        $scope.tracks.sort((a, b) => {
          if (a.track.artists[0].name < b.track.artists[0].name) return returnVal;
          if (a.track.artists[0].name > b.track.artists[0].name) return -returnVal;
          return 0;
        })
        break
      case 'album':
        $scope.tracks.sort((a, b) => {
          if (a.track.album.name < b.track.album.name) return returnVal;
          if (a.track.album.name > b.track.album.name) return -returnVal;
          return 0;
        })
        break
      case 'length':
        $scope.tracks.sort((a, b) => {
          if (a.track.duration_ms < b.track.duration_ms) return returnVal;
          if (a.track.duration_ms > b.track.duration_ms) return -returnVal;
          return 0;
        })
        break
    }
  }

  $scope.onEditButtonTap = function() {
    console.log('edit button tap')
    if($scope.editMode) {
      $scope.saveChanges()
    } else {
      toggleEditMode()
    }
  }

  $scope.onItemMove = function(item, fromIndex, toIndex) {
    $scope.tracks.splice(fromIndex, 1)
    $scope.tracks.splice(toIndex, 0, item)
    $scope.$apply()

    $scope.changesMade = true
  }

  $scope.onItemDelete = function(item) {
    $scope.tracks.splice($scope.tracks.indexOf(item), 1)

    $scope.changesMade = true
  }

  /**
   * Sorts $scope.tracks on different criteria
   * @param  {string} orderCriteria - value of select option
   */
  $scope.onSelectChange = function(orderCriteria) {
    $scope.orderCriteria = orderCriteria
    $scope.changesMade = true
    orderSongs()
  }

  $scope.onDescendToggle = function(descending) {
    $scope.descending = descending
    $scope.changesMade = true
    orderSongs()
  }

  $scope.saveChanges = function() {
    if($scope.changesMade === false) {
      console.log('no changes made')
      toggleEditMode()
    } else {
      console.log('save changes')
      let uris = []
      $scope.tracks.forEach(item => uris.push(item.track.uri))
      Spotify
        .replacePlaylistTracks(userid, listid, uris)
        .then(data => {
          $scope.changesMade = false // reset after save
          toggleEditMode()
          showPlaylistSavedToast()
        })
        .catch(error => {
          alert('There was an error saving changes.  Please try again.')
          console.dir(error)
        })
    }
  }

  $scope.cancelChanges = function() {
    console.log('cancel changes')

    getTracks()
    toggleEditMode()
  }

  // Save playlist upon leaving view
  $scope.$on("$ionicView.leave", $scope.saveChanges);

  // Save playlist upon app going into background
  $ionicPlatform.on('pause', $scope.saveChanges);
})

.controller('ChatsCtrl', function($scope, Chats) {
  // With the new view caching in Ionic, Controllers are only called
  // when they are recreated or on app start, instead of every page change.
  // To listen for when this page is active (for example, to refresh data),
  // listen for the $ionicView.enter event:
  //
  //$scope.$on('$ionicView.enter', function(e) {
  //});

  console.log('chats ctrl instantiated')

  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
})

.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
  console.log('chats detail ctrl instantiated')

  $scope.chat = Chats.get($stateParams.chatId);
})

.controller('AccountCtrl', function($scope, $cordovaOauth, Spotify) {
  console.log('accounts ctrl instantiated')

  $scope.settings = {
    enableFriends: true
  };

})

<commit_msg>refactor wip<commit_after>angular.module('controllers', [])

.controller('PlaylistsCtrl', function($scope, $ionicPlatform, $cordovaOauth, Spotify) {
  console.log('playlist control instantiated')

  let CLIENT_ID = 'd3fe3362f8634a1b82b89ab344238891'
  let SCOPE = ['user-read-private', 'playlist-read-private', 'playlist-modify-public', 'playlist-modify-private']

  $scope.playlists = []

  $ionicPlatform.ready(function() {
    let storedToken = window.localStorage.getItem('spotify-token')
    // let storedToken = null
    console.log('checking for stored token')
    if(storedToken) {
      Spotify.setAuthToken(storedToken)
      $scope.updateInfo()
      console.log('Stored token found')
    } else {
      $scope.performLogin()
    }
  })

  $scope.performLogin = function() {
    $cordovaOauth.spotify(CLIENT_ID, SCOPE).then(function(result) {
      console.log("login successful")
      window.localStorage.setItem('spotify-token', result.access_token)
      Spotify.setAuthToken(result.access_token)
      $scope.updateInfo()
    }, function(error) {
      console.log('Error -> ' + error)
    })
  }

  $scope.updateInfo = function() {
    Spotify.getCurrentUser().then(function (data) {
      $scope.getUserPlaylists(data.id)
    }, function(error) {
      $scope.performLogin()
    })
  }

  $scope.getUserPlaylists = function(userid) {
    Spotify.getUserPlaylists(userid).then(function (data) {
      $scope.playlists = data.items;
    })
  }
})

.controller('PlaylistDetailCtrl', function($scope, $state, $stateParams, Spotify, $ionicNavBarDelegate, $ionicPopup, $ionicPlatform, $ionicHistory, $cordovaToast) {
  console.log('playlist detail control instantiated')

  // Grab variables from route paramaters
  // This is how I pass the information from PlaylistCtrl
  let listid = $stateParams.listid
  let userid = $stateParams.userid
  $scope.playlistTitle = $stateParams.listTitle

  $scope.editMode = false // toggled on and off
  $scope.changesMade = false // turned to true when first edit made
  $scope.orderCriteria = "none"
  $scope.descending = false

  $scope.tracks = []
  getTracks() // load tracks immediately

  // Event Listeners
  // Save changes on exit app or view change
  $scope.$on("$ionicView.leave", $scope.saveChanges);
  $ionicPlatform.on('pause', $scope.saveChanges);

  /**
   * Get tracks for playlist using playlist id and user id
   */
  function getTracks() {
    Spotify.getPlaylist(userid, listid)
      .then(data => {
        $scope.tracks = data.tracks.items
      }).catch(error => {
        console.dir(error)
      })
  }

  /**
   * toggle edit mode variable
   * reset ordering variables
   */
  function toggleEditMode() {
    $scope.editMode = !$scope.editMode

    // Reset these values when entering/exiting edit mode
    $scope.orderCriteria = 'none'
    $scope.descending = false
  }

  /**
   * Display toast at bottom saying 'playlist saved'
   * Function called whenever playlist is saved
   */
  function showPlaylistSavedToast() {
    $cordovaToast.showWithOptions({
      message: "Playlist saved",
      duration: "short", // which is 2000 ms. "long" is 4000. Or specify the nr of ms yourself.
      position: "bottom",
      addPixelsY: -175  // added a negative value to move it up a bit (default 0)
    }).then(success => {
      console.log(success)
    }).catch(error => {
      console.log(error)
    })
  }

  /**
   * code to execute when user taps edit button
   * Calls saveChanges if attmpting to exiting edit mode
   * Toggles edit mode ON if not in edit mode
   */
  $scope.onEditButtonTap = function() {
    console.log('edit button tap')
    if($scope.editMode) {
      $scope.saveChanges()
    } else {
      toggleEditMode()
    }
  }

  /**
   * Called when item is reordered on DOM
   * @param  {obj} item - object containing track info and metadata
   * @param  {integer} fromIndex - index where the object was in $scope.tracks
   * @param  {integer} toIndex - index where the object was moved to in $scope.tracks
   */
  $scope.onItemMove = function(item, fromIndex, toIndex) {
    $scope.tracks.splice(fromIndex, 1)
    $scope.tracks.splice(toIndex, 0, item)
    $scope.$apply()

    $scope.changesMade = true
  }

  /**
   * Called when item deleted in DOM
   * @param  {obj} item - object containing track info and metadata
   */
  $scope.onItemDelete = function(item) {
    $scope.tracks.splice($scope.tracks.indexOf(item), 1)

    $scope.changesMade = true
  }

   /**
   * Sorts $scope.tracks on different criteria
   * @param  {string} orderCriteria - value of select option
   */
  $scope.onSelectChange = function(orderCriteria) {
    $scope.orderCriteria = orderCriteria
    $scope.changesMade = true
    orderSongs()
  }

  /**
   * Run when user selects toggles the "descending" option
   * @param  {boolean} descending - state of the "descending" toggle
   */
  $scope.onDescendToggle = function(descending) {
    $scope.descending = descending
    $scope.changesMade = true
    orderSongs()
  }

  /**
   * attempts post of new playlist state to Spotify if changes were made
   */
  $scope.saveChanges = function() {
    if($scope.changesMade === false) {
      console.log('no changes made')
      toggleEditMode()
    } else {
      console.log('save changes')
      let uris = []
      $scope.tracks.forEach(item => uris.push(item.track.uri))
      Spotify
        .replacePlaylistTracks(userid, listid, uris)
        .then(data => {
          $scope.changesMade = false // reset after save
          toggleEditMode()
          showPlaylistSavedToast()
        })
        .catch(error => {
          alert('There was an error saving changes.  Please try again.')
          console.dir(error)
        })
    }
  }

  /**
   * Code run when user selects "discard changes"
   * Gets current tracklist from Spotify and updates DOM
   */
  $scope.cancelChanges = function() {
    console.log('cancel changes')

    getTracks()
    toggleEditMode()
  }

  /**
   * Called whenever "order by" is changed or "descnding" is changed
   * Sorts based on order criteria and whether user wants them to descend or not
   */
  function orderSongs() {
    console.log("$scope.orderCriteria", $scope.orderCriteria)
    console.log("$scope.descending", $scope.descending)

    let returnVal
    if($scope.descending === false) returnVal = -1
    else returnVal = 1

    switch($scope.orderCriteria) {
      case 'song':
        $scope.tracks.sort((a, b) => {
          if (a.track.name < b.track.name) return returnVal;
          if (a.track.name > b.track.name) return -returnVal;
          return 0;
        })
        break
      case 'artist':
        $scope.tracks.sort((a, b) => {
          if (a.track.artists[0].name < b.track.artists[0].name) return returnVal;
          if (a.track.artists[0].name > b.track.artists[0].name) return -returnVal;
          return 0;
        })
        break
      case 'album':
        $scope.tracks.sort((a, b) => {
          if (a.track.album.name < b.track.album.name) return returnVal;
          if (a.track.album.name > b.track.album.name) return -returnVal;
          return 0;
        })
        break
      case 'length':
        $scope.tracks.sort((a, b) => {
          if (a.track.duration_ms < b.track.duration_ms) return returnVal;
          if (a.track.duration_ms > b.track.duration_ms) return -returnVal;
          return 0;
        })
        break
    }
  } // end orderSongs()
})

<|endoftext|>


<commit_before>/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require("path");
const { exec } = require("child_process");
const { file } = require("tmp-promise");
const { writeFile } = require("fs-extra");
const toneJson = require("../docs/tone.json");
const eachLimit = require("async/eachLimit");
const os = require("os");
const cpuCount = os.cpus().length;

/**
 * Get all of the examples
 */
function findExamples(obj) {
	let examples = [];
	for (let prop in obj) {
		if (Array.isArray(obj[prop])) {
			obj[prop].forEach(child => {
				examples = [...examples, ...findExamples(child)];
			});
		} else if (prop === "comment" && obj[prop].tags) {
			examples = [
				...examples,
				...obj[prop].tags.filter(tag => tag.tag === "example").map(tag => tag.text)
			];
		} else if (typeof obj[prop] === "object") {
			examples = [...examples, ...findExamples(obj[prop])];
		} else {
			// console.log(prop);
		}
	}
	// filter any repeats
	return [...new Set(examples)];
}

/**
 * A promise version of exec
 */
function execPromise(cmd) {
	return new Promise((done, error) => {
		exec(cmd, (e, output) => {
			if (e) {
				error(output);
			} else {
				done();
			}
		});
	});
}

/**
 * Run the string through the typescript compiler
 */
async function testExampleString(str) {
	str = str.replace("from \"tone\"", `from "${resolve(__dirname, "../")}"`);
	const { path, cleanup } = await file({ postfix: ".ts" });
	// work with file here in fd
	await writeFile(path, str);
	try {
		await execPromise(`tsc ${path} --noEmit`);
	} finally {
		cleanup();
	}
}

const examples = findExamples(toneJson);

async function main() {
	let passed = 0;
	await eachLimit(examples, cpuCount, async example => {
		try {
			await testExampleString(example);
			passed++;
		} catch (e) {
			console.log(example + "\n" + e);
		}
	});
	console.log(`valid examples ${passed}/${examples.length}`);
}
main();

<commit_msg>testing with dom lib<commit_after>/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require("path");
const { exec } = require("child_process");
const { file } = require("tmp-promise");
const { writeFile } = require("fs-extra");
const toneJson = require("../docs/tone.json");
const eachLimit = require("async/eachLimit");
const os = require("os");
const cpuCount = os.cpus().length;

/**
 * Get all of the examples
 */
function findExamples(obj) {
	let examples = [];
	for (let prop in obj) {
		if (Array.isArray(obj[prop])) {
			obj[prop].forEach(child => {
				examples = [...examples, ...findExamples(child)];
			});
		} else if (prop === "comment" && obj[prop].tags) {
			examples = [
				...examples,
				...obj[prop].tags.filter(tag => tag.tag === "example").map(tag => tag.text)
			];
		} else if (typeof obj[prop] === "object") {
			examples = [...examples, ...findExamples(obj[prop])];
		} else {
			// console.log(prop);
		}
	}
	// filter any repeats
	return [...new Set(examples)];
}

/**
 * A promise version of exec
 */
function execPromise(cmd) {
	return new Promise((done, error) => {
		exec(cmd, (e, output) => {
			if (e) {
				error(output);
			} else {
				done();
			}
		});
	});
}

/**
 * Run the string through the typescript compiler
 */
async function testExampleString(str) {
	str = str.replace("from \"tone\"", `from "${resolve(__dirname, "../")}"`);
	const { path, cleanup } = await file({ postfix: ".ts" });
	// work with file here in fd
	await writeFile(path, str);
	try {
		await execPromise(`tsc  --noEmit --target es5 --lib dom,ES2015 ${path}`);
	} finally {
		cleanup();
	}
}

const examples = findExamples(toneJson);

async function main() {
	let passed = 0;
	await eachLimit(examples, cpuCount, async example => {
		try {
			await testExampleString(example);
			passed++;
		} catch (e) {
			console.log(example + "\n" + e);
		}
	});
	console.log(`valid examples ${passed}/${examples.length}`);
}
main();

<|endoftext|>


<commit_before>/**
 * React form server.The simplest server ever.
 *
 * This Express application serves the contact form with its
 * assets and processes the submission. A lot of aspects
 * of a real life application have been omitted here
 * for the sake of simplicity such as form validation and
 * security.
 */

var bodyParser = require('body-parser');
var express = require('express');
var app = express();

// Sets the port to use.
app.set('port', (process.env.PORT || 3000));

// Exposes public assets such as index.html and JavaScript files.
app.use(express.static('public'));

// Adds support for JSON-encoded bodies used in POST requests.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

// Processes a form submission.
app.post('/send', function (req, res) {
  console.log(req.body);
  return res.send({status: 'OK'});
});

// Starts the web application.
var server = app.listen(app.get('port'), function () {
  console.log('App is running at port', app.get('port'));
});
<commit_msg>Simplify port logic at server.js<commit_after>/**
 * React form server.The simplest server ever.
 *
 * This Express application serves the contact form with its
 * assets and processes the submission. A lot of aspects
 * of a real life application have been omitted here
 * for the sake of simplicity such as form validation and
 * security.
 */

var bodyParser = require('body-parser');
var express = require('express');
var app = express();

// Exposes public assets such as index.html and JavaScript files.
app.use(express.static('public'));

// Adds support for JSON-encoded bodies used in POST requests.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

// Processes a form submission.
app.post('/send', function (req, res) {
  console.log(req.body);
  return res.send({status: 'OK'});
});

// Starts the web application.
app.set('port', (process.env.PORT || 3000));
app.listen(app.get('port'), function () {
  console.log('App is running at port', app.get('port'));
});
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

/*global Promise */

// Provides class sap.ui.core.ElementMetadata
sap.ui.define([
	'sap/base/Log',
	'sap/base/util/ObjectPath',
	'sap/ui/base/ManagedObjectMetadata',
	'sap/ui/core/Renderer'
],
	function(Log, ObjectPath, ManagedObjectMetadata, Renderer) {
	"use strict";

	/**
	 * Creates a new metadata object for a UIElement subclass.
	 *
	 * @param {string} sClassName fully qualified name of the class that is described by this metadata object
	 * @param {object} oStaticInfo static info to construct the metadata from
	 *
	 * @class
	 * @author SAP SE
	 * @version ${version}
	 * @since 0.8.6
	 * @alias sap.ui.core.ElementMetadata
	 * @extends sap.ui.base.ManagedObjectMetadata
	 * @public
	 */
	var ElementMetadata = function(sClassName, oClassInfo) {

		// call super constructor
		ManagedObjectMetadata.apply(this, arguments);
	};

	//chain the prototypes
	ElementMetadata.prototype = Object.create(ManagedObjectMetadata.prototype);
	ElementMetadata.prototype.constructor = ElementMetadata;

	/**
	 * Calculates a new id based on a prefix.
	 *
	 * @return {string} A (hopefully unique) control id
	 * @public
	 * @function
	 */
	ElementMetadata.uid = ManagedObjectMetadata.uid;

	/**
	 * By default, the element name is equal to the class name
	 * @return {string} the qualified name of the UIElement class
	 * @public
	 */
	ElementMetadata.prototype.getElementName = function() {
		return this._sClassName;
	};

	/**
	 * Determines the class name of the renderer for the described control class.
	 */
	ElementMetadata.prototype.getRendererName = function() {
		return this._sRendererName;
	};

	/**
	 * Retrieves the renderer for the described control class
	 */
	ElementMetadata.prototype.getRenderer = function() {

		if ( this._oRenderer ) {
			return this._oRenderer;
		}

		// determine name via function for those legacy controls that override getRendererName()
		var sRendererName = this.getRendererName();

		if ( !sRendererName ) {
			return;
		}

		// check if renderer class exists already, in case it was passed inplace,
		// and written to the global namespace during applySettings().
		this._oRenderer = ObjectPath.get(sRendererName);
		if (this._oRenderer) {
			return this._oRenderer;
		}

		// if not, try to load a module with the same name
		Log.warning("Synchronous loading of Renderer for control class '" + this.getName() + "', due to missing Renderer dependency.", "SyncXHR", null, function() {
			return {
				type: "SyncXHR",
				name: sRendererName
			};
		});
		this._oRenderer =
			sap.ui.requireSync(sRendererName.replace(/\./g, "/"))
			|| ObjectPath.get(sRendererName);

		return this._oRenderer;
	};

	ElementMetadata.prototype.applySettings = function(oClassInfo) {

		var oStaticInfo = oClassInfo.metadata;

		this._sVisibility = oStaticInfo.visibility || "public";

		// remove renderer stuff before calling super.
		var vRenderer = oClassInfo.hasOwnProperty("renderer") ? (oClassInfo.renderer || "") : undefined;
		delete oClassInfo.renderer;

		ManagedObjectMetadata.prototype.applySettings.call(this, oClassInfo);

		var oParent = this.getParent();
		this._sRendererName = this.getName() + "Renderer";
		this.dnd = Object.assign({
			draggable: false,
			droppable: false
		}, oParent.dnd, (typeof oStaticInfo.dnd == "boolean") ? {
			draggable: oStaticInfo.dnd,
			droppable: oStaticInfo.dnd
		} : oStaticInfo.dnd);

		if ( typeof vRenderer !== "undefined" ) {

			if ( typeof vRenderer === "string" ) {
				this._sRendererName = vRenderer || undefined;
				return;
			}

			// try to identify fully built renderers
			if ( typeof vRenderer === "object" && typeof vRenderer.render === "function" ) {
				var oRenderer = ObjectPath.get(this.getRendererName());
				if ( oRenderer === vRenderer ) {
					// the given renderer has been exported globally already, it can be used without further action
					this._oRenderer = vRenderer;
					return;
				}
				if ( oRenderer === undefined && typeof vRenderer.extend === "function" ) {
					// the given renderer has an 'extend' method, so it most likely has been created by one of the
					// extend methods and it is usable already; it just has to be exported globally
					ObjectPath.set(this.getRendererName(), vRenderer);
					this._oRenderer = vRenderer;
					return;
				}
			}

			if ( typeof vRenderer === "function" ) {
				vRenderer = { render : vRenderer };
			}

			var oParent = this.getParent();
			var oBaseRenderer;
			if ( oParent instanceof ElementMetadata ) {
				oBaseRenderer = oParent.getRenderer();
			}
			this._oRenderer = Renderer.extend.call(oBaseRenderer || Renderer, this.getRendererName(), vRenderer);
		}
	};

	ElementMetadata.prototype.afterApplySettings = function() {
		ManagedObjectMetadata.prototype.afterApplySettings.apply(this, arguments);
		this.register && this.register(this);
	};

	ElementMetadata.prototype.isHidden = function() {
		return this._sVisibility === "hidden";
	};

	// ---- Aggregation -----------------------------------------------------------------------

	var fnMetaFactoryAggregation = ElementMetadata.prototype.metaFactoryAggregation;

	function Aggregation(oClass, name, info) {
		fnMetaFactoryAggregation.apply(this, arguments);
		this.dnd = Object.assign({
			draggable: false,
			droppable: false,
			layout: "Vertical"
		}, (typeof info.dnd == "boolean") ? {
			draggable: info.dnd,
			droppable: info.dnd
		} : info.dnd);
	}

	Aggregation.prototype = Object.create(fnMetaFactoryAggregation.prototype);
	Aggregation.prototype.constructor = Aggregation;
	ElementMetadata.prototype.metaFactoryAggregation = Aggregation;

	/**
	 * Returns an info object describing the drag-and-drop behavior.
	 *
	 * @param {string} [sAggregationName] name of the aggregation or empty.
	 * @returns {Object} An info object about the drag-and-drop behavior.
	 * @public
	 * @since 1.56
	 */
	ElementMetadata.prototype.getDragDropInfo = function(sAggregationName) {
		if (!sAggregationName) {
			return this.dnd;
		}

		var oAggregation = this._mAllAggregations[sAggregationName] || this._mAllPrivateAggregations[sAggregationName];
		if (!oAggregation) {
			return {};
		}

		return oAggregation.dnd;
	};

	return ElementMetadata;

}, /* bExport= */ true);<commit_msg>[INTERNAL] sap.ui.core.ElementMetadata: solve eslint warnings<commit_after>/*!
 * ${copyright}
 */

/*global Promise */

// Provides class sap.ui.core.ElementMetadata
sap.ui.define([
	'sap/base/Log',
	'sap/base/util/ObjectPath',
	'sap/ui/base/ManagedObjectMetadata',
	'sap/ui/core/Renderer'
],
	function(Log, ObjectPath, ManagedObjectMetadata, Renderer) {
	"use strict";

	/**
	 * Creates a new metadata object for a UIElement subclass.
	 *
	 * @param {string} sClassName fully qualified name of the class that is described by this metadata object
	 * @param {object} oClassInfo static info to construct the metadata from
	 *
	 * @class
	 * @author SAP SE
	 * @version ${version}
	 * @since 0.8.6
	 * @alias sap.ui.core.ElementMetadata
	 * @extends sap.ui.base.ManagedObjectMetadata
	 * @public
	 */
	var ElementMetadata = function(sClassName, oClassInfo) {

		// call super constructor
		ManagedObjectMetadata.apply(this, arguments);
	};

	//chain the prototypes
	ElementMetadata.prototype = Object.create(ManagedObjectMetadata.prototype);
	ElementMetadata.prototype.constructor = ElementMetadata;

	/**
	 * Calculates a new id based on a prefix.
	 *
	 * @return {string} A (hopefully unique) control id
	 * @public
	 * @function
	 */
	ElementMetadata.uid = ManagedObjectMetadata.uid;

	/**
	 * By default, the element name is equal to the class name
	 * @return {string} the qualified name of the UIElement class
	 * @public
	 */
	ElementMetadata.prototype.getElementName = function() {
		return this._sClassName;
	};

	/**
	 * Determines the class name of the renderer for the described control class.
	 *
	 * @returns {string} The renderer name
	 */
	ElementMetadata.prototype.getRendererName = function() {
		return this._sRendererName;
	};

	/**
	 * Retrieves the renderer for the described control class
	 *
	 * If no renderer exists undefined is returned
	 * @returns {object|undefined} The renderer
	 */
	ElementMetadata.prototype.getRenderer = function() {

		if ( this._oRenderer ) {
			return this._oRenderer;
		}

		// determine name via function for those legacy controls that override getRendererName()
		var sRendererName = this.getRendererName();

		if ( !sRendererName ) {
			return undefined;
		}

		// check if renderer class exists already, in case it was passed inplace,
		// and written to the global namespace during applySettings().
		this._oRenderer = ObjectPath.get(sRendererName);
		if (this._oRenderer) {
			return this._oRenderer;
		}

		// if not, try to load a module with the same name
		Log.warning("Synchronous loading of Renderer for control class '" + this.getName() + "', due to missing Renderer dependency.", "SyncXHR", null, function() {
			return {
				type: "SyncXHR",
				name: sRendererName
			};
		});
		this._oRenderer =
			sap.ui.requireSync(sRendererName.replace(/\./g, "/"))
			|| ObjectPath.get(sRendererName);

		return this._oRenderer;
	};

	ElementMetadata.prototype.applySettings = function(oClassInfo) {

		var oStaticInfo = oClassInfo.metadata;

		this._sVisibility = oStaticInfo.visibility || "public";

		// remove renderer stuff before calling super.
		var vRenderer = oClassInfo.hasOwnProperty("renderer") ? (oClassInfo.renderer || "") : undefined;
		delete oClassInfo.renderer;

		ManagedObjectMetadata.prototype.applySettings.call(this, oClassInfo);

		var oParent = this.getParent();
		this._sRendererName = this.getName() + "Renderer";
		this.dnd = Object.assign({
			draggable: false,
			droppable: false
		}, oParent.dnd, (typeof oStaticInfo.dnd == "boolean") ? {
			draggable: oStaticInfo.dnd,
			droppable: oStaticInfo.dnd
		} : oStaticInfo.dnd);

		if ( typeof vRenderer !== "undefined" ) {

			if ( typeof vRenderer === "string" ) {
				this._sRendererName = vRenderer || undefined;
				return;
			}

			// try to identify fully built renderers
			if ( typeof vRenderer === "object" && typeof vRenderer.render === "function" ) {
				var oRenderer = ObjectPath.get(this.getRendererName());
				if ( oRenderer === vRenderer ) {
					// the given renderer has been exported globally already, it can be used without further action
					this._oRenderer = vRenderer;
					return;
				}
				if ( oRenderer === undefined && typeof vRenderer.extend === "function" ) {
					// the given renderer has an 'extend' method, so it most likely has been created by one of the
					// extend methods and it is usable already; it just has to be exported globally
					ObjectPath.set(this.getRendererName(), vRenderer);
					this._oRenderer = vRenderer;
					return;
				}
			}

			if ( typeof vRenderer === "function" ) {
				vRenderer = { render : vRenderer };
			}

			var oBaseRenderer;
			if ( oParent instanceof ElementMetadata ) {
				oBaseRenderer = oParent.getRenderer();
			}
			this._oRenderer = Renderer.extend.call(oBaseRenderer || Renderer, this.getRendererName(), vRenderer);
		}
	};

	ElementMetadata.prototype.afterApplySettings = function() {
		ManagedObjectMetadata.prototype.afterApplySettings.apply(this, arguments);
		this.register && this.register(this);
	};

	ElementMetadata.prototype.isHidden = function() {
		return this._sVisibility === "hidden";
	};

	// ---- Aggregation -----------------------------------------------------------------------

	var fnMetaFactoryAggregation = ElementMetadata.prototype.metaFactoryAggregation;

	function Aggregation(oClass, name, info) {
		fnMetaFactoryAggregation.apply(this, arguments);
		this.dnd = Object.assign({
			draggable: false,
			droppable: false,
			layout: "Vertical"
		}, (typeof info.dnd == "boolean") ? {
			draggable: info.dnd,
			droppable: info.dnd
		} : info.dnd);
	}

	Aggregation.prototype = Object.create(fnMetaFactoryAggregation.prototype);
	Aggregation.prototype.constructor = Aggregation;
	ElementMetadata.prototype.metaFactoryAggregation = Aggregation;

	/**
	 * Returns an info object describing the drag-and-drop behavior.
	 *
	 * @param {string} [sAggregationName] name of the aggregation or empty.
	 * @returns {Object} An info object about the drag-and-drop behavior.
	 * @public
	 * @since 1.56
	 */
	ElementMetadata.prototype.getDragDropInfo = function(sAggregationName) {
		if (!sAggregationName) {
			return this.dnd;
		}

		var oAggregation = this._mAllAggregations[sAggregationName] || this._mAllPrivateAggregations[sAggregationName];
		if (!oAggregation) {
			return {};
		}

		return oAggregation.dnd;
	};

	return ElementMetadata;

}, /* bExport= */ true);<|endoftext|>


<commit_before>'use strict';

var
  path = require('path'),
  utils = require('./util'),
  identifierSanitizer = require('./identifierSanitizer'),
  _ = require('lodash'),
  xmlWriter = require('./xmlWriter'),
  pageNameExtractor = require('./pageNameExtractor'),
  screenshotSaver = require('./screenshotSaver'),
  Q = require('q');

function normalizeMetadataDetails(details) {
  if (_.isUndefined(details)) {
    return [];
  }
  if (_.isArray(details) || !_.isObject(details)) {
    throw new Error('Step metadata details must be an object!');
  }

  var mappedDetails = [];
  _.forEach(details, function (value, key) {
    mappedDetails.push({key: key, value: value});
  });

  return {entry: mappedDetails};
}

/**
 * we save the step counter on the jasmine suite object.
 * this method returns the increased step counter.
 */
function increaseAndGetStepCounter() {
  var spec = jasmine.getEnv().currentSpec;
  if (utils.isDefined(spec.scenariooStepCounter)) {
    spec.scenariooStepCounter++;
  } else {
    spec.scenariooStepCounter = 0;
  }
  return spec.scenariooStepCounter;
}

/**
 * returns an object containing information about the current scenario
 */
function getCurrentScenario() {
  var spec, suite;
  spec = jasmine.getEnv().currentSpec;
  suite = spec.suite;

  return {
    useCaseName: suite.description,
    scenarioName: spec.description,
    stepCounter: increaseAndGetStepCounter()
  };
}

/**
 * writes scenario.xml to the configured output folder
 */
function writeScenarioXml(currentScenario, absScenarioPath) {
  var data = {
    name: identifierSanitizer.sanitize(currentScenario.scenarioName),
    description: currentScenario.scenarioDescription,
    status: currentScenario.status
  };
  return xmlWriter.writeXmlFile('scenario', data, path.join(absScenarioPath, 'scenario.xml'));
}

/**
 * writes useCase.xml to the configured output folder
 */
function writeUseCaseXml(useCase, absUseCasePath) {
  var data = {
    name: identifierSanitizer.sanitize(useCase.name),
    description: useCase.description,
    status: useCase.status,
    details: {}
  };
  return xmlWriter.writeXmlFile('useCase', data, path.join(absUseCasePath, 'usecase.xml'));
}

function getStepDataFromWebpage() {

  var deferred = Q.defer();
  browser
    .getCurrentUrl()
    .then(function (currentUrl) {
      element(by.css('body')).getOuterHtml().then(function (pageHtmlSource) {
        deferred.resolve({url: currentUrl, source: pageHtmlSource});
      }, deferred.reject);
    }, deferred.reject);

  return deferred.promise;
}

function getPageNameFromUrl(urlString) {
  return identifierSanitizer.sanitize(pageNameExtractor.getPageNameFromUrl(urlString));
}

/**
 * writes step xml file (000.xml, 001.xml, etc.)
 */
function writeStepXml(stepName, currentScenario, absScenarioPath, details) {
  var deferred = Q.defer();

  getStepDataFromWebpage()
    .then(function (browserData) {
      var currentStepCounter = utils.leadingZeros(currentScenario.stepCounter);
      var pageName = getPageNameFromUrl(browserData.url);
      var stepData = {
        page: {
          name: pageName
        },
        stepDescription: {
          index: currentScenario.stepCounter,
          title: stepName,
          screenshotFileName: currentStepCounter + '.png',
          details: {}
        },
        html: {
          htmlSource: browserData.source
        },
        metadata: {
          visibleText: '',
          details: normalizeMetadataDetails(details)
        }
      };

      var xmlFileName = path.join(absScenarioPath, 'steps', currentStepCounter + '.xml');

      xmlWriter
        .writeXmlFile('step', stepData, xmlFileName)
        .then(function () {
          deferred.resolve(stepData);
        }, deferred.reject);
    }, deferred.reject)
    .catch(function (err) {
      deferred.reject(err);
    });

  return deferred.promise;
}

var ScenarioDocuWriter = {

  targetDir: '',

  buildOutputDir: undefined,

  /**
   * Use this to register your custom pageName function.
   * Scenarioo will pass in a node.js url object.
   */
  registerPageNameFunction: function (pageNameFunction) {
    pageNameExtractor.registerCustomExtractionFunction(pageNameFunction);
  },

  /**
   * Is invoked by the jasmine reporter at the beginning of the test run
   */
  start: function (branch, build, scenariooTargetDirectory) {
    this.branch = branch;
    this.branch.name = identifierSanitizer.sanitize(branch.name);
    this.targetDir = scenariooTargetDirectory;
    build.name = identifierSanitizer.sanitize(build.name);

    // generate directories and write branch.xml
    this.buildOutputDir = path.join(this.targetDir, utils.encodeFileName(this.branch.name), utils.encodeFileName(build.name));
    return xmlWriter.writeXmlFile('branch', this.branch, path.resolve(path.join(this.targetDir, utils.encodeFileName(this.branch.name)), 'branch.xml'));
  },

  /**
   * invoked by the jasmine reporter at the end of the test run
   */
  saveBuild: function (build) {
    this.build = build;
    this.build.name = identifierSanitizer.sanitize(this.build.name);
    return xmlWriter.writeXmlFile('build', this.build, path.join(this.buildOutputDir, 'build.xml'));
  },

  /**
   * invoked by the jasmine reporter at the end of each use case (jasmine suite)
   */
  saveUseCase: function (useCase) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absUseCasePath = path.resolve(this.buildOutputDir, utils.encodeFileName(useCase.name));
    useCase.name = identifierSanitizer.sanitize(useCase.name);
    return writeUseCaseXml(useCase, absUseCasePath);
  },

  /**
   * invoked by the jasmine reporter at the end of each scenario (jasmine spec)
   */
  saveScenario: function (currentScenario) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.encodeFileName(currentScenario.useCaseName),
      utils.encodeFileName(currentScenario.scenarioName));

    currentScenario.name = identifierSanitizer.sanitize(currentScenario.name);
    return writeScenarioXml(currentScenario, absScenarioPath);
  },

  /**
   * To be invoked in your e2e tests to save a step
   */
  saveStep: function (stepName, details) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var currentScenario = getCurrentScenario();
    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.encodeFileName(currentScenario.useCaseName),
      utils.encodeFileName(currentScenario.scenarioName));

    var screenshotPromise = screenshotSaver.saveScreenshot(currentScenario, absScenarioPath);
    var stepXmlPromise = writeStepXml(stepName, currentScenario, absScenarioPath, details);
    return Q.all([stepXmlPromise, screenshotPromise]);
  }

};

module.exports = ScenarioDocuWriter;
<commit_msg>[noissue] hm. was missing in previous commit.<commit_after>'use strict';

var
  path = require('path'),
  utils = require('./util'),
  identifierSanitizer = require('./identifierSanitizer'),
  entityValidator = require('./entityValidator'),
  _ = require('lodash'),
  xmlWriter = require('./xmlWriter'),
  pageNameExtractor = require('./pageNameExtractor'),
  screenshotSaver = require('./screenshotSaver'),
  Q = require('q');

function normalizeMetadataDetails(details) {
  if (_.isUndefined(details)) {
    return [];
  }
  if (_.isArray(details) || !_.isObject(details)) {
    throw new Error('Step metadata details must be an object!');
  }

  var mappedDetails = [];
  _.forEach(details, function (value, key) {
    mappedDetails.push({key: key, value: value});
  });

  return {entry: mappedDetails};
}

/**
 * we save the step counter on the jasmine suite object.
 * this method returns the increased step counter.
 */
function increaseAndGetStepCounter() {
  var spec = jasmine.getEnv().currentSpec;
  if (utils.isDefined(spec.scenariooStepCounter)) {
    spec.scenariooStepCounter++;
  } else {
    spec.scenariooStepCounter = 0;
  }
  return spec.scenariooStepCounter;
}

/**
 * returns an object containing information about the current scenario
 */
function getCurrentScenario() {
  var spec, suite;
  spec = jasmine.getEnv().currentSpec;
  suite = spec.suite;

  return {
    useCaseName: suite.description,
    scenarioName: spec.description,
    stepCounter: increaseAndGetStepCounter()
  };
}

/**
 * writes scenario.xml to the configured output folder
 */
function writeScenarioXml(currentScenario, absScenarioPath) {
  var data = {
    name: identifierSanitizer.sanitize(currentScenario.scenarioName),
    description: currentScenario.scenarioDescription,
    status: currentScenario.status
  };
  return xmlWriter.writeXmlFile('scenario', data, path.join(absScenarioPath, 'scenario.xml'));
}

/**
 * writes useCase.xml to the configured output folder
 */
function writeUseCaseXml(useCase, absUseCasePath) {
  var data = {
    name: identifierSanitizer.sanitize(useCase.name),
    description: useCase.description,
    status: useCase.status,
    details: {}
  };
  return xmlWriter.writeXmlFile('useCase', data, path.join(absUseCasePath, 'usecase.xml'));
}

function getStepDataFromWebpage() {

  var deferred = Q.defer();
  browser
    .getCurrentUrl()
    .then(function (currentUrl) {
      element(by.css('body')).getOuterHtml().then(function (pageHtmlSource) {
        deferred.resolve({url: currentUrl, source: pageHtmlSource});
      }, deferred.reject);
    }, deferred.reject);

  return deferred.promise;
}

function getPageNameFromUrl(urlString) {
  return identifierSanitizer.sanitize(pageNameExtractor.getPageNameFromUrl(urlString));
}

/**
 * writes step xml file (000.xml, 001.xml, etc.)
 */
function writeStepXml(stepName, currentScenario, absScenarioPath, details) {
  var deferred = Q.defer();

  getStepDataFromWebpage()
    .then(function (browserData) {
      var currentStepCounter = utils.leadingZeros(currentScenario.stepCounter);
      var pageName = getPageNameFromUrl(browserData.url);
      var stepData = {
        page: {
          name: pageName
        },
        stepDescription: {
          index: currentScenario.stepCounter,
          title: stepName,
          screenshotFileName: currentStepCounter + '.png',
          details: {}
        },
        html: {
          htmlSource: browserData.source
        },
        metadata: {
          visibleText: '',
          details: normalizeMetadataDetails(details)
        }
      };

      var xmlFileName = path.join(absScenarioPath, 'steps', currentStepCounter + '.xml');

      xmlWriter
        .writeXmlFile('step', stepData, xmlFileName)
        .then(function () {
          deferred.resolve(stepData);
        }, deferred.reject);
    }, deferred.reject)
    .catch(function (err) {
      deferred.reject(err);
    });

  return deferred.promise;
}

var ScenarioDocuWriter = {

  targetDir: '',

  buildOutputDir: undefined,

  /**
   * Use this to register your custom pageName function.
   * Scenarioo will pass in a node.js url object.
   */
  registerPageNameFunction: function (pageNameFunction) {
    pageNameExtractor.registerCustomExtractionFunction(pageNameFunction);
  },

  /**
   * Is invoked by the jasmine reporter at the beginning of the test run
   */
  start: function (branch, buildname, scenariooTargetDirectory) {
    entityValidator.validateBranch(branch);
    this.branch = branch;
    this.branch.name = identifierSanitizer.sanitize(branch.name);
    this.targetDir = scenariooTargetDirectory;

    var buildDirName = utils.encodeFileName(identifierSanitizer.sanitize(buildname));

    // generate directories and write branch.xml
    this.buildOutputDir = path.join(this.targetDir, utils.encodeFileName(this.branch.name), buildDirName);
    return xmlWriter.writeXmlFile('branch', this.branch, path.resolve(path.join(this.targetDir, utils.encodeFileName(this.branch.name)), 'branch.xml'));
  },

  /**
   * invoked by the jasmine reporter at the end of the test run
   */
  saveBuild: function (build) {
    entityValidator.validateBuild(build);
    build.name = identifierSanitizer.sanitize(build.name);
    return xmlWriter.writeXmlFile('build', build, path.join(this.buildOutputDir, 'build.xml'));
  },

  /**
   * invoked by the jasmine reporter at the end of each use case (jasmine suite)
   */
  saveUseCase: function (useCase) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    entityValidator.validateUseCase(useCase);

    var absUseCasePath = path.resolve(this.buildOutputDir, utils.encodeFileName(useCase.name));
    useCase.name = identifierSanitizer.sanitize(useCase.name);
    return writeUseCaseXml(useCase, absUseCasePath);
  },

  /**
   * invoked by the jasmine reporter at the end of each scenario (jasmine spec)
   */
  saveScenario: function (currentScenario, useCaseName) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    entityValidator.validateScenario(currentScenario);

    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.encodeFileName(useCaseName),
      utils.encodeFileName(currentScenario.name));

    currentScenario.name = identifierSanitizer.sanitize(currentScenario.name);
    return writeScenarioXml(currentScenario, absScenarioPath);
  },

  /**
   * To be invoked in your e2e tests to save a step
   */
  saveStep: function (stepName, details) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var currentScenario = getCurrentScenario();
    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.encodeFileName(currentScenario.useCaseName),
      utils.encodeFileName(currentScenario.scenarioName));

    var screenshotPromise = screenshotSaver.saveScreenshot(currentScenario, absScenarioPath);
    var stepXmlPromise = writeStepXml(stepName, currentScenario, absScenarioPath, details);
    return Q.all([stepXmlPromise, screenshotPromise]);
  }

};

module.exports = ScenarioDocuWriter;
<|endoftext|>


<commit_before>/*JavaScript file that provides the functionality of the yourview.org.au political alignmenet visualisation:
This is:
	- layout of scatterplot (output) and controls (input)
	- plots a scatterplot of json defined coordinates when the html page is first loaded
	- re-plots the scatterplot with fresh points when a slider value is changed

TODO:
	- Move from emulating the API return files wtih lical dummy files to actual API calls
	- Wire up sliders to write to file/POST etc. to send to server the tag weights
	- Implement Entities tab with buttons
	- Implement show/hide of plot point of the entity button clicked
	- Overall style tidy, prettify, tweaking
*/

// ~~~~~~~~~~~~~~~~~~~~~~~~~~d3 stuff~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

var DEBUG = true;
// For bug where text labels are only half their supposed x value
// See http://stackoverflow.com/questions/7000190/detect-all-firefox-versions-in-js
// for this solution.
var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

var points;
var userdata;
var data = [];
var w = 600
var h = 600
var entities;

var svg = d3.select("#scatterplot")
	.append("svg")
	.attr("width", w)
	.attr("height", h);

// Retrieve user data from userdata.json
d3.json("json/user_data.json", function(json) {
	userdata = json;
	retrievePoints();
});

// Retrieve points data from points.json

function retrievePoints() {
	d3.json("json/points.json", function(json) {
		points = json;
		data = mergeJSON(data);
		plot();
	});
}

// Merge the points and user data from the json files
// into a single object per datum/plot point/political entity

function mergeJSON(dataset) {

	// Add the user data to the points
	for (var i = 0; i < points.length; i++) {
		dataset.push({
			x: points[i][0],
			y: points[i][1],
			colour: userdata[i].colour,
			cred: userdata[i].cred,
			id: userdata[i].id,
			link: userdata[i].link,
			primary: userdata[i].primary,
			username: userdata[i].username
		});

	}

	if (DEBUG) console.log(data);

	return dataset;
}

// Scale scatter plot points to be spaced out better
// TODO: Generalise this to calculate the scaling factor
// based on range of values.

function scale(set) {
	for (var i = 0; i < set.length; i++) {
		set[i].x = (set[i].x + 5) * 50
		set[i].y = (set[i].y + 5) * 50
		// points[i][j] = (points[i][j] + 5) * 50;
	}
	return set
}

// For the time being, while the cross site access gets sorted,
// We will use dummy json files to emulate calls to the API
// This is the purpose of the function below.
var previousIndex;

function chooseRandDummyFile() {
	var pathArray = [];

	path1 = "json/dummy_points1.json";
	path2 = "json/dummy_points2.json";
	path3 = "json/dummy_points3.json";
	path4 = "json/dummy_points4.json";
	path5 = "json/dummy_points5.json";

	pathArray.push(path1);
	pathArray.push(path2);
	pathArray.push(path3);
	pathArray.push(path4);
	pathArray.push(path5);

	// Make sure we choose an index different to the last one.
	while (true) {
		index = Math.floor((Math.random() * 5) + 1);
		if (previousIndex != index) break;
	}

	previousIndex = index;
	// Adjust index for zero base
	index -= 1;

	if (DEBUG) console.log(index);

	return pathArray[index];

}

// Called when a slider is changed (see line: 13).
// ATM the JSON API is not used and is replaced with a few random JSON files of points.
// Also implements the transition between the old and new points in the scatter plot.

function update() {
	d3.json(chooseRandDummyFile(), function(json) {
		updatedData = new Array();
		points = json;
		updatedData = mergeJSON(updatedData);

		if (DEBUG) console.log(updatedData);

		// enter() and append() are omitted as we are using transision()
		svg.selectAll("circle")
			.data(scale(updatedData))
			.transition()
			.duration(1500)
			.attr("cx", function(d) {
			return d.x;
		})
			.attr("cy", function(d) {
			return d.y;
		})
			.style("stroke", "#000")
			.attr("r", function(d) {
			return 20;
		})
			.style("fill", function(d) {
			return d3.rgb(d.colour);
		});

		// Move the labels too
		updateLabels();

	});

}

// Called when the visualisation is first loaded.
// Binds the data to the circle svg elements and sets the attributes for a datum.

function plot() {
	entities = svg.selectAll("g")
		.data(scale(data))
		.enter()
		.append("g")
		.on("mouseover", function(d) {
		var sel = d3.select(this);
		sel.moveToFront();
		console.log(d.username);
	});

	entities.append("circle")
		.attr("cx", function(d) {
		return d.x;
	})
		.attr("cy", function(d) {
		return d.y;
	})
		.style("fill", function(d) {
		return d.colour;
	})
		.style("stroke", "#000")
		.style("stroke-width", 2 + "px")
		.transition()
		.duration(800)
		.attr("r", function(d) {
		return 20;
	});

	entities.append("text")
		.attr("dx", function(d) {
		if (is_firefox) return d.x * 2;
		else return d.x;
	})
		.attr("dy", function(d) {
		return d.y + 40;
	})
		.attr("font-family", "sans-serif")
		.attr("font-size", "13px")
		.style("text-anchor", "middle")
		.text(function(d) {
		return d.username;
	});
}

// Animates the labels

function updateLabels() {
	svg.selectAll("text")
		.data(updatedData)
		.transition()
		.duration(1500)
		.attr("dx", function(d) {
		if (is_firefox) return d.x * 2;
		return d.x;
	})
		.attr("dy", function(d) {
		return d.y + 40;
	})
		.attr("font-family", "sans-serif")
		.attr("font-size", "13px")
		.style("text-anchor", "middle")
		.text(function(d) {
		return d.username;
	});
}

// Prototype of moveToFront to move an obscured plot point to the front.
// See mouse over on line: 183
d3.selection.prototype.moveToFront = function() {
	return this.each(function() {
		this.parentNode.appendChild(this);
	});
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~jQuery stuff~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create tabs
$(function() {
	$("#tabs").tabs();
});

// Create sliders for the Areas tab with on stop callback to update the plot
$(function() {
	$("#slider1").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider2").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider3").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider4").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider5").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
});<commit_msg>Removed stroke and reduce circle size.<commit_after>/*JavaScript file that provides the functionality of the yourview.org.au political alignmenet visualisation:
This is:
	- layout of scatterplot (output) and controls (input)
	- plots a scatterplot of json defined coordinates when the html page is first loaded
	- re-plots the scatterplot with fresh points when a slider value is changed

TODO:
	- Move from emulating the API return files wtih lical dummy files to actual API calls
	- Wire up sliders to write to file/POST etc. to send to server the tag weights
	- Implement Entities tab with buttons
	- Implement show/hide of plot point of the entity button clicked
	- Overall style tidy, prettify, tweaking
*/

// ~~~~~~~~~~~~~~~~~~~~~~~~~~d3 stuff~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

var DEBUG = true;
// For bug where text labels are only half their supposed x value
// See http://stackoverflow.com/questions/7000190/detect-all-firefox-versions-in-js
// for this solution.
var is_firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

var points;
var userdata;
var data = [];
var w = 600
var h = 600
var entities;

var svg = d3.select("#scatterplot")
	.append("svg")
	.attr("width", w)
	.attr("height", h);

// Retrieve user data from userdata.json
d3.json("json/user_data.json", function(json) {
	userdata = json;
	retrievePoints();
});

// Retrieve points data from points.json

function retrievePoints() {
	d3.json("json/points.json", function(json) {
		points = json;
		data = mergeJSON(data);
		plot();
	});
}

// Merge the points and user data from the json files
// into a single object per datum/plot point/political entity

function mergeJSON(dataset) {

	// Add the user data to the points
	for (var i = 0; i < points.length; i++) {
		dataset.push({
			x: points[i][0],
			y: points[i][1],
			colour: userdata[i].colour,
			cred: userdata[i].cred,
			id: userdata[i].id,
			link: userdata[i].link,
			primary: userdata[i].primary,
			username: userdata[i].username
		});

	}

	if (DEBUG) console.log(data);

	return dataset;
}

// Scale scatter plot points to be spaced out better
// TODO: Generalise this to calculate the scaling factor
// based on range of values.

function scale(set) {
	for (var i = 0; i < set.length; i++) {
		set[i].x = (set[i].x + 5) * 50
		set[i].y = (set[i].y + 5) * 50
		// points[i][j] = (points[i][j] + 5) * 50;
	}
	return set
}

// For the time being, while the cross site access gets sorted,
// We will use dummy json files to emulate calls to the API
// This is the purpose of the function below.
var previousIndex;

function chooseRandDummyFile() {
	var pathArray = [];

	path1 = "json/dummy_points1.json";
	path2 = "json/dummy_points2.json";
	path3 = "json/dummy_points3.json";
	path4 = "json/dummy_points4.json";
	path5 = "json/dummy_points5.json";

	pathArray.push(path1);
	pathArray.push(path2);
	pathArray.push(path3);
	pathArray.push(path4);
	pathArray.push(path5);

	// Make sure we choose an index different to the last one.
	while (true) {
		index = Math.floor((Math.random() * 5) + 1);
		if (previousIndex != index) break;
	}

	previousIndex = index;
	// Adjust index for zero base
	index -= 1;

	if (DEBUG) console.log(index);

	return pathArray[index];

}

// Called when a slider is changed (see line: 13).
// ATM the JSON API is not used and is replaced with a few random JSON files of points.
// Also implements the transition between the old and new points in the scatter plot.

function update() {
	d3.json(chooseRandDummyFile(), function(json) {
		updatedData = new Array();
		points = json;
		updatedData = mergeJSON(updatedData);

		if (DEBUG) console.log(updatedData);

		// enter() and append() are omitted as we are using transision()
		svg.selectAll("circle")
			.data(scale(updatedData))
			.transition()
			.duration(1500)
			.attr("cx", function(d) {
			return d.x;
		})
			.attr("cy", function(d) {
			return d.y;
		})
			.attr("r", function(d) {
			return 15;
		})
			.style("fill", function(d) {
			return d3.rgb(d.colour);
		});

		// Move the labels too
		updateLabels();

	});

}

// Called when the visualisation is first loaded.
// Binds the data to the circle svg elements and sets the attributes for a datum.

function plot() {
	entities = svg.selectAll("g")
		.data(scale(data))
		.enter()
		.append("g")
		.on("mouseover", function(d) {
		var sel = d3.select(this);
		sel.moveToFront();
		console.log(d.username);
	});

	entities.append("circle")
		.attr("cx", function(d) {
		return d.x;
	})
		.attr("cy", function(d) {
		return d.y;
	})
		.style("fill", function(d) {
		return d.colour;
	})
		.transition()
		.duration(800)
		.attr("r", function(d) {
		return 15;
	});

	entities.append("text")
		.attr("dx", function(d) {
		if (is_firefox) return d.x * 2;
		else return d.x;
	})
		.attr("dy", function(d) {
		return d.y + 40;
	})
		.attr("font-family", "sans-serif")
		.attr("font-size", "13px")
		.style("text-anchor", "middle")
		.text(function(d) {
		return d.username;
	});
}

// Animates the labels

function updateLabels() {
	svg.selectAll("text")
		.data(updatedData)
		.transition()
		.duration(1500)
		.attr("dx", function(d) {
		if (is_firefox) return d.x * 2;
		return d.x;
	})
		.attr("dy", function(d) {
		return d.y + 40;
	})
		.attr("font-family", "sans-serif")
		.attr("font-size", "13px")
		.style("text-anchor", "middle")
		.text(function(d) {
		return d.username;
	});
}

// Prototype of moveToFront to move an obscured plot point to the front.
// See mouse over on line: 183
d3.selection.prototype.moveToFront = function() {
	return this.each(function() {
		this.parentNode.appendChild(this);
	});
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~jQuery stuff~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create tabs
$(function() {
	$("#tabs").tabs();
});

// Create sliders for the Areas tab with on stop callback to update the plot
$(function() {
	$("#slider1").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider2").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider3").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider4").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
	$("#slider5").slider().slider("option", "min", -1).slider({
		max: 1
	}).on("slidestop", function(event, ui) {
		update();
	});
});<|endoftext|>


<commit_before>var sys        = require('sys'),
    path       = require('path'),
    http       = require('http'),
    formidable = require('./lib/formidable'),
    paperboy   = require('./lib/paperboy');

var PUBLIC = path.join(path.dirname(__filename), 'public');

var progresses = {}
var metadata   = {}

http.createServer(function(req, res) {

  // parse an upload using formidable.
  regex = new RegExp('/upload/(.+)');
  match = regex.exec(req.url);
  if (match && req.method.toLowerCase() == 'post') {
    var uuid = match[1];
    sys.print("receiving upload: "+uuid+'\n');

    var form = new formidable.IncomingForm();
    form.uploadDir = './data';
    form.keepExtensions = true;

    // keep track of progress.
    form.addListener('progress', function(recvd, expected) {
      progress = (recvd / expected * 100).toFixed(2);
      progresses[uuid] = progress
    });

    form.parse(req, function(error, fields, files) {
      var path     = files['file']['path'],
          filename = files['file']['filename'],
          mime     = files['file']['mime'];
      res.writeHead(200, {'content-type': 'text/html'});
      //res.write('<textarea>');
      res.write("upload complete.\n");
      res.write(filename + ':filename\n' + path + ':path\n');
      sys.print('Users file: '+filename + ':filename\nIs server file: ' + path + ':path\n');
      //res.write('</textarea>')
      res.end()
      sys.print("finished upload: "+uuid+'\n');
    });

    return;
  }

  // (update) metadata
  regex = new RegExp('/update/(.+)');
  match = regex.exec(req.url);
  if (match && req.method.toLowerCase() == 'post') {
    uuid = match[1]
    var form = new formidable.IncomingForm();
    form.addListener('field', function(name, value) {
      sys.print("fresh metadata for "+uuid+": "+name+" => "+value+"\n")
      metadata[name] = value;
    });
    form.parse(req);
  }

  // respond to progress queries.
  regex = new RegExp('/progress/(.+)');
  match = regex.exec(req.url);
  if (match) {
    uuid = match[1];
    res.writeHead(200, {'content-type': 'application/json'});
    res.write(JSON.stringify({'progress': progresses[uuid]}));
    res.end();
  }

  // let paperboy handle any static content.
  paperboy
    .deliver(PUBLIC, req, res)
    .after(function(statCode) {
      sys.log('Served Request: ' + statCode + ' ' + req.url)
    })
    .otherwise(function() {
      res.writeHead(404, {'Content-Type': 'text/plain'});
      res.write('Not Found');
      res.end();
    });

}).listen(8126);

sys.log('ready at http://localhost:8126/')
<commit_msg>distinguished treatment of srt and mp3<commit_after>var sys        = require('sys'),
    path       = require('path'),
    http       = require('http'),
    formidable = require('./lib/formidable'),
    paperboy   = require('./lib/paperboy');

var PUBLIC = path.join(path.dirname(__filename), 'public');

var progresses = {}
var metadata   = {}

http.createServer(function(req, res) {
  regex = new RegExp('/upload/(.+).srt');
  match = regex.exec(req.url);
  if (match && req.method.toLowerCase() == 'post') {
    var uuid = match[1];
    sys.print("Receiving transcription request: "+uuid+'\n');

    var form = new formidable.IncomingForm();
    form.uploadDir = './data';
    form.keepExtensions = true;

    // keep track of progress.
    form.addListener('progress', function(recvd, expected) {
      progress = (recvd / expected * 100).toFixed(2);
      progresses[uuid] = progress;
    });

    form.parse(req, function(error, fields, files) {
      var path     = files['file']['path'],
          filename = files['file']['filename'],
          mime     = files['file']['mime'];
      res.writeHead(200, {'content-type': 'text/html'});
      res.write("Transcription underway.\n");
      res.write(filename + ':filename\n' + path + ':path\n');
      sys.print('Users file: '+filename + ':filename\nIs server file: ' + path +  ':path\n');
      /*TODO open the coressponding _finished.srt and paste its contents in the
       * response res
       */
      res.write("Here are the contents of the file");
      res.end();
      sys.print("Transcription returned to client: "+uuid+'\n');

    });

    return;
  }
  // parse an upload using formidable.
  regex = new RegExp('/upload/(.+).mp3');
  match = regex.exec(req.url);
  if (match && req.method.toLowerCase() == 'post') {
    var uuid = match[1];
    sys.print("receiving upload: "+uuid+'\n');

    var form = new formidable.IncomingForm();
    form.uploadDir = './data';
    form.keepExtensions = true;

    // keep track of progress.
    form.addListener('progress', function(recvd, expected) {
      progress = (recvd / expected * 100).toFixed(2);
      progresses[uuid] = progress
    });

    form.parse(req, function(error, fields, files) {
      var path     = files['file']['path'],
          filename = files['file']['filename'],
          mime     = files['file']['mime'];
      res.writeHead(200, {'content-type': 'text/html'});
      //res.write('<textarea>');
      res.write("Upload complete.\n");
      res.write(filename + ':filename\n' + path + ':path\n');
      sys.print('Users file: '+filename + ':filename\nIs server file: ' + path + ':path\n');
      //res.write('</textarea>')
      res.end()
      sys.print("finished upload: "+uuid+'\n');
    });
    /*
     * TODO rename file to original filename?
     * /

    /*
     * TODO turn into raw PCM
     */

    /*
     * TODO run though pocketshinx
     */

    return;
  }

  // (update) metadata
  regex = new RegExp('/update/(.+)');
  match = regex.exec(req.url);
  if (match && req.method.toLowerCase() == 'post') {
    uuid = match[1];
    var form = new formidable.IncomingForm();
    form.addListener('field', function(name, value) {
      sys.print("fresh metadata for "+uuid+": "+name+" => "+value+"\n")
      metadata[name] = value;
    });
    form.parse(req);
  }

  // respond to progress queries.
  regex = new RegExp('/progress/(.+)');
  match = regex.exec(req.url);
  if (match) {
    uuid = match[1];
    res.writeHead(200, {'content-type': 'application/json'});
    res.write(JSON.stringify({'progress': progresses[uuid]}));
    res.end();
  }

  // let paperboy handle any static content.
  paperboy
    .deliver(PUBLIC, req, res)
    .after(function(statCode) {
      sys.log('Served Request: ' + statCode + ' ' + req.url)
    })
    .otherwise(function() {
      res.writeHead(404, {'Content-Type': 'text/plain'});
      res.write('Not Found');
      res.end();
    });

}).listen(8126);

sys.log('ready at http://localhost:8126/')
<|endoftext|>


<commit_before>/* http://developer.apple.com/safari/library/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/UsingtheJavascriptDatabase/UsingtheJavascriptDatabase.html */

function Migrator(db) {
    var migrations = [];
    var doReload = false;
    this.migration = function(number, func){
        migrations[number] = func;
    };
    var doMigration = function(number){
        if(migrations[number]){
            doReload = true;
            db.changeVersion(db.version, String(number), function(t){
                migrations[number](t);
            }, function(err){
                debug.log('DB changeVersion failed: ' + err.message);
            }, function(){
                debug.log('DB migration to ' + String(number) + ' successfull');
                doMigration(number+1);
            });
        } else {
            // migration done, reload
            if(doReload) {
                debug.log('reloading');
                window.location.reload(true);
            }
        }
    };
    this.doIt = function(){
        var initialVersion = parseInt(db.version) || 0;
        try {
            doMigration(initialVersion+1);
        } catch(e) {
            debug.log(e);
        }
    }
}

RadioDb = function() {

    var that = this;

    var db = null;

    var defaultStations = [
        [ "DRS 1"     , "http://glb-stream11.streamserver.ch/1/regi_zh_sh/mp3_128", "drs1.png"],
        [ "DRS 3"     , "http://zlz-stream10.streamserver.ch/1/drs3/mp3_128", "drs3.png"],
        [ "DRS Virus"     , "http://zlz-stream12.streamserver.ch/1/drsvirus/mp3_128", "drsvirus.png"],
        [ "Couleur 3", "http://broadcast.infomaniak.net:80/rsr-couleur3-high.mp3", ""],
        [ "Radio 1", "http://stream.radio1.ch:8000/radio1", "drs3.png"],
        [ 'Radio 24', 'http://s3.global-streaming.net:8000/listen.pls', ''],
        ['Energy Zürich', 'http://broadcast.infomaniak.net/energyzuerich-high.mp3.pls', ''],
        ['Radio Argovia', 'http://shoutcast.argovia.ch:8060/listen.pls', ''],
        ['Radio Pilatus', 'http://www.radiopilatus.ch/streams/pilatus128.pls', ''],

        ];

    var ERR_NONDB = 0;
    var ERR_OTHER = 1;
    var ERR_VERSION_MISMATCH = 2;
    var ERR_RESULTSIZE_EXCEEDED = 3;
    var ERR_MAXSIZE_EXCEEDED = 4;
    var ERR_LOCK_ERROR = 5; // contention
    var ERR_CONSTRAINT = 6;

    var upgradeSuccess = function() { debug.log('Successfully upgraded db.'); };
    var upgradeError = function(error) { debug.log('Failed upgrading db.'); return true; /* treat all errs as fatal */ };
    /** Return TRUE for fatal error */
    var errorHandler = function(t, error) {
        if(error.code == ERR_VERSION_MISMATCH) {
            // well, we just log&ignore it.. safari doesn't see the changeVersion effect until restart Oo
            debug.log('ERR_VERSION_MISMATCH');
            return false;
        }
        alert('Whoops, DB error: ' + error.message + ' (' + error.code + ')');
        return true; // fatal error
    };

    /** This is used as a data handler for a request that should return no data. */
    var nullDataHandler = function(t, results) { };

    var insertDefaults = function(t) {
        // insert default data
        var query = 'INSERT INTO stations(name, stream, logo, listened_at) VALUES(?, ?, ?, NULL);';
        for( var i=0; i < defaultStations.length; ++i ) {
            t.executeSql(query, defaultStations[i], nullDataHandler, errorHandler);
        }
    };

    this.init = function() {
        try {
            if(!window.openDatabase) {
                alert('Ihr Browser unterstützt kein Local Storage');
            } else {
                db = openDatabase('radios_db', '', 'Radios Database', 524288); // 512KiB
                debug.log('Current DB Version: ' + db.version);
                var M = new Migrator(db);
                M.migration(1, function(t) {
                    t.executeSql('CREATE TABLE stations(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE ON CONFLICT IGNORE, stream TEXT NOT NULL, logo TEXT NOT NULL, listened_at DATE )',
                        [], nullDataHandler, errorHandler);
                    insertDefaults(t);
                });
                M.doIt();
            }
            return db;
        } catch(e) {
            if(e.code == ERR_VERSION_MISMATCH) {
                debug.log('ERR_VERSION_MISMATCH');
            } else {
                alert('Error: ' + e);
            }
        }
    };

    this.updateListenAt = function(id) {
        db.transaction(function(t) {
            t.executeSql('UPDATE stations SET listened_at = datetime("now","localtime") WHERE id = ?', [ id ], nullDataHandler, errorHandler);
        });
    };

    /** Returns station that user listened to last. */
    this.getLastListenedStation = function(resultHandler) {
        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations ORDER BY listened_at DESC LIMIT 1", [] , function(t, results) {
                resultHandler( results.rows.length == 0 ? null : results.rows.item(0) );
            }, errorHandler);
        });
    };

    var getStation = function(id, resultHandler, doUpdateListen) {
        if( doUpdateListen )
            that.updateListenAt(id);

        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations WHERE id = ?", [ id ] , function(t, results) {
                resultHandler( results.rows.length == 0 ? null : results.rows.item(0) );
            }, errorHandler);
        });
    };

    var getStations = function(resultHandler, filter) {
        var where = '';
        if ( filter ) {
            where = " WHERE name LIKE '%" + filter + "%';"; // FIXME EEEEESCAPE!
        }
        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations " + where + " ORDER BY name ASC", [ ] , function(t, results) {
                resultHandler(results.rows);
            }, errorHandler);
        });
    };

    this.insertStation = function(station) {
        db.transaction(function(t) {
            t.executeSql('INSERT INTO stations(name, stream, logo, listened_at) VALUES(?, ?, ?, NULL);', [ station.name, station.stream, station.logo ], nullDataHandler, errorHandler);
        });
    };

    this.autoSearch = function() {
        var searchel = document.getElementById('search-field');
        searchel.onkeyup = function(ev) {
            that.populateStations(searchel.value);
        };
    };

    this.populateStations = function(filter) {
        getStations(function(stations) {
            var ul = document.createElement('ul');
            var li, txt;
            var playing = document.createElement('span');
            var playingOn = document.createTextNode('On');
            playing.setAttribute('class', 'playing');
            playing.appendChild(playingOn);

            for( var i=0; i<stations.length; ++i ) {
                li = document.createElement('li');
                txt = document.createTextNode(stations.item(i).name);
                li.appendChild(txt);
                li.setAttribute('id', 'station-'+ stations.item(i).id);
                if (stations.item(i).name == "DRS 3") {
                    li.appendChild(playing);
                }
                li.onclick = function(ev) {

                    var id = this.getAttribute('id').split('-')[1];
                    getStation(id, function(station) {
                        document.getElementById('station-'+station.id).appendChild(playing);
                        radio.station = station.name;
                        radio.clear();
                        radio.noTrack(true);
                        playSound(station.stream);
                        // remove mute
                        document.getElementById("mute").setAttribute('class', '');
                    }, true);

                };
                ul.appendChild(li);
            }
            // remove current nav
            var statel = document.getElementById("stations");
            if( statel.hasChildNodes() ) {
                while( statel.childNodes.length >= 1 )
                    statel.removeChild(statel.firstChild);
            }
            // append updated nav
            statel.appendChild(ul);
        }, filter);
    };
};

<commit_msg>No more needed.<commit_after>/* http://developer.apple.com/safari/library/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/UsingtheJavascriptDatabase/UsingtheJavascriptDatabase.html */

function Migrator(db) {
    var migrations = [];
    var doReload = false;
    this.migration = function(number, func){
        migrations[number] = func;
    };
    var doMigration = function(number){
        if(migrations[number]){
            doReload = true;
            db.changeVersion(db.version, String(number), function(t){
                migrations[number](t);
            }, function(err){
                debug.log('DB changeVersion failed: ' + err.message);
            }, function(){
                debug.log('DB migration to ' + String(number) + ' successfull');
                doMigration(number+1);
            });
        } else {
            // migration done, reload
            if(doReload) {
                debug.log('reloading');
                window.location.reload(true);
            }
        }
    };
    this.doIt = function(){
        var initialVersion = parseInt(db.version) || 0;
        try {
            doMigration(initialVersion+1);
        } catch(e) {
            debug.log(e);
        }
    }
}

RadioDb = function() {

    var that = this;

    var db = null;

    var defaultStations = [
        [ "DRS 1"     , "http://glb-stream11.streamserver.ch/1/regi_zh_sh/mp3_128", "drs1.png"],
        [ "DRS 3"     , "http://zlz-stream10.streamserver.ch/1/drs3/mp3_128", "drs3.png"],
        [ "DRS Virus"     , "http://zlz-stream12.streamserver.ch/1/drsvirus/mp3_128", "drsvirus.png"],
        [ "Couleur 3", "http://broadcast.infomaniak.net:80/rsr-couleur3-high.mp3", ""],
        [ "Radio 1", "http://stream.radio1.ch:8000/radio1", "drs3.png"],
        [ 'Radio 24', 'http://s3.global-streaming.net:8000/listen.pls', ''],
        ['Energy Zürich', 'http://broadcast.infomaniak.net/energyzuerich-high.mp3.pls', ''],
        ['Radio Argovia', 'http://shoutcast.argovia.ch:8060/listen.pls', ''],
        ['Radio Pilatus', 'http://www.radiopilatus.ch/streams/pilatus128.pls', ''],

        ];

    var ERR_NONDB = 0;
    var ERR_OTHER = 1;
    var ERR_VERSION_MISMATCH = 2;
    var ERR_RESULTSIZE_EXCEEDED = 3;
    var ERR_MAXSIZE_EXCEEDED = 4;
    var ERR_LOCK_ERROR = 5; // contention
    var ERR_CONSTRAINT = 6;

    var upgradeSuccess = function() { debug.log('Successfully upgraded db.'); };
    var upgradeError = function(error) { debug.log('Failed upgrading db.'); return true; /* treat all errs as fatal */ };
    /** Return TRUE for fatal error */
    var errorHandler = function(t, error) {
        if(error.code == ERR_VERSION_MISMATCH) {
            // well, we just log&ignore it.. safari doesn't see the changeVersion effect until restart Oo
            debug.log('ERR_VERSION_MISMATCH');
            return false;
        }
        alert('Whoops, DB error: ' + error.message + ' (' + error.code + ')');
        return true; // fatal error
    };

    /** This is used as a data handler for a request that should return no data. */
    var nullDataHandler = function(t, results) { };

    var insertDefaults = function(t) {
        // insert default data
        var query = 'INSERT INTO stations(name, stream, logo, listened_at) VALUES(?, ?, ?, NULL);';
        for( var i=0; i < defaultStations.length; ++i ) {
            t.executeSql(query, defaultStations[i], nullDataHandler, errorHandler);
        }
    };

    this.init = function() {
        try {
            if(!window.openDatabase) {
                alert('Ihr Browser unterstützt kein Local Storage');
            } else {
                db = openDatabase('radios_db', '', 'Radios Database', 524288); // 512KiB
                debug.log('Current DB Version: ' + db.version);
                var M = new Migrator(db);
                M.migration(1, function(t) {
                    t.executeSql('CREATE TABLE stations(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE ON CONFLICT IGNORE, stream TEXT NOT NULL, logo TEXT NOT NULL, listened_at DATE )',
                        [], nullDataHandler, errorHandler);
                    insertDefaults(t);
                });
                M.doIt();
            }
            return db;
        } catch(e) {
            if(e.code == ERR_VERSION_MISMATCH) {
                debug.log('ERR_VERSION_MISMATCH');
            } else {
                alert('Error: ' + e);
            }
        }
    };

    this.updateListenAt = function(id) {
        db.transaction(function(t) {
            t.executeSql('UPDATE stations SET listened_at = datetime("now","localtime") WHERE id = ?', [ id ], nullDataHandler, errorHandler);
        });
    };

    /** Returns station that user listened to last. */
    this.getLastListenedStation = function(resultHandler) {
        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations ORDER BY listened_at DESC LIMIT 1", [] , function(t, results) {
                resultHandler( results.rows.length == 0 ? null : results.rows.item(0) );
            }, errorHandler);
        });
    };

    var getStation = function(id, resultHandler, doUpdateListen) {
        if( doUpdateListen )
            that.updateListenAt(id);

        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations WHERE id = ?", [ id ] , function(t, results) {
                resultHandler( results.rows.length == 0 ? null : results.rows.item(0) );
            }, errorHandler);
        });
    };

    var getStations = function(resultHandler, filter) {
        var where = '';
        if ( filter ) {
            where = " WHERE name LIKE '%" + filter + "%';"; // FIXME EEEEESCAPE!
        }
        db.transaction(function(t) {
            t.executeSql("SELECT id, name, stream, logo, listened_at FROM stations " + where + " ORDER BY name ASC", [ ] , function(t, results) {
                resultHandler(results.rows);
            }, errorHandler);
        });
    };

    this.insertStation = function(station) {
        db.transaction(function(t) {
            t.executeSql('INSERT INTO stations(name, stream, logo, listened_at) VALUES(?, ?, ?, NULL);', [ station.name, station.stream, station.logo ], nullDataHandler, errorHandler);
        });
    };

    this.autoSearch = function() {
        var searchel = document.getElementById('search-field');
        searchel.onkeyup = function(ev) {
            that.populateStations(searchel.value);
        };
    };

    this.populateStations = function(filter) {
        getStations(function(stations) {
            var ul = document.createElement('ul');
            var li, txt;
            var playing = document.createElement('span');
            var playingOn = document.createTextNode('On');
            playing.setAttribute('class', 'playing');
            playing.appendChild(playingOn);

            for( var i=0; i<stations.length; ++i ) {
                li = document.createElement('li');
                txt = document.createTextNode(stations.item(i).name);
                li.appendChild(txt);
                li.setAttribute('id', 'station-'+ stations.item(i).id);
                li.onclick = function(ev) {

                    var id = this.getAttribute('id').split('-')[1];
                    getStation(id, function(station) {
                        document.getElementById('station-'+station.id).appendChild(playing);
                        radio.station = station.name;
                        radio.clear();
                        radio.noTrack(true);
                        playSound(station.stream);
                        // remove mute
                        document.getElementById("mute").setAttribute('class', '');
                    }, true);

                };
                ul.appendChild(li);
            }
            // remove current nav
            var statel = document.getElementById("stations");
            if( statel.hasChildNodes() ) {
                while( statel.childNodes.length >= 1 )
                    statel.removeChild(statel.firstChild);
            }
            // append updated nav
            statel.appendChild(ul);
        }, filter);
    };
};

<|endoftext|>