<commit_before>
import { NORMAL, SPLAY, SURFACE } from '../core/constants.js';
import { Tree } from '../core/Tree.js';

function loxHandler  ( fileName, dataStream ) {

	this.fileName          = fileName;
	this.entrances         = [];
	this.terrain           = [];
	this.terrainBitmap     = "";
	this.terrainDimensions = {};
	this.scraps            = [];
	this.faults            = [];
	this.lineSegments      = [];
	this.sections          = new Map();
	this.surveyTree        = new Tree( "", 0 );
	this.isRegion		   = false;

	var lineSegments = [];
	var xSects       = [];
	var stations     = [];
	var lines        = 0;
	var samples      = 0;
	var self         = this;
	var surveyTree   = this.surveyTree;

	// assumes little endian data ATM - FIXME

	var source = dataStream;
	var pos = 0; // file position
	var dataStart;
	var f = new DataView( source, 0 );
	var l = source.byteLength;

	while ( pos < l ) {

		readChunkHdr();

	}

	this.lineSegments = lineSegments;

	// Drop data to give GC a chance ASAP
	source = null;

	// strip empty/single top nodes of tree
	this.surveyTree = surveyTree.reduce( "unknown" );

	return;

	// .lox parsing functions

	function readChunkHdr () {

		var m_type     = readUint();
		var m_recSize  = readUint();
		var m_recCount = readUint();
		var m_dataSize = readUint();
		var doFunction;

		// offset of data region for out of line strings/images/scrap data.
		dataStart  = pos + m_recSize;

		switch ( m_type ) {

		case 1:

			doFunction = readSurvey;

			break;

		case 2:

			doFunction = readStation;

			break;

		case 3:

			doFunction = readShot;

			break;

		case 4:

			doFunction = readScrap;

			break;

		case 5:

			doFunction = readSurface;

			break;

		case 6:

			doFunction = readSurfaceBMP;

			break;

		default:

			console.log( "unknown chunk header. type : ", m_type );

		}

		if ( doFunction !== undefined) {

			for ( var i = 0; i < m_recCount; i++ ) {

				doFunction( i );

			}

		}

		skipData( m_dataSize );
	}

	function readUint () {

		var i = f.getUint32( pos, true );

		pos += 4;

		return i;

	}

	function skipData ( i ) {

		pos += i;

	}

	function readSurvey ( i ) {

		var m_id     = readUint();
		var namePtr  = readDataPtr();
		var m_parent = readUint();
		var titlePtr = readDataPtr();

		if ( m_parent != m_id && !surveyTree.addById( readString( namePtr ), m_id, m_parent ) ) {

			console.log( "error constructing survey tree" );

		}

	}

	function readDataPtr() {

		var m_position = readUint();
		var m_size     = readUint();

		return { position: m_position, size: m_size };

	}

	function readString ( ptr ) {

		var bytes = new Uint8Array( source, dataStart + ptr.position, ptr.size );

		return String.fromCharCode.apply( null, bytes );

	}

	function readStation () {

		var m_id       = readUint();
		var m_surveyId = readUint();
		var namePtr    = readDataPtr();

		readDataPtr(); // commentPtr

		var m_flags    = readUint();
		var coords     = readCoords();

		stations[ m_id ]  = coords;

		// add non surface stations to surveyTree

		if ( ! ( m_flags & 0x01 ) ) surveyTree.addById( readString( namePtr ),  m_id, m_surveyId, { p: coords } );

		if ( m_flags & 0x02 ) {

			// entrance
			self.entrances.push( { position: coords, label: readString(namePtr), survey: m_surveyId } );

		}

	}

	function readCoords () {

		var f = new DataView( source, pos );
		var coords = {};

		coords.x = f.getFloat64( 0,  true );
		coords.y = f.getFloat64( 8,  true );
		coords.z = f.getFloat64( 16, true );
		pos += 24;

		return coords;

	}

	function readShot () {

		var m_from = readUint();
		var m_to   = readUint();

		var fromLRUD = readLRUD();
		var toLRUD   = readLRUD();

		var m_flags       = readUint();
		var m_sectionType = readUint();
		var m_surveyId    = readUint();
		var m_threshold   = f.getFloat64( pos, true );
		var type          = NORMAL;

		pos += 8;

		if ( m_flags && 0x01 ) type = SURFACE;
		if ( m_flags && 0x08 ) type = SPLAY;

		if ( m_flags === 0x16 ) {

			xSects[ m_from ] = fromLRUD;
			xSects[ m_to ]   = toLRUD;

		}

		lineSegments.push( { from: stations[ m_from ], to: stations[ m_to ], type: type, survey: m_surveyId } );

	}

	function readLRUD () {

		var f          = new DataView( source, pos );
		var L		   = f.getFloat64( 0,  true );
		var R		   = f.getFloat64( 8,  true );
		var U		   = f.getFloat64( 16, true );
		var D   	   = f.getFloat64( 24, true );

		pos += 32;

		return { l: L, r: R, u: U, d: D };

	}

	function readScrap () {

		var m_id         = readUint();
		var m_surveyId   = readUint();

		var m_numPoints  = readUint();
		var pointsPtr    = readDataPtr();

		var m_num3Angles = readUint();
		var facesPtr     = readDataPtr();

		var scrap = { vertices: [], faces: [], survey: m_surveyId };
		var lastFace;
		var i;

		for ( i = 0; i < m_numPoints; i++ ) {

			var offset = dataStart + pointsPtr.position + i * 24; // 24 = 3 * sizeof(double)
			var f = new DataView( source, offset );
			var vertex = {};

			vertex.x = f.getFloat64( 0,  true );
			vertex.y = f.getFloat64( 8,  true );
			vertex.z = f.getFloat64( 16, true );

			scrap.vertices.push( vertex );

		}

		// read faces from out of line data area

		for ( i = 0; i < m_num3Angles; i++ ) {

			var offset = dataStart + facesPtr.position + i * 12; // 12 = 3 * sizeof(uint32)
			var f = new DataView( source, offset );
			var face = [];

			face[0] = f.getUint32( 0, true );
			face[1] = f.getUint32( 4, true );
			face[2] = f.getUint32( 8, true );

			// check for face winding order == orientation

			fix_direction: { if ( lastFace !== undefined ) {

				var j;

				for ( j = 0; j < 3; j++ ) { // this case triggers more often than those below.

					if (face[ j ] == lastFace[ ( j + 2 ) % 3 ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 3 ) % 3 ] ) {

						face.reverse();
						break fix_direction;

					}

				}

				for ( j = 0; j < 3; j++ ) {

					if ( face[ j ] == lastFace[ j ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 1 ) % 3 ] ) {

						face.reverse();
						break fix_direction;

					}

				}

				for ( j = 0; j < 3; j++ ) {

					if ( face[ j ] == lastFace[ ( j + 1 ) % 3 ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 2 ) % 3] ) {

						face.reverse();
						break fix_direction;

					}

				}
			} }

			scrap.faces.push( face );
			lastFace = face;
		}

		self.scraps.push( scrap );

	}

	function readSurface () {

		var m_id       = readUint();
		var m_width    = readUint();
		var m_height   = readUint();

		var surfacePtr = readDataPtr();
		var m_calib    = readCalibration();

		var ab = source.slice( pos, pos + surfacePtr.size ); // required for 64b alignment

		self.terrain = new Float64Array( ab, 0 );

		self.terrainDimensions.samples = m_width;
		self.terrainDimensions.lines   = m_height;
		self.terrainDimensions.xOrigin = m_calib[0];
		self.terrainDimensions.yOrigin = m_calib[1];
		self.terrainDimensions.xDelta  = m_calib[2];
		self.terrainDimensions.yDelta  = m_calib[5];

	}

	function readCalibration () {

		var f = new DataView( source, pos );
		var m_calib = [];

		m_calib[0] = f.getFloat64( 0,  true );
		m_calib[1] = f.getFloat64( 8,  true );
		m_calib[2] = f.getFloat64( 16, true );
		m_calib[3] = f.getFloat64( 24, true );
		m_calib[4] = f.getFloat64( 32, true );
		m_calib[5] = f.getFloat64( 40, true );

		pos += 48;

		return m_calib;

	}

	function readSurfaceBMP () {

		var m_type      = readUint();
		var m_surfaceId = readUint();

		var imagePtr = readDataPtr();
		var m_calib  = readCalibration();

		self.terrainBitmap = extractImage( imagePtr );
	}

	function extractImage ( imagePtr ) {

		var imgData = new Uint8Array( source, dataStart + imagePtr.position, imagePtr.size );
		var type;

		var b1 = imgData[0];
		var b2 = imgData[1];

		if ( b1 === 0xff && b2 === 0xd8 ) {

			type = "image/jpeg";

		}

		if ( b1 === 0x89 && b2 === 0x50 ) {

			type = "image/png";

		}

		if (!type) {

			return "";

		}

		var blob = new Blob( [imgData], { type: type } );
		var blobURL = URL.createObjectURL( blob );

		return blobURL;

	}
}

loxHandler.prototype.constructor = loxHandler;

loxHandler.prototype.getLineSegments = function () {

	return this.lineSegments;

}

loxHandler.prototype.getSurveyTree = function () {

	return this.surveyTree;

}

loxHandler.prototype.getTerrainDimensions = function () {

	return this.terrainDimensions;

}

loxHandler.prototype.getTerrainData = function () {

	// flip y direction
	var flippedTerrain = [];
	var lines   = this.terrainDimensions.lines;
	var samples = this.terrainDimensions.samples;

	for ( var i = 0; i < lines; i++ ) {

		var offset = ( lines - 1 - i ) * samples;

		for ( var j = 0; j < samples; j++ ) {

			flippedTerrain.push( this.terrain[offset + j] );

		}

	}

	return flippedTerrain;

}

loxHandler.prototype.getTerrainBitmap = function () {

	return this.terrainBitmap;

}

loxHandler.prototype.getFaults = function () {

	return this.chains;

}

loxHandler.prototype.getName = function () {

  return this.fileName;

}

loxHandler.prototype.getSurvey = function () {

	return {
		title: this.fileName,
		lineSegments: this.lineSegments,
		crossSections: [],
		scraps: this.scraps,
		entrances: this.entrances,
		hasTerrain: false
	}

}

export { loxHandler };

// EOF<commit_msg>remove missed redundant method<commit_after>
import { NORMAL, SPLAY, SURFACE } from '../core/constants.js';
import { Tree } from '../core/Tree.js';

function loxHandler  ( fileName, dataStream ) {

	this.fileName          = fileName;
	this.entrances         = [];
	this.terrain           = [];
	this.terrainBitmap     = "";
	this.terrainDimensions = {};
	this.scraps            = [];
	this.faults            = [];
	this.lineSegments      = [];
	this.sections          = new Map();
	this.surveyTree        = new Tree( "", 0 );
	this.isRegion		   = false;

	var lineSegments = [];
	var xSects       = [];
	var stations     = [];
	var lines        = 0;
	var samples      = 0;
	var self         = this;
	var surveyTree   = this.surveyTree;

	// assumes little endian data ATM - FIXME

	var source = dataStream;
	var pos = 0; // file position
	var dataStart;
	var f = new DataView( source, 0 );
	var l = source.byteLength;

	while ( pos < l ) {

		readChunkHdr();

	}

	this.lineSegments = lineSegments;

	// Drop data to give GC a chance ASAP
	source = null;

	// strip empty/single top nodes of tree
	this.surveyTree = surveyTree.reduce( "unknown" );

	return;

	// .lox parsing functions

	function readChunkHdr () {

		var m_type     = readUint();
		var m_recSize  = readUint();
		var m_recCount = readUint();
		var m_dataSize = readUint();
		var doFunction;

		// offset of data region for out of line strings/images/scrap data.
		dataStart  = pos + m_recSize;

		switch ( m_type ) {

		case 1:

			doFunction = readSurvey;

			break;

		case 2:

			doFunction = readStation;

			break;

		case 3:

			doFunction = readShot;

			break;

		case 4:

			doFunction = readScrap;

			break;

		case 5:

			doFunction = readSurface;

			break;

		case 6:

			doFunction = readSurfaceBMP;

			break;

		default:

			console.log( "unknown chunk header. type : ", m_type );

		}

		if ( doFunction !== undefined) {

			for ( var i = 0; i < m_recCount; i++ ) {

				doFunction( i );

			}

		}

		skipData( m_dataSize );
	}

	function readUint () {

		var i = f.getUint32( pos, true );

		pos += 4;

		return i;

	}

	function skipData ( i ) {

		pos += i;

	}

	function readSurvey ( i ) {

		var m_id     = readUint();
		var namePtr  = readDataPtr();
		var m_parent = readUint();
		var titlePtr = readDataPtr();

		if ( m_parent != m_id && !surveyTree.addById( readString( namePtr ), m_id, m_parent ) ) {

			console.log( "error constructing survey tree" );

		}

	}

	function readDataPtr() {

		var m_position = readUint();
		var m_size     = readUint();

		return { position: m_position, size: m_size };

	}

	function readString ( ptr ) {

		var bytes = new Uint8Array( source, dataStart + ptr.position, ptr.size );

		return String.fromCharCode.apply( null, bytes );

	}

	function readStation () {

		var m_id       = readUint();
		var m_surveyId = readUint();
		var namePtr    = readDataPtr();

		readDataPtr(); // commentPtr

		var m_flags    = readUint();
		var coords     = readCoords();

		stations[ m_id ]  = coords;

		// add non surface stations to surveyTree

		if ( ! ( m_flags & 0x01 ) ) surveyTree.addById( readString( namePtr ),  m_id, m_surveyId, { p: coords } );

		if ( m_flags & 0x02 ) {

			// entrance
			self.entrances.push( { position: coords, label: readString(namePtr), survey: m_surveyId } );

		}

	}

	function readCoords () {

		var f = new DataView( source, pos );
		var coords = {};

		coords.x = f.getFloat64( 0,  true );
		coords.y = f.getFloat64( 8,  true );
		coords.z = f.getFloat64( 16, true );
		pos += 24;

		return coords;

	}

	function readShot () {

		var m_from = readUint();
		var m_to   = readUint();

		var fromLRUD = readLRUD();
		var toLRUD   = readLRUD();

		var m_flags       = readUint();
		var m_sectionType = readUint();
		var m_surveyId    = readUint();
		var m_threshold   = f.getFloat64( pos, true );
		var type          = NORMAL;

		pos += 8;

		if ( m_flags && 0x01 ) type = SURFACE;
		if ( m_flags && 0x08 ) type = SPLAY;

		if ( m_flags === 0x16 ) {

			xSects[ m_from ] = fromLRUD;
			xSects[ m_to ]   = toLRUD;

		}

		lineSegments.push( { from: stations[ m_from ], to: stations[ m_to ], type: type, survey: m_surveyId } );

	}

	function readLRUD () {

		var f          = new DataView( source, pos );
		var L		   = f.getFloat64( 0,  true );
		var R		   = f.getFloat64( 8,  true );
		var U		   = f.getFloat64( 16, true );
		var D   	   = f.getFloat64( 24, true );

		pos += 32;

		return { l: L, r: R, u: U, d: D };

	}

	function readScrap () {

		var m_id         = readUint();
		var m_surveyId   = readUint();

		var m_numPoints  = readUint();
		var pointsPtr    = readDataPtr();

		var m_num3Angles = readUint();
		var facesPtr     = readDataPtr();

		var scrap = { vertices: [], faces: [], survey: m_surveyId };
		var lastFace;
		var i;

		for ( i = 0; i < m_numPoints; i++ ) {

			var offset = dataStart + pointsPtr.position + i * 24; // 24 = 3 * sizeof(double)
			var f = new DataView( source, offset );
			var vertex = {};

			vertex.x = f.getFloat64( 0,  true );
			vertex.y = f.getFloat64( 8,  true );
			vertex.z = f.getFloat64( 16, true );

			scrap.vertices.push( vertex );

		}

		// read faces from out of line data area

		for ( i = 0; i < m_num3Angles; i++ ) {

			var offset = dataStart + facesPtr.position + i * 12; // 12 = 3 * sizeof(uint32)
			var f = new DataView( source, offset );
			var face = [];

			face[0] = f.getUint32( 0, true );
			face[1] = f.getUint32( 4, true );
			face[2] = f.getUint32( 8, true );

			// check for face winding order == orientation

			fix_direction: { if ( lastFace !== undefined ) {

				var j;

				for ( j = 0; j < 3; j++ ) { // this case triggers more often than those below.

					if (face[ j ] == lastFace[ ( j + 2 ) % 3 ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 3 ) % 3 ] ) {

						face.reverse();
						break fix_direction;

					}

				}

				for ( j = 0; j < 3; j++ ) {

					if ( face[ j ] == lastFace[ j ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 1 ) % 3 ] ) {

						face.reverse();
						break fix_direction;

					}

				}

				for ( j = 0; j < 3; j++ ) {

					if ( face[ j ] == lastFace[ ( j + 1 ) % 3 ] && face[ ( j + 1 ) % 3 ] == lastFace[ ( j + 2 ) % 3] ) {

						face.reverse();
						break fix_direction;

					}

				}
			} }

			scrap.faces.push( face );
			lastFace = face;
		}

		self.scraps.push( scrap );

	}

	function readSurface () {

		var m_id       = readUint();
		var m_width    = readUint();
		var m_height   = readUint();

		var surfacePtr = readDataPtr();
		var m_calib    = readCalibration();

		var ab = source.slice( pos, pos + surfacePtr.size ); // required for 64b alignment

		self.terrain = new Float64Array( ab, 0 );

		self.terrainDimensions.samples = m_width;
		self.terrainDimensions.lines   = m_height;
		self.terrainDimensions.xOrigin = m_calib[0];
		self.terrainDimensions.yOrigin = m_calib[1];
		self.terrainDimensions.xDelta  = m_calib[2];
		self.terrainDimensions.yDelta  = m_calib[5];

	}

	function readCalibration () {

		var f = new DataView( source, pos );
		var m_calib = [];

		m_calib[0] = f.getFloat64( 0,  true );
		m_calib[1] = f.getFloat64( 8,  true );
		m_calib[2] = f.getFloat64( 16, true );
		m_calib[3] = f.getFloat64( 24, true );
		m_calib[4] = f.getFloat64( 32, true );
		m_calib[5] = f.getFloat64( 40, true );

		pos += 48;

		return m_calib;

	}

	function readSurfaceBMP () {

		var m_type      = readUint();
		var m_surfaceId = readUint();

		var imagePtr = readDataPtr();
		var m_calib  = readCalibration();

		self.terrainBitmap = extractImage( imagePtr );
	}

	function extractImage ( imagePtr ) {

		var imgData = new Uint8Array( source, dataStart + imagePtr.position, imagePtr.size );
		var type;

		var b1 = imgData[0];
		var b2 = imgData[1];

		if ( b1 === 0xff && b2 === 0xd8 ) {

			type = "image/jpeg";

		}

		if ( b1 === 0x89 && b2 === 0x50 ) {

			type = "image/png";

		}

		if (!type) {

			return "";

		}

		var blob = new Blob( [imgData], { type: type } );
		var blobURL = URL.createObjectURL( blob );

		return blobURL;

	}
}

loxHandler.prototype.constructor = loxHandler;

loxHandler.prototype.getSurveyTree = function () {

	return this.surveyTree;

}

loxHandler.prototype.getTerrainDimensions = function () {

	return this.terrainDimensions;

}

loxHandler.prototype.getTerrainData = function () {

	// flip y direction
	var flippedTerrain = [];
	var lines   = this.terrainDimensions.lines;
	var samples = this.terrainDimensions.samples;

	for ( var i = 0; i < lines; i++ ) {

		var offset = ( lines - 1 - i ) * samples;

		for ( var j = 0; j < samples; j++ ) {

			flippedTerrain.push( this.terrain[offset + j] );

		}

	}

	return flippedTerrain;

}

loxHandler.prototype.getTerrainBitmap = function () {

	return this.terrainBitmap;

}

loxHandler.prototype.getFaults = function () {

	return this.chains;

}

loxHandler.prototype.getName = function () {

  return this.fileName;

}

loxHandler.prototype.getSurvey = function () {

	return {
		title: this.fileName,
		lineSegments: this.lineSegments,
		crossSections: [],
		scraps: this.scraps,
		entrances: this.entrances,
		hasTerrain: false
	}

}

export { loxHandler };

// EOF<|endoftext|>


<commit_before>import moment from 'moment'
import {Record} from 'immutable'

const OAuthTokenRecord = Record({  // eslint-disable-line new-cap
  host: '',

  access_token: null,
  refresh_tokend: null,
  created_at: null,
  scope: '',
  token_type: '',
})

/**
 * OAuthTokenを表す
 */
export default class OAuthToken extends OAuthTokenRecord {
  static storeName = 'oauth_tokens'
  static keyPath = 'address'
  static indexes = [
    ['access_token', ['host', 'access_token'], {unique: true}],
  ]

  /**
   * @constructor
   * @param {Objet} token
   */
  constructor(...args) {
    super(...args)
  }

  toJS() {
    const js = super.toJS()

    js.address = this.address
    return js
  }

  isEqual(other) {
    return this.access_token === other.access_token &&
      this.scope === other.access_token &&
      this.token_type === other.token_type
  }

  get accessToken() {
    return this.access_token
  }

  get address() {
    return `${this.host}::${this.access_token}`
  }

  get expiresAt() {
    // Mastodonでは使わん
    // const {
    //   access_token,
    //   refresh_token,
    //   expires,
    //   expires_in,
    // } = token

    // require('assert')(access_token && refresh_token)
    // this.accessToken = access_token
    // this.refreshToken = refresh_token

    // require('assert')(!(expires && expires_in), 'expires and expires_in cannot set simultaneously')
    // require('assert')(expires || expires_in, 'expires or expires_in is required')

    // if(expires_in) {
    //   // 時刻設定連れを考慮して10%の誤差を...
    //   this.expires = moment().add(expires_in * 0.9, 's')
    // } else {
    //   this.expires = expires
    // }
    throw new Error('not implemented')
  }

  /**
   * APIRequesterを作る
   * TODO: 場所を考える
   */
  get requester() {
    if(!this._requester) {
      const {makeOAuthAPIRequester} = require('src/api/APIRequester')
      const MastodonAPISpec = require('src/api/MastodonAPISpec').default

      this._requester = makeOAuthAPIRequester(
        MastodonAPISpec, {
          token: this,
          endpoint: `https://${this.host}/api/v1`,
          hooks: {
            response: (method, apiName, responseBody) => {
              if(Array.isArray(responseBody)) {
                return responseBody.map((obj) => {
                  return {host: this.host, ...obj}
                })
              } else {
                return {host: this.host, ...responseBody}
              }
            },
          },
        })
    }
    return this._requester
  }

  /**
   * TokenがexpireしていたらTrueを返す
   * @return {bool}
   */
  isExpired() {
    // refresh_tokenが無い場合はexpiresしないと考える?
    if(!this.refresh_token)
      return false

    return this.expires.isBefore(moment())
  }
}
<commit_msg>fix OAuthToken isEqual bug<commit_after>import moment from 'moment'
import {Record} from 'immutable'

const OAuthTokenRecord = Record({  // eslint-disable-line new-cap
  host: '',

  access_token: null,
  refresh_tokend: null,
  created_at: null,
  scope: '',
  token_type: '',
})

/**
 * OAuthTokenを表す
 */
export default class OAuthToken extends OAuthTokenRecord {
  static storeName = 'oauth_tokens'
  static keyPath = 'address'
  static indexes = [
    ['access_token', ['host', 'access_token'], {unique: true}],
  ]

  /**
   * @constructor
   * @param {Objet} token
   */
  constructor(...args) {
    super(...args)
  }

  toJS() {
    const js = super.toJS()

    js.address = this.address
    return js
  }

  isEqual(other) {
    return this.access_token === other.access_token &&
      this.scope === other.scope &&
      this.token_type === other.token_type
  }

  get accessToken() {
    return this.access_token
  }

  get address() {
    return `${this.host}::${this.access_token}`
  }

  get expiresAt() {
    // Mastodonでは使わん
    // const {
    //   access_token,
    //   refresh_token,
    //   expires,
    //   expires_in,
    // } = token

    // require('assert')(access_token && refresh_token)
    // this.accessToken = access_token
    // this.refreshToken = refresh_token

    // require('assert')(!(expires && expires_in), 'expires and expires_in cannot set simultaneously')
    // require('assert')(expires || expires_in, 'expires or expires_in is required')

    // if(expires_in) {
    //   // 時刻設定連れを考慮して10%の誤差を...
    //   this.expires = moment().add(expires_in * 0.9, 's')
    // } else {
    //   this.expires = expires
    // }
    throw new Error('not implemented')
  }

  /**
   * APIRequesterを作る
   * TODO: 場所を考える
   */
  get requester() {
    if(!this._requester) {
      const {makeOAuthAPIRequester} = require('src/api/APIRequester')
      const MastodonAPISpec = require('src/api/MastodonAPISpec').default

      this._requester = makeOAuthAPIRequester(
        MastodonAPISpec, {
          token: this,
          endpoint: `https://${this.host}/api/v1`,
          hooks: {
            response: (method, apiName, responseBody) => {
              if(Array.isArray(responseBody)) {
                return responseBody.map((obj) => {
                  return {host: this.host, ...obj}
                })
              } else {
                return {host: this.host, ...responseBody}
              }
            },
          },
        })
    }
    return this._requester
  }

  /**
   * TokenがexpireしていたらTrueを返す
   * @return {bool}
   */
  isExpired() {
    // refresh_tokenが無い場合はexpiresしないと考える?
    if(!this.refresh_token)
      return false

    return this.expires.isBefore(moment())
  }
}
<|endoftext|>


<commit_before>/**
 * Created by jerico on 4/28/2015.
 */
angular.module("fluidPage", ["fluidHttp", "fluidOption", "fluidPanel"])
    .directive("fluidPage", ["$templateCache", "fluidPageService", "FluidPage", "$compile", "FluidBreadcrumb", "FluidOption",
        function (tc, fps, FluidPage, c, FluidBreadcrumb, FluidOption) {
            return {
                restrict: "E",
                scope: {fluidPanel: "="},
                template: tc.get("templates/fluid/fluidPage.html"),
                link: {
                    pre: function (scope) {
                        scope.$on("$destroy", function () {
                            console.debug("fluid-page.$destroy", scope.fluidPage);
                            if (scope.fluidPage) {
                                scope.fluidPage.clear();
                            }
                        });
                        scope.fluidPageService = fps;
                        scope.loadPage = function (newPage) {
                            var pageElement = $("#" + scope.fluidPanel.getElementFlowId("_id_fp_p"));
                            console.debug("fluidPage-loadPage.page", newPage);
                            console.debug("fluidPage-loadPage.fluidPanel", scope.fluidPanel);
                            scope.fluidPage = newPage;
                            if (scope.fluidPage.ajax) {
                                fps.loadAjax(newPage)
                                    .then(function (data) {
                                        console.debug("fluidPage-loadPage.data", data);
                                        scope.data = data;
                                        pageElement.html("<fluid-include ng-controller='" + scope.fluidPage.controller + "' class='page' url='{{fluidPageService.render(fluidPage)}}' complete='onLoad()'></fluid-include>");
                                        pageElement.attr("page-name", newPage.name);
                                        c(pageElement.contents())(scope);
                                        console.debug("fluidPage-loadPage.loaded-page", newPage);
                                        scope.loadFrameAdjustment();
                                    }, function (reason) {
                                        //TODO: add error loading page
                                    });
                            } else {
                                pageElement.html("<fluid-include  ng-controller='" + scope.fluidPage.controller + "' class='page' url='{{fluidPageService.render(fluidPage)}}' complete='onLoad()'></fluid-include>");
                                pageElement.attr("page-name", newPage.name);
                                c(pageElement.contents())(scope);
                                console.debug("fluidPage-loadPage.loaded-page", newPage);
                                scope.loadFrameAdjustment();
                            }
                        };

                        scope.loadFrameAdjustment = function () {
                            if (scope.fluidPanel.frame.fullScreen) {
                                scope.fluidPanel.frame.$().scrollTop(0);
                                var maxHeight = scope.fluidPanel.frame.$().css("height");
                                console.debug("fluidPage.fullScreen.maxHeight", maxHeight);
                                console.debug("fluidPage.fullScreen.innerHeight", scope.fluidPanel.frame.$().innerHeight());
                                autoFullscreen(scope.fluidPanel.$(), maxHeight.replace("px", ""), scope.fluidPanel.frame.$().innerWidth());
                            } else {
                                scope.fluidPanel.frame.$().find(".fluid-page").css("height", "").css("overflow-y", "");
                            }
                        }
                    },
                    post: function (scope, element) {
                        //TODO: page onLeave handling
                        if (scope.fluidPanel) {
                            scope.fluidPanel.frame.progress.onComplete(scope.fluidPanel.getElementFlowId("toggleFullscreen"), function () {
                                console.debug("fluidPage.fluidPanel.frame.progress.onComplete-toggleFullscreen", scope.fluidPanel.currentPage());
                                var page = scope.fluidPanel.currentPage();
                                if (page) {
                                    scope.loadPage(page);
                                }
                            });
                            scope.fluidPanel.frame.progress.onComplete(scope.fluidPanel.getElementFlowId("closeTask"), function () {
                                console.debug("fluidPage.fluidPanel.frame.progress.onComplete-closeTask", scope.fluidPanel.currentPage());
                                var page = scope.fluidPanel.currentPage();
                                if (page) {
                                    scope.loadPage(page);
                                }

                            });
                            scope.fluidPanel.progress.onComplete("loadPage", function (page) {
                                console.debug("fluidPage.fluidPanel.progress.onComplete-loadPage", page);
                                scope.loadPage(page);
                            });
                        }

                        scope.onLoad = function () {
                            console.debug("fluidPage-page-onload.fluidId", scope.fluidPanel.id);
                            scope.fluidPage.fluidId = scope.fluidPanel.id;
                            scope.fluidPage.$ = function () {
                                return $("#" + element.attr("id"));
                            };
                            scope.fluidPage.$scope = function () {
                                return scope;
                            };
                            console.debug("fluidPage-page-onload.$", scope.fluidPage.$());
                            scope.fluidPage.option = new FluidOption(scope.fluidPanel);
                            scope.fluidPage.loaded = false;
                            //TODO: page onLoad error handling

                            scope.fluidPage.load(function () {
                                scope.fluidPage.loaded = true;
                            }, function () {
                                scope.fluidPage.loaded = false;
                                scope.fluidPage.onClose = function (ok, cancel) {
                                    return ok();
                                };
                                element.html("");
                                c(element.contents())(scope);
                            });
                        }

                    }
                },
                replace: true
            }
        }])
    .factory("FluidPage", ["fluidPageService", "$resource", "$q", "$timeout", "$rootScope", function (fps, r, q, t, rs) {
        var fluidPage = function (page) {

            if (page.ajax) {
                var url = page.ajax.url;

                if (page.host) {

                    var length = page.host.length;
                    var lastChar = page.host.indexOf(page.host.length - 1);

                    if (lastChar === "/") {
                        length--;
                    }
                    var host = page.host.substring(0, length - 1);

                    if (url.indexOf(0) === "/") {
                        url = url.substring(1, url.length - 1);
                    }

                    url = host + "/" + url;
                }

                if (url) {
                    if (!page.ajax.actions) {
                        page.ajax.actions = [];
                    }
                    if (!page.ajax.param) {
                        page.ajax.param = {};
                    }

                    url = "fid=" + page.fluidId + ";pg=" + page.name + ";" + url;

                    this.resource = r(url, page.ajax.param, page.ajax.actions);

                }
                /*else {
                 throw "Page ajax.url is required!";
                 }*/
            }

            /*
             TODO: v 0.2.0 url page
             if(page.url){

             }else{

             }
             */
            console.debug("FluidPage-FluidPage.page", page);
            this.controller = page.controller;
            this.isHome = page.isHome;
            this.name = page.name;
            this.id = page.id;
            this.title = page.title;
            this.static = page.static;
            this.html = page.html;
            this.home = page.home;
            this.ajax = page.ajax;
            this.fluidId = page.fluidId;
            this.host = page.host;
            this.showToolBar = page.showToolBar;
            /*
             TODO: create page transition
             if (page.animate) {
             this.animate = {};
             this.animate.previous = page.animate.previous ? page.animate.previous : 'slideOutRight';
             this.animate.next = page.animate.next ? page.animate.next : 'slideOutLeft';
             this.animate.enterPrevious = page.animate.enterPrevious ? page.animate.enterPrevious : 'slideInLeft';
             this.animate.enterNext = page.animate.enterNext ? page.animate.enterNext : 'slideInRight';
             this.animate.enter = page.animate.enter ? page.animate.enter : 'slideInLeft';
             this.animate.leave = page.animate.leave ? page.animate.leave : 'slideOutRight';
             } else {
             this.animate = {};
             this.animate.previous = 'slideOutRight';
             this.animate.next = 'slideOutLeft';
             this.animate.enterPrevious = 'slideInLeft';
             this.animate.enterNext = 'slideInRight';
             this.animate.enter = 'slideInLeft';
             this.animate.leave = 'slideOutRight';
             }*/
            this.refresh = function (proceed, cancel, $event) {
                var page = this;
                this.onRefresh(function () {
                    console.debug("fluid-page-FluidPage-onRefresh.proceed", proceed);
                    page.isRefreshed = true;
                    proceed(page);
                    if (page.option) {
                        page.option.isCancelled = false;
                        page.option.close();
                    }
                }, function () {
                    cancel();
                    if (page.option) {
                        page.option.isCancelled = true;
                        page.option.close();
                    }
                }, $event);
            };
            this.close = function (ok, cancel, $event) {
                var page2 = this;
                var pageElement = page2.$();
                if (pageElement) {
                    var panelBody = pageElement.parent();
                    if (panelBody) {
                        var collapsePanel = panelBody.parent();
                        if (collapsePanel && !collapsePanel.hasClass("in")) {
                            collapsePanel.collapse("show");
                        }
                    }
                }

                this.onClose(function () {
                    ok();
                    if (page2.option) {
                        page2.option.isCancelled = false;
                        page2.option.close();
                    }
                    page2.onDestroy();
                    page2.clear();
                }, function () {
                    cancel();
                    if (page2.option) {
                        page2.option.isCancelled = true;
                        page2.option.close();
                    }
                }, $event);
            };
            this.change = function (proceed, cancel, $event) {
                var page = this;
                this.onChange(function () {
                    proceed();
                    if (page.option) {
                        page.option.isCancelled = false;
                        page.option.close();
                    }
                }, function () {
                    cancel();
                    if (page.option) {
                        page.option.isCancelled = true;
                        page.option.close();
                    }
                }, $event);
            };
            this.load = function (ok, cancel) {
                ok();
                this.onLoad(cancel);
            };
            this.failed = function (reason) {
                rs.$broadcast("page_close_failed_evt" + this.fluidId + "_pg_" + this.name, reason);
            };
            this.fullscreen = function (ok, cancel) {
                this.onFullscreen(function () {
                    ok();
                }, function () {
                    cancel();
                });
            };
            this.fluidscreen = function (ok, cancel) {
                this.onFluidscreen(function () {
                    ok();
                }, function () {
                    cancel();
                });
            };
            this.onLoad = function (cancel) {
            };
            this.onClose = function (ok, cancel) {
                ok();
            };
            this.onChange = function (proceed, cancel, $event) {
                proceed();
            };
            this.onRefresh = function (proceed, cancel, $event) {
                proceed(this);
            };
            this.onDestroy = function () {
            };
            this.onViewportChange = function (viewport) {
            };
            this.onSizeChange = function (size) {

            };
            this.onFullscreen = function (ok, cancel) {
                ok();
            };
            this.onFluidscreen = function (ok, cancel) {
                ok();
            };
            this.clear = function () {
                this.onDestroy();
                fps.destroyFluidPageState(this.name, this.fluidId);
            };
            this.watch = page.watch ? page.watch.split(",") : undefined;
            var def = {};
            angular.copy(this, def);
            this.default = def;
            console.debug("fluidPage-FluidPageg-newPage.page", this);
            return this;
        };
        return fluidPage;
    }])
    .service("fluidPageService", ["$templateCache", "$q", "$sce", "$timeout", function (tc, q, sce) {
        this.pages = [];
        this.states = [];
        this.pageState = function (name) {

            if (this.states[name] !== undefined) {
                return this.states[name];
            } else {
                var state = {
                    $updated: undefined,
                    $cached: false,
                    tasks: []
                };
                this.states[name] = state;
                return this.states[name];
            }

        };
        this.fluidPageState = function (page, fluidId) {

            var state = this.pageState(page);

            if (state.tasks[fluidId] !== undefined) {
                return state.tasks[fluidId];
            } else {
                var fluidPageState = {
                    $dirty: false,
                    $lastUpdated: undefined,
                    $history: [],
                    $currentState: 0
                };
                state.tasks[fluidId] = fluidPageState;
                return fluidPageState;
            }

        };
        this.destroyFluidPageState = function (page, fluidId) {
            var state = this.pageState(page);
            if (state.tasks[fluidId] != null) {
                state.tasks[fluidId] = null;
            }
        };
        this.destroyState = function (name) {
            if (this.states[name] != null) {
                this.states[name] = null;
            }
        };
        this.loadAjax = function (fluidPage) {
            var fps = this;
            return q(function (resolve) {
                if (fluidPage.ajax) {

                    var ajax = fluidPage.ajax;
                    if (ajax.auto === undefined) {
                        ajax.auto = true;
                    }
                    if (ajax.auto === true) {
                        var $updated;

                        var state = fps.pageState(fluidPage.name);
                        var fluidState = fps.fluidPageState(fluidPage.name, fluidPage.fluidId);

                        console.debug("fluidPage-fluidPageService-loadAjax.state", state);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidState", fluidState);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage", fluidPage);

                        $updated = state.$updated !== undefined ? fluidState.$lastUpdated < state.$updated : false;
                        console.debug("fluidPage-fluidPageService-loadAjax.$updated", $updated);
                        if (!$updated && fluidPage.watch) {
                            for (var i = 0; i < fluidPage.watch.length; i++) {
                                var watch = fluidPage.watch[i];
                                console.debug("fluidPage-fluidPageService-loadAjax.watch", watch);
                                var watchedState = fps.pageState(watch);
                                console.debug("fluidPage-fluidPageService-loadAjax.watchedState", watchedState);
                                $updated = watchedState.$updated !== undefined ? fluidState.$lastUpdated < watchedState.$updated : false;
                                if ($updated) {
                                    break;
                                }
                            }
                        }

                        if ($updated) {
                            $updated = !fluidState.$dirty;

                        }

                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage.isNew", fluidPage.isNew);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage.isRefreshed", fluidPage.isRefreshed);
                        if (ajax.data) {
                            console.debug("fluidPage-fluidPageService.ajax.data", ajax.data);
                            if (fluidPage.isNew || fluidPage.isRefreshed || $updated) {
                                if (!fluidPage.cached) {
                                    fluidPage.cached = {}
                                }
                                fluidPage.isNew = false;
                                fluidPage.isRefreshed = false;
                                fluidState.$lastUpdated = new Date().getTime();
                                angular.extend(fluidPage.cached, ajax.data);
                                fluidPage.cached.__proto__ = ajax.data.__proto__;
                                resolve(fluidPage.cached);
                            }
                        }

                        if (fluidPage.isNew || fluidPage.isRefreshed || $updated) {
                            if (!fluidPage.cached) {
                                fluidPage.cached = {}
                            }
                            if (ajax.isArray) {
                                var query = fluidPage.resource.query(fluidPage.ajax.param, function () {
                                    fluidPage.isNew = false;
                                    fluidPage.isRefreshed = false;
                                    fluidPage.cached = query;
                                    if (fluidState) {
                                        fluidState.$lastUpdated = new Date().getTime();
                                    }
                                    resolve(fluidPage.cached);
                                });
                            } else {
                                fluidPage.resource.get(fluidPage.ajax.param, function (data) {
                                    fluidPage.isNew = false;
                                    fluidPage.isRefreshed = false;
                                    if (fluidState) {
                                        fluidState.$lastUpdated = new Date().getTime();
                                    }
                                    angular.extend(fluidPage.cached, data);
                                    fluidPage.cached.__proto__ = data.__proto__;
                                    resolve(fluidPage.cached);
                                });
                            }
                        } else {
                            resolve(fluidPage.cached);
                        }
                    } else {
                        fluidPage.cached = {};
                        resolve(fluidPage.cached);
                    }
                }
            });
        };
        this.clear = function (page) {
            this.pages[page] = undefined;
        };
        this.render = function (page) {
            if (page) {
                if (page.static) {
                    page.home = "template_" + page.id;
                    if (!tc.get(page.home)) {
                        tc.put(page.home, page.html);
                    }
                }

                return sce.trustAsUrl(page.home);
            }

        };

        return this;
    }]);

<commit_msg>added fluid-include;<commit_after>/**
 * Created by jerico on 4/28/2015.
 */
angular.module("fluidPage", ["fluidHttp", "fluidOption", "fluidPanel"])
    .directive("fluidPage", ["$templateCache", "fluidPageService", "FluidPage", "$compile", "FluidBreadcrumb", "FluidOption",
        function (tc, fps, FluidPage, c, FluidBreadcrumb, FluidOption) {
            return {
                restrict: "E",
                scope: {fluidPanel: "="},
                template: tc.get("templates/fluid/fluidPage.html"),
                link: {
                    pre: function (scope) {
                        scope.$on("$destroy", function () {
                            console.debug("fluid-page.$destroy", scope.fluidPage);
                            if (scope.fluidPage) {
                                scope.fluidPage.clear();
                            }
                        });
                        scope.fluidPageService = fps;
                        scope.loadPage = function (newPage) {
                            var pageElement = $("#" + scope.fluidPanel.getElementFlowId("_id_fp_p"));
                            console.debug("fluidPage-loadPage.page", newPage);
                            console.debug("fluidPage-loadPage.fluidPanel", scope.fluidPanel);
                            scope.fluidPage = newPage;
                            if (scope.fluidPage.ajax) {
                                fps.loadAjax(newPage)
                                    .then(function (data) {
                                        console.debug("fluidPage-loadPage.data", data);
                                        scope.data = data;
                                        pageElement.html("<fluid-include ng-controller='" + scope.fluidPage.controller + "' class='page' url='{{fluidPageService.render(fluidPage)}}' complete='onLoad()'></fluid-include>");
                                        pageElement.attr("page-name", newPage.name);
                                        c(pageElement.contents())(scope);
                                        console.debug("fluidPage-loadPage.loaded-page", newPage);
                                        scope.loadFrameAdjustment();
                                    }, function (reason) {
                                        //TODO: add error loading page
                                    });
                            } else {
                                pageElement.html("<fluid-include  ng-controller='" + scope.fluidPage.controller + "' class='page' url='{{fluidPageService.render(fluidPage)}}' complete='onLoad()'></fluid-include>");
                                pageElement.attr("page-name", newPage.name);
                                c(pageElement.contents())(scope);
                                console.debug("fluidPage-loadPage.loaded-page", newPage);
                                scope.loadFrameAdjustment();
                            }
                        };

                        scope.loadFrameAdjustment = function () {
                            if (scope.fluidPanel.frame.fullScreen) {
                                scope.fluidPanel.frame.$().scrollTop(0);
                                var maxHeight = scope.fluidPanel.frame.$().css("height");
                                console.debug("fluidPage.fullScreen.maxHeight", maxHeight);
                                console.debug("fluidPage.fullScreen.innerHeight", scope.fluidPanel.frame.$().innerHeight());
                                autoFullscreen(scope.fluidPanel.$(), maxHeight.replace("px", ""), scope.fluidPanel.frame.$().innerWidth());
                            } else {
                                scope.fluidPanel.frame.$().find(".fluid-page").css("height", "").css("overflow-y", "");
                            }
                        }
                    },
                    post: function (scope, element) {
                        //TODO: page onLeave handling
                        if (scope.fluidPanel) {
                            scope.fluidPanel.frame.progress.onComplete(scope.fluidPanel.getElementFlowId("toggleFullscreen"), function () {
                                console.debug("fluidPage.fluidPanel.frame.progress.onComplete-toggleFullscreen", scope.fluidPanel.currentPage());
                                var page = scope.fluidPanel.currentPage();
                                if (page) {
                                    scope.loadPage(page);
                                }
                            });
                            scope.fluidPanel.frame.progress.onComplete(scope.fluidPanel.getElementFlowId("closeTask"), function () {
                                console.debug("fluidPage.fluidPanel.frame.progress.onComplete-closeTask", scope.fluidPanel.currentPage());
                                var page = scope.fluidPanel.currentPage();
                                if (page) {
                                    scope.loadPage(page);
                                }

                            });
                            scope.fluidPanel.progress.onComplete("loadPage", function (page) {
                                console.debug("fluidPage.fluidPanel.progress.onComplete-loadPage", page);
                                scope.loadPage(page);
                            });
                        }

                        scope.onLoad = function () {
                            console.debug("fluidPage-page-onload.fluidId", scope.fluidPanel.id);
                            scope.fluidPage.fluidId = scope.fluidPanel.id;
                            scope.fluidPage.$ = function () {
                                return $("#" + element.attr("id"));
                            };
                            scope.fluidPage.$scope = function () {
                                return scope;
                            };
                            console.debug("fluidPage-page-onload.$", scope.fluidPage.$());
                            scope.fluidPage.option = new FluidOption(scope.fluidPanel);
                            scope.fluidPage.loaded = false;
                            //TODO: page onLoad error handling

                            scope.fluidPage.load(function () {
                                scope.fluidPage.loaded = true;
                            }, function () {
                                scope.fluidPage.loaded = false;
                                scope.fluidPage.onClose = function (ok, cancel) {
                                    return ok();
                                };
                                element.html("");
                                c(element.contents())(scope);
                            });
                        }

                    }
                },
                replace: true
            }
        }])
    .factory("FluidPage", ["fluidPageService", "$resource", "$q", "$timeout", "$rootScope", function (fps, r, q, t, rs) {
        var fluidPage = function (page) {

            if (page.ajax) {
                var url = page.ajax.url;

                if (page.host) {

                    var length = page.host.length;
                    var lastChar = page.host.indexOf(page.host.length - 1);

                    if (lastChar === "/") {
                        length--;
                    }
                    var host = page.host.substring(0, length - 1);

                    if (url.indexOf(0) === "/") {
                        url = url.substring(1, url.length - 1);
                    }

                    url = host + "/" + url;
                }

                if (url) {
                    if (!page.ajax.actions) {
                        page.ajax.actions = [];
                    }
                    if (!page.ajax.param) {
                        page.ajax.param = {};
                    }

                    url = "fid=" + page.fluidId + ";pg=" + page.name + ";" + url;

                    this.resource = r(url, page.ajax.param, page.ajax.actions);

                }
                /*else {
                 throw "Page ajax.url is required!";
                 }*/
            }

            /*
             TODO: v 0.2.0 url page
             if(page.url){

             }else{

             }
             */
            console.debug("FluidPage-FluidPage.page", page);
            this.controller = page.controller;
            this.isHome = page.isHome;
            this.name = page.name;
            this.id = page.id;
            this.title = page.title;
            this.static = page.static;
            this.html = page.html;
            this.home = page.home;
            this.ajax = page.ajax;
            this.fluidId = page.fluidId;
            this.host = page.host;
            this.showToolBar = page.showToolBar;
            /*
             TODO: create page transition
             if (page.animate) {
             this.animate = {};
             this.animate.previous = page.animate.previous ? page.animate.previous : 'slideOutRight';
             this.animate.next = page.animate.next ? page.animate.next : 'slideOutLeft';
             this.animate.enterPrevious = page.animate.enterPrevious ? page.animate.enterPrevious : 'slideInLeft';
             this.animate.enterNext = page.animate.enterNext ? page.animate.enterNext : 'slideInRight';
             this.animate.enter = page.animate.enter ? page.animate.enter : 'slideInLeft';
             this.animate.leave = page.animate.leave ? page.animate.leave : 'slideOutRight';
             } else {
             this.animate = {};
             this.animate.previous = 'slideOutRight';
             this.animate.next = 'slideOutLeft';
             this.animate.enterPrevious = 'slideInLeft';
             this.animate.enterNext = 'slideInRight';
             this.animate.enter = 'slideInLeft';
             this.animate.leave = 'slideOutRight';
             }*/
            this.refresh = function (proceed, cancel, $event) {
                var page = this;
                this.onRefresh(function () {
                    console.debug("fluid-page-FluidPage-onRefresh.proceed", proceed);
                    page.isRefreshed = true;
                    proceed(page);
                    if (page.option) {
                        page.option.isCancelled = false;
                        page.option.close();
                    }
                }, function () {
                    cancel();
                    if (page.option) {
                        page.option.isCancelled = true;
                        page.option.close();
                    }
                }, $event);
            };
            this.close = function (ok, cancel, $event) {
                var page2 = this;
                var pageElement = page2.$();
                if (pageElement) {
                    var panelBody = pageElement.parent();
                    if (panelBody) {
                        var collapsePanel = panelBody.parent();
                        if (collapsePanel && !collapsePanel.hasClass("in")) {
                            collapsePanel.collapse("show");
                        }
                    }
                }

                this.onClose(function () {
                    ok();
                    if (page2.option) {
                        page2.option.isCancelled = false;
                        page2.option.close();
                    }
                    page2.onDestroy();
                    page2.clear();
                }, function () {
                    cancel();
                    if (page2.option) {
                        page2.option.isCancelled = true;
                        page2.option.close();
                    }
                }, $event);
            };
            this.change = function (proceed, cancel, $event) {
                var page = this;
                this.onChange(function () {
                    proceed();
                    if (page.option) {
                        page.option.isCancelled = false;
                        page.option.close();
                    }
                }, function () {
                    cancel();
                    if (page.option) {
                        page.option.isCancelled = true;
                        page.option.close();
                    }
                }, $event);
            };
            this.load = function (ok, cancel) {
                ok();
                this.onLoad(cancel);
            };
            this.failed = function (reason) {
                rs.$broadcast("page_close_failed_evt" + this.fluidId + "_pg_" + this.name, reason);
            };
            this.fullscreen = function (ok, cancel) {
                this.onFullscreen(function () {
                    ok();
                }, function () {
                    cancel();
                });
            };
            this.fluidscreen = function (ok, cancel) {
                this.onFluidscreen(function () {
                    ok();
                }, function () {
                    cancel();
                });
            };
            this.onLoad = function (cancel) {
            };
            this.onClose = function (ok, cancel) {
                ok();
            };
            this.onChange = function (proceed, cancel, $event) {
                proceed();
            };
            this.onRefresh = function (proceed, cancel, $event) {
                proceed(this);
            };
            this.onDestroy = function () {
            };
            this.onViewportChange = function (viewport) {
            };
            this.onSizeChange = function (size) {

            };
            this.onFullscreen = function (ok, cancel) {
                ok();
            };
            this.onFluidscreen = function (ok, cancel) {
                ok();
            };
            this.clear = function () {
                this.onDestroy();
                fps.destroyFluidPageState(this.name, this.fluidId);
            };
            this.watch = page.watch ? page.watch.split(",") : undefined;
            var def = {};
            angular.copy(this, def);
            this.default = def;
            console.debug("fluidPage-FluidPageg-newPage.page", this);
            return this;
        };
        return fluidPage;
    }])
    .service("fluidPageService", ["$templateCache", "$q", "$sce", "$timeout", function (tc, q, sce) {
        this.pages = [];
        this.states = [];
        this.pageState = function (name) {

            if (this.states[name] !== undefined) {
                return this.states[name];
            } else {
                var state = {
                    $updated: undefined,
                    $cached: false,
                    tasks: []
                };
                this.states[name] = state;
                return this.states[name];
            }

        };
        this.fluidPageState = function (page, fluidId) {

            var state = this.pageState(page);

            if (state.tasks[fluidId] !== undefined) {
                return state.tasks[fluidId];
            } else {
                var fluidPageState = {
                    $dirty: false,
                    $lastUpdated: undefined,
                    $history: [],
                    $currentState: 0
                };
                state.tasks[fluidId] = fluidPageState;
                return fluidPageState;
            }

        };
        this.destroyFluidPageState = function (page, fluidId) {
            var state = this.pageState(page);
            if (state.tasks[fluidId] != null) {
                state.tasks[fluidId] = null;
            }
        };
        this.destroyState = function (name) {
            if (this.states[name] != null) {
                this.states[name] = null;
            }
        };
        this.loadAjax = function (fluidPage) {
            var fps = this;
            return q(function (resolve) {
                if (fluidPage.ajax) {

                    var ajax = fluidPage.ajax;
                    if (ajax.auto === undefined) {
                        ajax.auto = true;
                    }
                    if (ajax.auto === true) {
                        var $updated;

                        var state = fps.pageState(fluidPage.name);
                        var fluidState = fps.fluidPageState(fluidPage.name, fluidPage.fluidId);

                        console.debug("fluidPage-fluidPageService-loadAjax.state", state);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidState", fluidState);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage", fluidPage);

                        $updated = state.$updated !== undefined ? fluidState.$lastUpdated < state.$updated : false;
                        console.debug("fluidPage-fluidPageService-loadAjax.$updated", $updated);
                        if (!$updated && fluidPage.watch) {
                            for (var i = 0; i < fluidPage.watch.length; i++) {
                                var watch = fluidPage.watch[i];
                                console.debug("fluidPage-fluidPageService-loadAjax.watch", watch);
                                var watchedState = fps.pageState(watch);
                                console.debug("fluidPage-fluidPageService-loadAjax.watchedState", watchedState);
                                $updated = watchedState.$updated !== undefined ? fluidState.$lastUpdated < watchedState.$updated : false;
                                if ($updated) {
                                    break;
                                }
                            }
                        }

                        if ($updated) {
                            $updated = !fluidState.$dirty;

                        }

                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage.isNew", fluidPage.isNew);
                        console.debug("fluidPage-fluidPageService-loadAjax.fluidPage.isRefreshed", fluidPage.isRefreshed);
                        if (ajax.data) {
                            console.debug("fluidPage-fluidPageService.ajax.data", ajax.data);
                            if (fluidPage.isNew || fluidPage.isRefreshed || $updated) {
                                if (!fluidPage.cached) {
                                    fluidPage.cached = {}
                                }
                                fluidPage.isNew = false;
                                fluidPage.isRefreshed = false;
                                fluidState.$lastUpdated = new Date().getTime();
                                angular.extend(fluidPage.cached, ajax.data);
                                fluidPage.cached.__proto__ = ajax.data.__proto__;
                                resolve(fluidPage.cached);
                            }
                        }

                        if (fluidPage.isNew || fluidPage.isRefreshed || $updated) {
                            if (!fluidPage.cached) {
                                fluidPage.cached = {}
                            }
                            if (ajax.isArray) {
                                var query = fluidPage.resource.query(fluidPage.ajax.param, function () {
                                    fluidPage.isNew = false;
                                    fluidPage.isRefreshed = false;
                                    fluidPage.cached = query;
                                    if (fluidState) {
                                        fluidState.$lastUpdated = new Date().getTime();
                                    }
                                    resolve(fluidPage.cached);
                                });
                            } else {
                                fluidPage.resource.get(fluidPage.ajax.param, function (data) {
                                    fluidPage.isNew = false;
                                    fluidPage.isRefreshed = false;
                                    if (fluidState) {
                                        fluidState.$lastUpdated = new Date().getTime();
                                    }
                                    angular.extend(fluidPage.cached, data);
                                    fluidPage.cached.__proto__ = data.__proto__;
                                    resolve(fluidPage.cached);
                                });
                            }
                        } else {
                            resolve(fluidPage.cached);
                        }
                    } else {
                        fluidPage.cached = {};
                        resolve(fluidPage.cached);
                    }
                }
            });
        };
        this.clear = function (page) {
            this.pages[page] = undefined;
        };
        this.render = function (page) {
            if (page) {
                if (page.static) {
                    page.home = "template_" + page.id;
                    if (!tc.get(page.home)) {
                        tc.put(page.home, page.html);
                    }
                }

                return page.home;
            }

        };

        return this;
    }]);

<|endoftext|>


<commit_before>/*global Promise, chrome, PlayMidnightUtilities */
var PlayMidnight = (function(_){
	'use strict';

	// Our Friend
	var PM = {};

	// Dev Mode: Use CSS File rather than inline <style> (inline allows dynamic accent colors)
	var _dev = false;

	var VERSION_NUMBER = '2.1.5';

	// Reset Options when version less than
	var _resetOptions = '2.1.0';

	// Nuke All Options
	var _nukeOptions = '2.1.0';

	// All Options Defined
	var _optionsGraph = {}; // Full Options Tree (options.json)
	var _defaultOptions = {}; // Default Options
	var _userOptions = {}; // User Loaded Options

	// Favicon Attributes
	var _favicon = {
		// Load Newest Icon with Timestamp to prevent Caching
		url: _.browser.url('dist/images/favicon.ico') + '?v=' + Date.now()
	};

	// Stylesheets
	var _stylesheets = {
		main: {
			id: 'play-midnight-stylesheet',
			url: _.browser.url('dist/css/play-midnight.css'),
			html: '',
			enabled: function() {
				return _userOptions.enabled;
			}
		},

		accents: {
			id: 'play-midnight-accents',
			url: _.browser.url('dist/css/play-midnight-accents.css'),
			html: '',
			enabled: function() {
				return (_userOptions.enabled === false && _userOptions.accentsOnly);
			}
		},

		options: {
			id: 'play-midnight-options',
			url: _.browser.url('dist/css/play-midnight-options.css'),
			html: ''
		}
	};

	// Replace Rules
	var _replaceRules = [
		{
			name: 'Accent Color',
			regex: /\#fb8521/gi,
			replace: function() {
				return _userOptions.accent.color;
			}
		},
		{
			name: 'Chrome URL',
			regex: /\{CHROME_DIR\}/gi,
			replace: function() {
				return _.browser.url('/dist');
			}
		}
	];

	// Load User Options from Chrome Storage
	function loadOptions(cb) {
		_.$http.get(_.browser.url('dist/options.json'))
		.then(function(options) {
			_optionsGraph = JSON.parse(options);
			_defaultOptions = parseOptions(_optionsGraph);

			_.log('Default Options Loaded');
			for (var key in _defaultOptions) {
				if (_defaultOptions.hasOwnProperty(key)) {
					_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(_defaultOptions[key]));
				}
			}

			_.browser.get(_defaultOptions, function(options) {
				checkUpdated(options, cb);
			});
		});
	}

	// Parse Options
	function parseOptions(options, _parsed) {
		var option, rules;

		_parsed = _parsed || {};

		rules = {
			'{VERSION_NUMBER}': VERSION_NUMBER
		};

		for (var key in options) {
			if (options.hasOwnProperty(key)) {
				option = options[key];

				if (option.type === 'section') {
					parseOptions(option.options, _parsed);
				} else if (option.type === 'array') {
					_parsed[option.single] = rules[option.default] || option.default;
					_parsed[key] = option.collection;
				} else {
					if (option.type === 'string' && option.saved === false) {
						continue;
					}

					_parsed[key] = rules[option.default] || option.default;
				}
			}
		}

		return _parsed;
	}

	// Check if Play Midnight has Updated, Reset options if needed
	function checkUpdated(options, cb) {
		var skipped = ['lastRun', 'accent', 'accents', 'queue'];

		// No Version Found (< v2.0.0)
		if (options.version === undefined || _.versionCompare(options.version, _nukeOptions) === -1) {
			if (options.version === undefined) {
				_.log('PLAY MIDNIGHT: No Current Options Found, Setting to Default');
			} else {
				_.log('PLAY MIDNIGHT: Nuking All Options to Default');
			}

			_.browser.save(_defaultOptions, function() {
				_userOptions = _defaultOptions;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Version Older Than Required Reset (For Resetting to add new options)
		} else if (_.versionCompare(options.version, _resetOptions) === -1) {
			_.log('PLAY MIDNIGHT: Options Update, Forcing Reset');

			for (var key in _defaultOptions) {
				if (_defaultOptions.hasOwnProperty(key)) {
					if (options[key] === undefined || (Object.prototype.toString.call(options[key]) !== Object.prototype.toString.call(_defaultOptions[key]) && skipped.indexOf(key) === -1)) {
						_.log('Setting %s to default (User: %s, Default: %s)', key, JSON.stringify(options[key]), JSON.stringify(_defaultOptions[key]));
						options[key] = _defaultOptions[key];
					} else {
						_.log('Skipping %s, User Value: %s', key, JSON.stringify(options[key]));
					}
				}
			}

			_.browser.save(options, function() {
				_userOptions = options;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Update Version Number
		} else if (_.versionCompare(options.version, VERSION_NUMBER) === -1) {
			_.log('PLAY MIDNIGHT: Updated to version %s', VERSION_NUMBER);

			_.browser.save({ version: VERSION_NUMBER }, function() {
				_userOptions.version = VERSION_NUMBER;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Options All Good
		} else {
			_userOptions = options;
			if (cb && typeof cb === 'function') {
				cb();
			}
			return;
		}

		// Something went wrong loading options, set user to default for this Run
		_userOptions = _defaultOptions;
	}

	// Inject Stylesheet
	function injectStyle() {
		var promises = [];

		function doInject() {
			var stylesheet, link, style, replace, rule, key, temp;

			// Inject Stylesheet as <link>
			if (_dev) {
				_.log('DEV MODE ENABLED: Using <link> tag');
				link = document.createElement('link');
				link.rel = 'stylesheet';
				link.type = 'text/css';

				for (key in _stylesheets) {
					if (_stylesheets.hasOwnProperty(key)) {
						stylesheet = _stylesheets[key];

						if (stylesheet.hasOwnProperty('enabled') && !stylesheet.enabled()) {
							continue;
						}

						temp = link.cloneNode();

						temp.href = stylesheet.url;
						document.head.appendChild(temp);
					}
				}

				return;
			}

			_.log('DEV MODE DISABLED: Using <style> tag');
			style = document.createElement('style');
			style.type = 'text/css';

			for (key in _stylesheets) {
				if (_stylesheets.hasOwnProperty(key)) {
					stylesheet = _stylesheets[key];

					if (stylesheet.hasOwnProperty('enabled') && !stylesheet.enabled()) {
						continue;
					}

					if (_userOptions.enabled || _userOptions.accentsOnly) {
						for (var i = 0, len = _replaceRules.length; i < len; i++) {
							rule = _replaceRules[i];
							replace = rule.replace();

							stylesheet.html = stylesheet.html.replace(rule.regex, replace);
						}
					}

					temp = style.cloneNode();

					temp.id = stylesheet.id;
					temp.innerHTML = stylesheet.html;
					document.head.appendChild(temp);
				}
			}
		}

		for (var key in _stylesheets) {
			if (_stylesheets.hasOwnProperty(key)) {
				promises.push(
					_.$http.get(_stylesheets[key].url)
				);
			}
		}

		Promise.all(promises)
		.then(function(stylesheets) {
			var i = 0;
			for (var key in _stylesheets) {
				if (_stylesheets.hasOwnProperty(key)) {
					_stylesheets[key].html = stylesheets[i] || '';
					i++;
				}
			}

			doInject();
		});
	}

	// Update Favicon
	function updateFavicon() {
		if (!_userOptions.favicon) {
			return;
		}

		var icon = document.createElement('link');

		// Remove Old Favicon
		var current = document.querySelectorAll('link[rel="SHORTCUT ICON"]');
		_.remove(current);

		icon.rel = 'shortcut icon';
		icon.href = _favicon.url;

		document.head.appendChild(icon);
	}

	// Update Queue
	function updateQueue() {
		if (!_userOptions.queue) {
			return;
		}

		document.querySelector('#queue-overlay').classList.add('pm-expanded-queue');
	}

	// Update Sidebar
	function updateSidebar() {
		if (!_userOptions.sidebar) {
			return;
		}

		document.body.classList.add('pm-static-sidebar');
		document.querySelector('#left-nav-open-button').click();
	}

	// Display Notification if new one exists
	function checkNotification() {
		if (!_userOptions.notify) {
			_.log('Notifications are disabled via user options, skipping check');
			return;
		}

		var notificationUrl = _.browser.url('dist/templates/notifications/' + VERSION_NUMBER + '.html');

		// No New Version
		if (typeof _userOptions.lastRun === 'string' && _.versionCompare(_userOptions.lastRun, VERSION_NUMBER) > -1) {
			_.log('Already on Current Version (v%s), Skipping Modal', _userOptions.lastRun);
			return;
		}

		_.$http.get(notificationUrl).then(function(template) {
			_.log('Show notification for version: %s', VERSION_NUMBER);
			PM.Modal.show(template, function() {
				_.browser.save({ lastRun: VERSION_NUMBER }, function() {
					_userOptions.lastRun = VERSION_NUMBER;
				});
			});
		}).catch(function() {
			if (_userOptions.lastRun !== null) {
				_.log('No new notification for Current Version (v%s), Skipping Modal', VERSION_NUMBER);

				_.browser.save({ lastRun: VERSION_NUMBER }, function() {
					_userOptions.lastRun = VERSION_NUMBER;
				});

				return;
			}

			_.log('No notification template exists for version %s, loading default', VERSION_NUMBER);

			notificationUrl = _.browser.url('dist/templates/notifications/default.html');
			_.$http.get(notificationUrl).then(function(template) {
				_.log('Show Default Notification', VERSION_NUMBER);
				PM.Modal.show(template, function() {
					_.browser.save({ lastRun: VERSION_NUMBER }, function() {
						_userOptions.lastRun = VERSION_NUMBER;
					});
				});
			}).catch(function() {
				_.log('Failed to load Default Notification');
			});
		});
	}

	// Configuration
	function config() {
		_.setVerbose(_userOptions.verbose || _dev);

		if (_.verbose()) {
			_.log('PLAY MIDNIGHT: Verbose Mode ENABLED');
			_.log('===========================================');
			_.log('PLAY MIDNIGHT: Loaded User Options');

			for (var key in _userOptions) {
				if (_userOptions.hasOwnProperty(key)) {
					_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(_userOptions[key]));
				}
			}

			chrome.storage.onChanged.addListener(function(changes) {
				_.log('PLAY MIDNIGHT: Option Changed!');

				for (var key in changes) {
					if (changes.hasOwnProperty(key)) {
						_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(changes[key]));
					}
				}
			});
		}
	}

	// Yay Initialize!
	function init() {
		_.setVerbose(_dev);

		loadOptions(function() {
			config();
			injectStyle();

			window.addEventListener('load', function() {
				PM.Options.create();
				updateFavicon();
				updateQueue();
				updateSidebar();
				checkNotification();
			});
		});
	}

	function getUserOptions() {
		return _userOptions;
	}

	function getOptionsGraph() {
		return _optionsGraph;
	}

	// Expose to Outside World
	PM.version = VERSION_NUMBER;
	PM.getUserOptions = getUserOptions;
	PM.getOptionsGraph = getOptionsGraph;
	PM.init = init;

	// Return Object for Modularity
	return PM;
})(PlayMidnightUtilities);
<commit_msg>Updated core to show default modal to new user always, rather than version number<commit_after>/*global Promise, chrome, PlayMidnightUtilities */
var PlayMidnight = (function(_){
	'use strict';

	// Our Friend
	var PM = {};

	// Dev Mode: Use CSS File rather than inline <style> (inline allows dynamic accent colors)
	var _dev = false;

	var VERSION_NUMBER = '2.1.5';

	// Reset Options when version less than
	var _resetOptions = '2.1.0';

	// Nuke All Options
	var _nukeOptions = '2.1.0';

	// All Options Defined
	var _optionsGraph = {}; // Full Options Tree (options.json)
	var _defaultOptions = {}; // Default Options
	var _userOptions = {}; // User Loaded Options

	// Favicon Attributes
	var _favicon = {
		// Load Newest Icon with Timestamp to prevent Caching
		url: _.browser.url('dist/images/favicon.ico') + '?v=' + Date.now()
	};

	// Stylesheets
	var _stylesheets = {
		main: {
			id: 'play-midnight-stylesheet',
			url: _.browser.url('dist/css/play-midnight.css'),
			html: '',
			enabled: function() {
				return _userOptions.enabled;
			}
		},

		accents: {
			id: 'play-midnight-accents',
			url: _.browser.url('dist/css/play-midnight-accents.css'),
			html: '',
			enabled: function() {
				return (_userOptions.enabled === false && _userOptions.accentsOnly);
			}
		},

		options: {
			id: 'play-midnight-options',
			url: _.browser.url('dist/css/play-midnight-options.css'),
			html: ''
		}
	};

	// Replace Rules
	var _replaceRules = [
		{
			name: 'Accent Color',
			regex: /\#fb8521/gi,
			replace: function() {
				return _userOptions.accent.color;
			}
		},
		{
			name: 'Chrome URL',
			regex: /\{CHROME_DIR\}/gi,
			replace: function() {
				return _.browser.url('/dist');
			}
		}
	];

	// Load User Options from Chrome Storage
	function loadOptions(cb) {
		_.$http.get(_.browser.url('dist/options.json'))
		.then(function(options) {
			_optionsGraph = JSON.parse(options);
			_defaultOptions = parseOptions(_optionsGraph);

			_.log('Default Options Loaded');
			for (var key in _defaultOptions) {
				if (_defaultOptions.hasOwnProperty(key)) {
					_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(_defaultOptions[key]));
				}
			}

			_.browser.get(_defaultOptions, function(options) {
				checkUpdated(options, cb);
			});
		});
	}

	// Parse Options
	function parseOptions(options, _parsed) {
		var option, rules;

		_parsed = _parsed || {};

		rules = {
			'{VERSION_NUMBER}': VERSION_NUMBER
		};

		for (var key in options) {
			if (options.hasOwnProperty(key)) {
				option = options[key];

				if (option.type === 'section') {
					parseOptions(option.options, _parsed);
				} else if (option.type === 'array') {
					_parsed[option.single] = rules[option.default] || option.default;
					_parsed[key] = option.collection;
				} else {
					if (option.type === 'string' && option.saved === false) {
						continue;
					}

					_parsed[key] = rules[option.default] || option.default;
				}
			}
		}

		return _parsed;
	}

	// Check if Play Midnight has Updated, Reset options if needed
	function checkUpdated(options, cb) {
		var skipped = ['lastRun', 'accent', 'accents', 'queue'];

		// No Version Found (< v2.0.0)
		if (options.version === undefined || _.versionCompare(options.version, _nukeOptions) === -1) {
			if (options.version === undefined) {
				_.log('PLAY MIDNIGHT: No Current Options Found, Setting to Default');
			} else {
				_.log('PLAY MIDNIGHT: Nuking All Options to Default');
			}

			_.browser.save(_defaultOptions, function() {
				_userOptions = _defaultOptions;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Version Older Than Required Reset (For Resetting to add new options)
		} else if (_.versionCompare(options.version, _resetOptions) === -1) {
			_.log('PLAY MIDNIGHT: Options Update, Forcing Reset');

			for (var key in _defaultOptions) {
				if (_defaultOptions.hasOwnProperty(key)) {
					if (options[key] === undefined || (Object.prototype.toString.call(options[key]) !== Object.prototype.toString.call(_defaultOptions[key]) && skipped.indexOf(key) === -1)) {
						_.log('Setting %s to default (User: %s, Default: %s)', key, JSON.stringify(options[key]), JSON.stringify(_defaultOptions[key]));
						options[key] = _defaultOptions[key];
					} else {
						_.log('Skipping %s, User Value: %s', key, JSON.stringify(options[key]));
					}
				}
			}

			_.browser.save(options, function() {
				_userOptions = options;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Update Version Number
		} else if (_.versionCompare(options.version, VERSION_NUMBER) === -1) {
			_.log('PLAY MIDNIGHT: Updated to version %s', VERSION_NUMBER);

			_.browser.save({ version: VERSION_NUMBER }, function() {
				_userOptions.version = VERSION_NUMBER;
				if (cb && typeof cb === 'function') {
					cb();
				}
				return;
			});

			// Options All Good
		} else {
			_userOptions = options;
			if (cb && typeof cb === 'function') {
				cb();
			}
			return;
		}

		// Something went wrong loading options, set user to default for this Run
		_userOptions = _defaultOptions;
	}

	// Inject Stylesheet
	function injectStyle() {
		var promises = [];

		function doInject() {
			var stylesheet, link, style, replace, rule, key, temp;

			// Inject Stylesheet as <link>
			if (_dev) {
				_.log('DEV MODE ENABLED: Using <link> tag');
				link = document.createElement('link');
				link.rel = 'stylesheet';
				link.type = 'text/css';

				for (key in _stylesheets) {
					if (_stylesheets.hasOwnProperty(key)) {
						stylesheet = _stylesheets[key];

						if (stylesheet.hasOwnProperty('enabled') && !stylesheet.enabled()) {
							continue;
						}

						temp = link.cloneNode();

						temp.href = stylesheet.url;
						document.head.appendChild(temp);
					}
				}

				return;
			}

			_.log('DEV MODE DISABLED: Using <style> tag');
			style = document.createElement('style');
			style.type = 'text/css';

			for (key in _stylesheets) {
				if (_stylesheets.hasOwnProperty(key)) {
					stylesheet = _stylesheets[key];

					if (stylesheet.hasOwnProperty('enabled') && !stylesheet.enabled()) {
						continue;
					}

					if (_userOptions.enabled || _userOptions.accentsOnly) {
						for (var i = 0, len = _replaceRules.length; i < len; i++) {
							rule = _replaceRules[i];
							replace = rule.replace();

							stylesheet.html = stylesheet.html.replace(rule.regex, replace);
						}
					}

					temp = style.cloneNode();

					temp.id = stylesheet.id;
					temp.innerHTML = stylesheet.html;
					document.head.appendChild(temp);
				}
			}
		}

		for (var key in _stylesheets) {
			if (_stylesheets.hasOwnProperty(key)) {
				promises.push(
					_.$http.get(_stylesheets[key].url)
				);
			}
		}

		Promise.all(promises)
		.then(function(stylesheets) {
			var i = 0;
			for (var key in _stylesheets) {
				if (_stylesheets.hasOwnProperty(key)) {
					_stylesheets[key].html = stylesheets[i] || '';
					i++;
				}
			}

			doInject();
		});
	}

	// Update Favicon
	function updateFavicon() {
		if (!_userOptions.favicon) {
			return;
		}

		var icon = document.createElement('link');

		// Remove Old Favicon
		var current = document.querySelectorAll('link[rel="SHORTCUT ICON"]');
		_.remove(current);

		icon.rel = 'shortcut icon';
		icon.href = _favicon.url;

		document.head.appendChild(icon);
	}

	// Update Queue
	function updateQueue() {
		if (!_userOptions.queue) {
			return;
		}

		document.querySelector('#queue-overlay').classList.add('pm-expanded-queue');
	}

	// Update Sidebar
	function updateSidebar() {
		if (!_userOptions.sidebar) {
			return;
		}

		document.body.classList.add('pm-static-sidebar');
		document.querySelector('#left-nav-open-button').click();
	}

	// Display Notification if new one exists
	function checkNotification() {
		if (!_userOptions.notify) {
			_.log('Notifications are disabled via user options, skipping check');
			return;
		}

		var notificationUrl = _.browser.url('dist/templates/notifications/' + VERSION_NUMBER + '.html');

		// No New Version
		if (typeof _userOptions.lastRun === 'string' && _.versionCompare(_userOptions.lastRun, VERSION_NUMBER) > -1) {
			_.log('Already on Current Version (v%s), Skipping Modal', _userOptions.lastRun);
			return;
		}

		// New User
		if (_userOptions.lastRun === null) {
			_.log('New user, loading default', VERSION_NUMBER);

			notificationUrl = _.browser.url('dist/templates/notifications/default.html');
			_.$http.get(notificationUrl).then(function(template) {
				_.log('Show Default Notification', VERSION_NUMBER);
				PM.Modal.show(template, function() {
					_.browser.save({ lastRun: VERSION_NUMBER }, function() {
						_userOptions.lastRun = VERSION_NUMBER;
					});
				});
			}).catch(function() {
				_.log('Failed to load Default Notification');
			});

			return;

		// Returning User, check for new modal
		} else {
			_.$http.get(notificationUrl).then(function(template) {
				_.log('Show notification for version: %s', VERSION_NUMBER);
				PM.Modal.show(template, function() {
					_.browser.save({ lastRun: VERSION_NUMBER }, function() {
						_userOptions.lastRun = VERSION_NUMBER;
					});
				});
			}).catch(function() {
				_.log('No new notification for Current Version (v%s), Skipping Modal', VERSION_NUMBER);

				_.browser.save({ lastRun: VERSION_NUMBER }, function() {
					_userOptions.lastRun = VERSION_NUMBER;
				});

				return;
			});
		}
	}

	// Configuration
	function config() {
		_.setVerbose(_userOptions.verbose || _dev);

		if (_.verbose()) {
			_.log('PLAY MIDNIGHT: Verbose Mode ENABLED');
			_.log('===========================================');
			_.log('PLAY MIDNIGHT: Loaded User Options');

			for (var key in _userOptions) {
				if (_userOptions.hasOwnProperty(key)) {
					_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(_userOptions[key]));
				}
			}

			chrome.storage.onChanged.addListener(function(changes) {
				_.log('PLAY MIDNIGHT: Option Changed!');

				for (var key in changes) {
					if (changes.hasOwnProperty(key)) {
						_.log('%s: %s', key.toString().toUpperCase(), JSON.stringify(changes[key]));
					}
				}
			});
		}
	}

	// Yay Initialize!
	function init() {
		_.setVerbose(_dev);

		loadOptions(function() {
			config();
			injectStyle();

			window.addEventListener('load', function() {
				PM.Options.create();
				updateFavicon();
				updateQueue();
				updateSidebar();
				checkNotification();
			});
		});
	}

	function getUserOptions() {
		return _userOptions;
	}

	function getOptionsGraph() {
		return _optionsGraph;
	}

	// Expose to Outside World
	PM.version = VERSION_NUMBER;
	PM.getUserOptions = getUserOptions;
	PM.getOptionsGraph = getOptionsGraph;
	PM.init = init;

	// Return Object for Modularity
	return PM;
})(PlayMidnightUtilities);
<|endoftext|>