<commit_before>/*
	ActionManager
*/
"use strict";

var KEY = require('../opts/keys.js'),
	PointerTracker = require('./pointerTracker.js'),
	Action = require('../bits/action.js'),
	utils = require('../utils/utils.js'),
	ActionManager = function () {},
	actionManager,
	allActions = [],
	activeTokens = [],
	deactivateQueue = [],
	baseActions = {};

ActionManager.prototype = {

	/*
		Create action

		@return [Action]
	*/
	create: function () {
		var action = new Action();

		this.register(action);

		return action;
	},

	/*
		Change action

		@param [Token]: Token for the action
		@param [object]: Value properties
		@param [object]: Action options
	*/
	change: function (token, changes, e) {
		var action = this.get(token);

        if (changes.link === KEY.LINK.POINTER) {
			changes.pointerOffset = PointerTracker.start(e);
        }

		action.set(changes);
	},

	/*
		Define a base action

		Can be called later with .play()

		@param [object]
	*/
	define: function (actions) {
		var key,
			chain,
			baseAction = {};

		for (key in actions) {
			if (actions.hasOwnProperty(key)) {
				if (baseActions[key] && !actions[key].forceOverride) {
					throw KEY.ERROR.ACTION_EXISTS;
				} else {
					chain = key.split('.');

					// If there's an inheritence chain, merge
					// TODO: multilayered inheritence?
					if (chain.length > 1) {
						if (baseActions[chain[0]]) {
							baseActions[key] = utils.merge(baseActions[chain[0]], actions[key]);
						// if we can't find action
						} else {
							throw KEY.ERROR.NO_ACTION;
						}
					// Else directly copy
					} else {
						baseActions[key] = actions[key];
					}
				}
			}
		}
	},

	/*
    	Create base action

    	@param [string || array || object]:
    	    String: Name or space-delimited playlist of actions
    	    Array: Playlist of actions
    	    Object: Raw action
        @param [object]: Action override
	*/
	createBase: function (defs, override) {
    	var baseAction = {},
    	    actionList = [];

        // If this is a straight action
        if (utils.isObj(defs)) {
            baseAction = defs;

        // These are previously defined actions
        } else {
            // Comma-delimited string or single action name
            if (utils.isString(defs)) {
                actionList = defs.split(" ");

            // Array of action names
            } else {
                actionList = defs;
            }

            baseAction = this.getDefined(actionList[0]);
            baseAction.playlist = actionList;
            baseAction.playhead = 0;
        }

        // Apply overrides if present
        if (utils.isObj(override)) {
            baseAction = utils.merge(baseAction, override);
        }

        return baseAction;
	},

	/*
    	Get defined action

    	@param [string]: The name of the predefined action
	*/
	getDefined: function (key) {
		return utils.copy(baseActions[key]);
	},

	/*
		Register action

		@param [Action]: Add action to storage array
	*/
	register: function (action) {
		allActions[action.token] = action;
	},

	/*
		Get the specified action

		@param [Token]: Token
		@return [Action || boolean]: Action with specified token, false if none found
	*/
	get: function (token) {
		return allActions[token] || false;
	},

	/*
    	Get tokens of all active actions

    	@return [array]: Array of active tokens
	*/
	getActiveTokens: function () {
    	return activeTokens;
	},

	/*
    	Activate specified action

    	Activates action and adds to activeTokens array

    	@param [Token]: Token
	*/
	activate: function (token) {
    	var action = this.get(token),
	        index = activeTokens.indexOf(token),
	        deactivateIndex = deactivateQueue.indexOf(token);

    	if (index === -1) {
            activeTokens.push(token);
        }

        // Remove from deactivate queue if it's been placed there
        if (deactivateIndex >= 0) {
            deactivateQueue.splice(deactivateIndex, 1);
        }

    	action.start();
	},

	/*
    	Deactivate specified action

    	Activates action and removes from activeTokens array

    	@param [Token]: Token
	*/
	deactivate: function (token) {
    	var action = this.get(token),
	        index = activeTokens.indexOf(token);

	    action.stop();

    	if (index > -1) {
    	    activeTokens.splice(index, 1);
    	}
	},

	/*
    	Purge deactivate queue

    	Loops through the deactivate queue and decides whether to deactivate
    	or swap action parameters with the next in the action's playList

    	We use a deactivate queue rather than deactivate as soon as we process it
    	because if we manipulate the list of activated Actions while it's being
    	looped through, well you can only imagine the fun that causes.
	*/
	purge: function () {
	    var queueLength = deactivateQueue.length;

	    // Run through all queued actions and decide what to do next
	    for (var i = 0; i < queueLength; ++i) {
	        if (utils.isNum(deactivateQueue[i])) {
    	        this.decideNext(deactivateQueue[i]);
	        }
	    }

        // Clear deactivateQueue
		deactivateQueue = [];
	},

	/*
    	Decide what to do with an action

    	Takes an action and decides, based on its playlist and loop properties,
    	what to do with it next.

    	@param [Token]: Action token
	*/
	decideNext: function (token) {
    	if (!this.loop(token) && !this.yoyo(token) && !this.playNext(token)) {
        	this.deactivate(token);
    	}
	},

	/*
    	Play next in playlist, if exists

    	@param [Token]: Action token
    	@return [boolean]: Success
	*/
	playNext: function (token) {
    	var hasPlayedNext = false,
    	    action = this.get(token),
    	    playlistLength = action.playlist ? action.playlist.length : 0;

        // Check we have a playlist and that this is an animation
        // TODO: Maybe make a set of properties on the rubix that says allowPlaylist: true
    	if (playlistLength && action.link === KEY.LINK.TIME) {
    	    ++action.playhead;
    	    this.change(token, this.getDefined(action.playlist[action.playhead]));
    	    this.activate(token);
    	    hasPlayedNext = true;
    	}

    	return hasPlayedNext;
	},

	/*
    	Loop current action, if we're that way inclined

    	@param [Token]: Action token
    	@return [boolean]: Success
	*/
	loop: function (token) {
    	var hasLooped = false,
    	    action = this.get(token),
    	    loopForever = (action.loop === true);

        if (action.link === KEY.LINK.TIME && (loopForever || utils.isNum(action.loop))) {
            ++action.loopCount;
            if ((loopForever || utils.isNum(action.loop) && action.loopCount <= action.loop)) {
	            action.resetValues();
	            this.activate(token);
	            hasLooped = true;
            }
        }

    	return hasLooped;
	},

	yoyo: function (token) {
		var hasYoyoed = false,
			action = this.get(token),
			yoyoForever = (action.yoyo === true);

		if (action.link === KEY.LINK.TIME && (yoyoForever || utils.isNum(action.yoyo))) {
			++action.yoyoCount;
			if (yoyoForever || (utils.isNum(action.yoyo) && action.yoyoCount <= action.yoyo)) {
				action.reverseValues();
				this.activate(token);
				hasYoyoed = true;
			}
		}

		return hasYoyoed;
	},

	/*
    	Add token to the deactivate queue

    	Queue gets processed at the end of every frame

    	@param [Token]: Token of action
	*/
	queueDeactivate: function (token) {
		deactivateQueue.push(token);
	},

	/*
    	Get data from our action's data object

    	@param [Token]: Action token
    	@param [string]: Key of data point
	*/
	getData: function (token, key) {
	    var action = this.get(token);

    	return action.data ? action.data[key] : undefined;
	},

	/*
    	Set data point to action's data object

    	@param [Token]: Action token
    	@param [object]: Data to save to action
	*/
	setData: function (token, data) {
    	var action = this.get(token);

    	for (var key in data) {
        	if (data.hasOwnProperty(key)) {
            	action.data[key] = data[key];

            	if (key === KEY.JQUERY_ELEMENT) {
                	action.data = data[key];
            	}
        	}
    	}
	}
};

actionManager = new ActionManager();

module.exports = actionManager;<commit_msg>cant rememeber<commit_after>/*
	ActionManager
*/
"use strict";

var KEY = require('../opts/keys.js'),
	PointerTracker = require('./pointerTracker.js'),
	Action = require('../bits/action.js'),
	utils = require('../utils/utils.js'),
	ActionManager = function () {},
	actionManager,
	allActions = [],
	activeTokens = [],
	deactivateQueue = [],
	baseActions = {};

ActionManager.prototype = {

	/*
		Create action

		@return [Action]
	*/
	create: function () {
		var action = new Action();

		this.register(action);

		return action;
	},

	/*
		Change action

		@param [Token]: Token for the action
		@param [object]: Value properties
		@param [object]: Action options
	*/
	change: function (token, changes, e) {
		var action = this.get(token);

        if (changes.link === KEY.LINK.POINTER) {
			changes.pointerOffset = PointerTracker.start(e);
        }

		action.set(changes);
	},

	/*
		Define a base action

		Can be called later with .play()

		@param [object]
	*/
	define: function (actions) {
		var key,
			chain,
			baseAction = {};

		for (key in actions) {
			if (actions.hasOwnProperty(key)) {
				if (baseActions[key] && !actions[key].forceOverride) {
					throw KEY.ERROR.ACTION_EXISTS;
				} else {
					chain = key.split('.');

					// If there's an inheritence chain, merge
					// TODO: multilayered inheritence?
					if (chain.length > 1) {
						if (baseActions[chain[0]]) {
							baseActions[key] = utils.merge(baseActions[chain[0]], actions[key]);
						// if we can't find action
						} else {
							throw KEY.ERROR.NO_ACTION;
						}
					// Else directly copy
					} else {
						baseActions[key] = actions[key];
					}
				}
			}
		}
	},

	/*
    	Create base action

    	@param [string || array || object]:
    	    String: Name or space-delimited playlist of actions
    	    Array: Playlist of actions
    	    Object: Raw action
        @param [object]: Action override
	*/
	createBase: function (defs, override) {
    	var baseAction = {},
    	    actionList = [];

        // If this is a straight action
        if (utils.isObj(defs)) {
            baseAction = defs;

        // These are previously defined actions
        } else {
            // Comma-delimited string or single action name
            if (utils.isString(defs)) {
                actionList = defs.split(" ");

            // Array of action names
            } else {
                actionList = defs;
            }

            baseAction = this.getDefined(actionList[0]);
            baseAction.playlist = actionList;
            baseAction.playhead = 0;
        }

        // Apply overrides if present
        if (utils.isObj(override)) {
            baseAction = utils.merge(baseAction, override);
        }

        return baseAction;
	},

	/*
    	Get defined action

    	@param [string]: The name of the predefined action
	*/
	getDefined: function (key) {
		return utils.copy(baseActions[key]);
	},

	/*
		Register action

		@param [Action]: Add action to storage array
	*/
	register: function (action) {
		allActions[action.token] = action;
	},

	/*
		Get the specified action

		@param [Token]: Token
		@return [Action || boolean]: Action with specified token, false if none found
	*/
	get: function (token) {
		return allActions[token] || false;
	},

	/*
    	Get tokens of all active actions

    	@return [array]: Array of active tokens
	*/
	getActiveTokens: function () {
    	return activeTokens;
	},

	/*
    	Activate specified action

    	Activates action and adds to activeTokens array

    	@param [Token]: Token
	*/
	activate: function (token) {
    	var action = this.get(token),
	        index = activeTokens.indexOf(token),
	        deactivateIndex = deactivateQueue.indexOf(token);

    	if (index === -1) {
            activeTokens.push(token);
        }

        // Remove from deactivate queue if it's been placed there
        if (deactivateIndex >= 0) {
            deactivateQueue.splice(deactivateIndex, 1);
        }

    	action.start();
	},

	/*
    	Deactivate specified action

    	Activates action and removes from activeTokens array

    	@param [Token]: Token
	*/
	deactivate: function (token) {
    	var action = this.get(token),
	        index = activeTokens.indexOf(token);

	    action.stop();

    	if (index > -1) {
    	    activeTokens.splice(index, 1);
    	}
	},

	/*
    	Purge deactivate queue

    	Loops through the deactivate queue and decides whether to deactivate
    	or swap action parameters with the next in the action's playList

    	We use a deactivate queue rather than deactivate as soon as we process it
    	because if we manipulate the list of activated Actions while it's being
    	looped through, well you can only imagine the fun that causes.
	*/
	purge: function () {
	    var queueLength = deactivateQueue.length;

	    // Run through all queued actions and decide what to do next
	    for (var i = 0; i < queueLength; ++i) {
	        if (utils.isNum(deactivateQueue[i])) {
    	        this.decideNext(deactivateQueue[i]);
	        }
	    }

        // Clear deactivateQueue
		deactivateQueue = [];
	},

	/*
    	Decide what to do with an action

    	Takes an action and decides, based on its playlist and loop properties,
    	what to do with it next.

    	@param [Token]: Action token
	*/
	decideNext: function (token) {
		var nexts = ['loop', 'yoyo', 'playNext'],
			num = nexts.length,
			hasFuture = false;

		for (var i = 0; i < num; ++i) {
			if (this[nexts[i]](token)) {
				hasFuture = true;
				break;
			}
		}

    	if (!hasFuture) {
        	this.deactivate(token);
    	}
	},

	/*
    	Play next in playlist, if exists

    	@param [Token]: Action token
    	@return [boolean]: Success
	*/
	playNext: function (token) {
    	var hasPlayedNext = false,
    	    action = this.get(token),
    	    playlistLength = action.playlist ? action.playlist.length : 0;

        // Check we have a playlist and that this is an animation
        // TODO: Maybe make a set of properties on the rubix that says allowPlaylist: true
    	if (playlistLength && action.link === KEY.LINK.TIME) {
    	    ++action.playhead;
    	    this.change(token, this.getDefined(action.playlist[action.playhead]));
    	    this.activate(token);
    	    hasPlayedNext = true;
    	}

    	return hasPlayedNext;
	},

	/*
    	Loop current action, if we're that way inclined

    	@param [Token]: Action token
    	@return [boolean]: Success
	*/
	loop: function (token) {
    	var hasLooped = false,
    	    action = this.get(token),
    	    loopForever = (action.loop === true);

        if (action.link === KEY.LINK.TIME && (loopForever || utils.isNum(action.loop))) {
            ++action.loopCount;
            if ((loopForever || utils.isNum(action.loop) && action.loopCount <= action.loop)) {
	            action.resetValues();
	            this.activate(token);
	            hasLooped = true;
            }
        }

    	return hasLooped;
	},

	yoyo: function (token) {
		var hasYoyoed = false,
			action = this.get(token),
			yoyoForever = (action.yoyo === true);

		if (action.link === KEY.LINK.TIME && (yoyoForever || utils.isNum(action.yoyo))) {
			++action.yoyoCount;
			if (yoyoForever || (utils.isNum(action.yoyo) && action.yoyoCount <= action.yoyo)) {
				action.reverseValues();
				this.activate(token);
				hasYoyoed = true;
			}
		}

		return hasYoyoed;
	},

	/*
    	Add token to the deactivate queue

    	Queue gets processed at the end of every frame

    	@param [Token]: Token of action
	*/
	queueDeactivate: function (token) {
		deactivateQueue.push(token);
	},

	/*
    	Get data from our action's data object

    	@param [Token]: Action token
    	@param [string]: Key of data point
	*/
	getData: function (token, key) {
	    var action = this.get(token);

    	return action.data ? action.data[key] : undefined;
	},

	/*
    	Set data point to action's data object

    	@param [Token]: Action token
    	@param [object]: Data to save to action
	*/
	setData: function (token, data) {
    	var action = this.get(token);

    	for (var key in data) {
        	if (data.hasOwnProperty(key)) {
            	action.data[key] = data[key];

            	if (key === KEY.JQUERY_ELEMENT) {
                	action.data = data[key];
            	}
        	}
    	}
	}
};

actionManager = new ActionManager();

module.exports = actionManager;<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import { BinaryChart } from 'binary-charts'
import { logoutAllTokens } from 'binary-common-utils/lib/account'
import { observer } from 'binary-common-utils/lib/observer'
import { getTokenList, removeAllTokens, get as getStorage, set as setStorage, getToken,
} from 'binary-common-utils/lib/storageManager'
import TradeInfo from './tradeInfo'
import _Blockly from './blockly'
import { translator } from '../../common/translator'
import Welcome from './tours/welcome'
import Introduction from './tours/introduction'
import MakeSimpleStrategy from './tours/makeSimpleStrategy'
import { logHandler } from './logger'
import { SaveXml } from './react-components/SaveXml'
import { RestartTimeout } from './react-components/RestartTimeout'

let realityCheckTimeout

const showRealityCheck = () => {
  $('.blocker').show()
  $('.reality-check').show()
}

const hideRealityCheck = () => {
  $('.blocker').hide()
  $('.reality-check').hide()
}

const stopRealityCheck = () => {
  clearInterval(realityCheckTimeout)
  realityCheckTimeout = null
}

const realityCheckInterval = () => {
  realityCheckTimeout = setInterval(() => {
    const now = parseInt((new Date().getTime()) / 1000, 10)
    const checkTime = +getStorage('realityCheckTime')
    if (checkTime && now >= checkTime) {
      showRealityCheck()
      stopRealityCheck()
    }
  }, 1000)
}

const startRealityCheck = (time, token) => {
  stopRealityCheck()
  if (time) {
    const start = parseInt((new Date().getTime()) / 1000, 10) + (time * 60)
    setStorage('realityCheckTime', start)
    realityCheckInterval()
  } else {
    const tokenObj = getToken(token)
    if (tokenObj.hasRealityCheck) {
      const checkTime = +getStorage('realityCheckTime')
      if (!checkTime) {
        showRealityCheck()
      } else {
        realityCheckInterval()
      }
    }
  }
}

const clearRealityCheck = () => {
  setStorage('realityCheckTime', null)
  stopRealityCheck()
}

const resetRealityCheck = (token) => {
  clearRealityCheck()
  startRealityCheck(null, token)
}

export default class View {
  constructor() {
    this.chartType = 'line'
    this.tours = {}
    logHandler()
    this.tradeInfo = new TradeInfo()
    this.addTranslationToUi()
    this.initPromise = new Promise((resolve) => {
      this.updateTokenList()
      this.blockly = new _Blockly()
      this.blockly.initPromise.then(() => {
        $('.actions_menu').show()
        this.setElementActions()
        this.initTours()
        $('#accountLis')
        startRealityCheck(null, $('.account-id').first().attr('value'))
        resolve()
      })
    })
  }
  updateTokenList() {
    const tokenList = getTokenList()
    const loginButton = $('#login')
    const accountList = $('#account-list')
    if (tokenList.length === 0) {
      loginButton.show()
      accountList.hide()
    } else {
      loginButton.hide()
      accountList.show()
      for (const tokenInfo of tokenList) {
        let prefix = ''
        if ('isVirtual' in tokenInfo) {
          prefix = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account'
        }
        if (tokenList.indexOf(tokenInfo) === 0) {
          $('.account-id').attr('value', `${tokenInfo.token}`)
            .text(`${tokenInfo.account_name}`)
          $('.account-type').text(`${prefix}`)
        } else {
          $('.login-id-list').append(`<a href="#" value="${tokenInfo.token}"><li><span>${prefix}</span><div>${tokenInfo.account_name}</div></li></a>` +
            '

');
        }
      }
    }
  }
  addTranslationToUi() {
    $('[data-i18n-text]')
      .each(function each() {
        const contents = $(this).contents()
        if (contents.length > 0) {
          if (contents.get(0).nodeType === Node.TEXT_NODE) {
            $(this).text(translator.translateText($(this)
              .attr('data-i18n-text')))
              .append(contents.slice(1))
          }
        } else {
          $(this)
            .text(translator.translateText($(this)
              .attr('data-i18n-text')))
        }
      })
  }
  initTours() {
    this.tours.introduction = new Introduction()
    this.tours.welcome = new Welcome()
    this.tours.makeSimpleStrategy = new MakeSimpleStrategy()
  }
  startTour() {
    const viewScope = this
    $('#tours').on('change', function onChange() {
      const value = $(this).val()
      if (value === '') return
      if (viewScope.activeTour) {
        viewScope.activeTour.stop()
      }
      viewScope.activeTour = viewScope.tours[value]
      viewScope.activeTour.start(() => {
        viewScope.activeTour = null
      })
    })
  }
  setFileBrowser() {
    const readFile = (f, dropEvent = {}) => {
      const reader = new FileReader()
      reader.onload = e => this.blockly.load(e.target.result, dropEvent)
      reader.readAsText(f)
    }

    const handleFileSelect = (e) => {
      let files
      let dropEvent
      if (e.type === 'drop') {
        e.stopPropagation()
        e.preventDefault()
        files = e.dataTransfer.files
        dropEvent = e
      } else {
        files = e.target.files
      }
      files = [...files]
      for (const file of files) {
        if (file.type.match('text/xml')) {
          readFile(file, dropEvent)
        } else {
          observer.emit('ui.log.info', `${
          translator.translateText('File is not supported:')} ${file.name}`)
        }
      }
    }

    const handleDragOver = (e) => {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy'; // eslint-disable-line no-param-reassign
    }

    const dropZone = document.body

    dropZone.addEventListener('dragover', handleDragOver, false)
    dropZone.addEventListener('drop', handleFileSelect, false)
    if (document.getElementById('files')) {
      document.getElementById('files')
        .addEventListener('change', handleFileSelect, false)
    }
    $('#open_btn')
      .on('click', () => {
        $.FileDialog({ // eslint-disable-line new-cap
          accept: '.xml',
          cancelButton: 'Close',
          dragMessage: 'Drop files here',
          dropheight: 400,
          errorMessage: 'An error occured while loading file',
          multiple: false,
          okButton: 'OK',
          readAs: 'DataURL',
          removeMessage: 'Remove file',
          title: 'Load file',
        })
      })
      .on('files.bs.filedialog', (ev) => {
        handleFileSelect(ev.files)
      })
      .on('cancel.bs.filedialog', (ev) => {
        handleFileSelect(ev)
      })
  }
  setElementActions() {
    this.setFileBrowser()
    this.startTour()
    this.addBindings()
    this.addEventHandlers()
  }
  addBindings() {
    const logout = () => {
      logoutAllTokens(() => {
        this.updateTokenList()
        observer.emit('ui.log.info', translator.translateText('Logged you out!'))
        clearRealityCheck()
      })
    }

    $('#stopButton')
      .click(e => {
        if (e) {
          e.preventDefault()
        }
        stopRealityCheck()
        window.Bot.stop()
      })
      .hide()

    $('.panelExitButton')
      .click(function onClick() {
        $(this)
          .parent()
          .hide()
      })

    $('.panel')
      .hide()

    $('.panel')
      .drags()

    $('.panel .content')
      .mousedown(e => e.stopPropagation()) // prevent content to trigger draggable

    ReactDOM.render(
      <SaveXml
      onSave={(filename, collection) => this.blockly.save(filename, collection)}
      />
      , $('#saveXml')[0])

    $('#saveXml')
      .click(() => {
        $('#saveAs').show()
      })

    $('#undo')
      .click(() => {
        this.blockly.undo()
      })

    $('#redo')
      .click(() => {
        this.blockly.redo()
      })

    $('#zoomIn')
      .click(() => {
        this.blockly.zoomOnPlusMinus(true)
      })

    $('#zoomOut')
      .click(() => {
        this.blockly.zoomOnPlusMinus(false)
      })

    $('#cleanUp')
      .click(() => {
        this.blockly.cleanUp()
      })

    $('#showSummary')
      .click(() => {
        $('#summaryPanel')
          .show()
      })

    $('#loadXml')
      .click(() => {
        $('#files')
          .click()
      })

    $('#logout')
      .click(() => {
        logout()
        $('.logout').hide()
      })

    $('#continueTrading')
      .click(() => {
        const time = parseInt($('#realityDuration').val(), 10)
        if (time >= 10 && time <= 120) {
          $('#rc-err').hide()
          hideRealityCheck()
          startRealityCheck(time)
        } else {
          $('#rc-err').show()
        }
      })

    $('#runButton')
      .click(() => {
        $('#stopButton').show()
        $('#runButton').hide()
        this.blockly.run()
      })

    $('#resetButton')
      .click(() => {
        this.blockly.resetWorkspace()
      })

    $('.login-id-list')
      .on('click', 'a', (e) => {
        resetRealityCheck($(e.currentTarget).attr('value'))
        e.preventDefault()
        const $el = $(e.currentTarget)
        const $oldType = $el.find('li span')
        const $oldTypeText = $oldType.text()
        const $oldID = $el.find('li div')
        const $oldIDText = $oldID.text()
        const $oldValue = $el.attr('value')
        const $newType = $('.account-type')
        const $newTypeText = $newType.first().text()
        const $newID = $('.account-id')
        const $newIDText = $newID.first().text()
        const $newValue = $newID.attr('value')
        $oldType.html($newTypeText)
        $oldID.html($newIDText)
        $el.attr('value', $newValue)
        $newType.html($oldTypeText)
        $newID.html($oldIDText)
        $newID.attr('value', $oldValue)
      })

    $('#login')
      .bind('click.login', () => {
        document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' +
          `${getStorage('appId')}&l=${translator.getLanguage().toUpperCase()}`
      })
      .text('Log in')

    $(document).keydown((e) => {
      if (e.which === 189) { // -
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(false)
          e.preventDefault()
        }
      } else if (e.which === 187) { // +
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(true)
          e.preventDefault()
        }
      } else if (e.which === 39) { // right
        if (this.activeTour) {
          this.activeTour.next()
          e.preventDefault()
        }
      } else if (e.which === 27) { // Esc
        const exitButton = $('.panel:hover .panelExitButton')
        if (exitButton.length === 1) {
          exitButton.click()
          e.preventDefault()
        }
      }
    })
  }
  updateChart(info) {
    const chartToDataType = {
      area: 'ticks',
      line: 'ticks',
      candlestick: 'ohlc',
      ohlc: 'ohlc',
    }

    const isLine = () => ['area', 'line'].indexOf(this.chartType) >= 0

    const zoomInMax = (ev, chart) => {
      const {
        dataMax,
      } = chart.xAxis[0].getExtremes()
      const {
        minRange,
      } = chart.xAxis[0].options
      chart.xAxis[0].setExtremes(dataMax - minRange, dataMax)
    }

    const events = [
      {
        type: 'zoom-in-max',
        handler: zoomInMax,
      },
    ]

    if (isLine() && this.contractForChart) {
      const chartDiv = document.getElementById('trade-chart0')
      if (chartDiv) {
        chartDiv.dispatchEvent(new Event('zoom-in-max'))
      }
    }
    ReactDOM.render(
      <BinaryChart
      className="trade-chart"
      id="trade-chart0"
      contract={isLine() ? this.contractForChart : false}
      hideZoomControls={isLine() && this.contractForChart}
      pipSize={Number(Number(info.pip).toExponential().substring(3))}
      shiftMode={this.contractForChart ? 'dynamic' : 'fixed'}
      ticks={info[chartToDataType[this.chartType]]}
      type={this.chartType}
      events={events}
      hideIntervalPicker
      onTypeChange={(type) => (this.chartType = type)}
      />, $('#chart')[0])
  }
  addEventHandlers() {
    for (const errorType of ['api.error', 'BlocklyError', 'RuntimeError']) {
      observer.register(errorType, (error) => { // eslint-disable-line no-loop-func
        if (error.error && error.error.code === 'InvalidToken') {
          removeAllTokens()
          this.updateTokenList()
        }
        window.Bot.stop()
        if (window.Bot.shouldRestartOnError()) {
          ReactDOM.render(
            <RestartTimeout
            timeout="3"
            />
            , document.getElementById('restartTimeout'))
        }
      })
    }

    observer.register('bot.stop', () => {
      $('#runButton').show()
      $('#stopButton').hide()
    })

    observer.register('bot.tradeInfo', (tradeInfo) => {
      for (const key of Object.keys(tradeInfo)) {
        this.tradeInfo.tradeInfo[key] = tradeInfo[key]
      }
      this.tradeInfo.update()
    })

    observer.register('bot.tradeUpdate', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = {
        ...contract,
      }
      this.contractForChart.date_expiry = Number(this.contractForChart.date_expiry)
      this.contractForChart.date_settlement = Number(this.contractForChart.date_settlement)
      this.contractForChart.date_start = Number(this.contractForChart.date_start)
    })

    observer.register('bot.finish', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = false
    })

    observer.register('bot.tickUpdate', (info) => {
      this.updateChart(info)
    })
  }
}
<commit_msg>Remove account list on logout or invalid token<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import { BinaryChart } from 'binary-charts'
import { logoutAllTokens } from 'binary-common-utils/lib/account'
import { observer } from 'binary-common-utils/lib/observer'
import { getTokenList, removeAllTokens, get as getStorage, set as setStorage, getToken,
} from 'binary-common-utils/lib/storageManager'
import TradeInfo from './tradeInfo'
import _Blockly from './blockly'
import { translator } from '../../common/translator'
import Welcome from './tours/welcome'
import Introduction from './tours/introduction'
import MakeSimpleStrategy from './tours/makeSimpleStrategy'
import { logHandler } from './logger'
import { SaveXml } from './react-components/SaveXml'
import { RestartTimeout } from './react-components/RestartTimeout'

let realityCheckTimeout

const showRealityCheck = () => {
  $('.blocker').show()
  $('.reality-check').show()
}

const hideRealityCheck = () => {
  $('.blocker').hide()
  $('.reality-check').hide()
}

const stopRealityCheck = () => {
  clearInterval(realityCheckTimeout)
  realityCheckTimeout = null
}

const realityCheckInterval = () => {
  realityCheckTimeout = setInterval(() => {
    const now = parseInt((new Date().getTime()) / 1000, 10)
    const checkTime = +getStorage('realityCheckTime')
    if (checkTime && now >= checkTime) {
      showRealityCheck()
      stopRealityCheck()
    }
  }, 1000)
}

const startRealityCheck = (time, token) => {
  stopRealityCheck()
  if (time) {
    const start = parseInt((new Date().getTime()) / 1000, 10) + (time * 60)
    setStorage('realityCheckTime', start)
    realityCheckInterval()
  } else {
    const tokenObj = getToken(token)
    if (tokenObj.hasRealityCheck) {
      const checkTime = +getStorage('realityCheckTime')
      if (!checkTime) {
        showRealityCheck()
      } else {
        realityCheckInterval()
      }
    }
  }
}

const clearRealityCheck = () => {
  setStorage('realityCheckTime', null)
  stopRealityCheck()
}

const resetRealityCheck = (token) => {
  clearRealityCheck()
  startRealityCheck(null, token)
}

export default class View {
  constructor() {
    this.chartType = 'line'
    this.tours = {}
    logHandler()
    this.tradeInfo = new TradeInfo()
    this.addTranslationToUi()
    this.initPromise = new Promise((resolve) => {
      this.updateTokenList()
      this.blockly = new _Blockly()
      this.blockly.initPromise.then(() => {
        $('.actions_menu').show()
        this.setElementActions()
        this.initTours()
        $('#accountLis')
        startRealityCheck(null, $('.account-id').first().attr('value'))
        resolve()
      })
    })
  }
  updateTokenList() {
    const tokenList = getTokenList()
    const loginButton = $('#login')
    const accountList = $('#account-list')
    if (tokenList.length === 0) {
      loginButton.show()
      accountList.hide()
      $('.account-id').removeAttr('value').text('')
      $('.account-type').text('')
      $('.login-id-list').children().remove()
    } else {
      loginButton.hide()
      accountList.show()
      for (const tokenInfo of tokenList) {
        let prefix = ''
        if ('isVirtual' in tokenInfo) {
          prefix = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account'
        }
        if (tokenList.indexOf(tokenInfo) === 0) {
          $('.account-id').attr('value', `${tokenInfo.token}`)
            .text(`${tokenInfo.account_name}`)
          $('.account-type').text(`${prefix}`)
        } else {
          $('.login-id-list').append(`<a href="#" value="${tokenInfo.token}"><li><span>${prefix}</span><div>${tokenInfo.account_name}</div></li></a>` +
            '

');
        }
      }
    }
  }
  addTranslationToUi() {
    $('[data-i18n-text]')
      .each(function each() {
        const contents = $(this).contents()
        if (contents.length > 0) {
          if (contents.get(0).nodeType === Node.TEXT_NODE) {
            $(this).text(translator.translateText($(this)
              .attr('data-i18n-text')))
              .append(contents.slice(1))
          }
        } else {
          $(this)
            .text(translator.translateText($(this)
              .attr('data-i18n-text')))
        }
      })
  }
  initTours() {
    this.tours.introduction = new Introduction()
    this.tours.welcome = new Welcome()
    this.tours.makeSimpleStrategy = new MakeSimpleStrategy()
  }
  startTour() {
    const viewScope = this
    $('#tours').on('change', function onChange() {
      const value = $(this).val()
      if (value === '') return
      if (viewScope.activeTour) {
        viewScope.activeTour.stop()
      }
      viewScope.activeTour = viewScope.tours[value]
      viewScope.activeTour.start(() => {
        viewScope.activeTour = null
      })
    })
  }
  setFileBrowser() {
    const readFile = (f, dropEvent = {}) => {
      const reader = new FileReader()
      reader.onload = e => this.blockly.load(e.target.result, dropEvent)
      reader.readAsText(f)
    }

    const handleFileSelect = (e) => {
      let files
      let dropEvent
      if (e.type === 'drop') {
        e.stopPropagation()
        e.preventDefault()
        files = e.dataTransfer.files
        dropEvent = e
      } else {
        files = e.target.files
      }
      files = [...files]
      for (const file of files) {
        if (file.type.match('text/xml')) {
          readFile(file, dropEvent)
        } else {
          observer.emit('ui.log.info', `${
          translator.translateText('File is not supported:')} ${file.name}`)
        }
      }
    }

    const handleDragOver = (e) => {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy'; // eslint-disable-line no-param-reassign
    }

    const dropZone = document.body

    dropZone.addEventListener('dragover', handleDragOver, false)
    dropZone.addEventListener('drop', handleFileSelect, false)
    if (document.getElementById('files')) {
      document.getElementById('files')
        .addEventListener('change', handleFileSelect, false)
    }
    $('#open_btn')
      .on('click', () => {
        $.FileDialog({ // eslint-disable-line new-cap
          accept: '.xml',
          cancelButton: 'Close',
          dragMessage: 'Drop files here',
          dropheight: 400,
          errorMessage: 'An error occured while loading file',
          multiple: false,
          okButton: 'OK',
          readAs: 'DataURL',
          removeMessage: 'Remove file',
          title: 'Load file',
        })
      })
      .on('files.bs.filedialog', (ev) => {
        handleFileSelect(ev.files)
      })
      .on('cancel.bs.filedialog', (ev) => {
        handleFileSelect(ev)
      })
  }
  setElementActions() {
    this.setFileBrowser()
    this.startTour()
    this.addBindings()
    this.addEventHandlers()
  }
  addBindings() {
    const logout = () => {
      logoutAllTokens(() => {
        this.updateTokenList()
        observer.emit('ui.log.info', translator.translateText('Logged you out!'))
        clearRealityCheck()
      })
    }

    $('#stopButton')
      .click(e => {
        if (e) {
          e.preventDefault()
        }
        stopRealityCheck()
        window.Bot.stop()
      })
      .hide()

    $('.panelExitButton')
      .click(function onClick() {
        $(this)
          .parent()
          .hide()
      })

    $('.panel')
      .hide()

    $('.panel')
      .drags()

    $('.panel .content')
      .mousedown(e => e.stopPropagation()) // prevent content to trigger draggable

    ReactDOM.render(
      <SaveXml
      onSave={(filename, collection) => this.blockly.save(filename, collection)}
      />
      , $('#saveXml')[0])

    $('#saveXml')
      .click(() => {
        $('#saveAs').show()
      })

    $('#undo')
      .click(() => {
        this.blockly.undo()
      })

    $('#redo')
      .click(() => {
        this.blockly.redo()
      })

    $('#zoomIn')
      .click(() => {
        this.blockly.zoomOnPlusMinus(true)
      })

    $('#zoomOut')
      .click(() => {
        this.blockly.zoomOnPlusMinus(false)
      })

    $('#cleanUp')
      .click(() => {
        this.blockly.cleanUp()
      })

    $('#showSummary')
      .click(() => {
        $('#summaryPanel')
          .show()
      })

    $('#loadXml')
      .click(() => {
        $('#files')
          .click()
      })

    $('#logout')
      .click(() => {
        logout()
        $('.logout').hide()
      })

    $('#continueTrading')
      .click(() => {
        const time = parseInt($('#realityDuration').val(), 10)
        if (time >= 10 && time <= 120) {
          $('#rc-err').hide()
          hideRealityCheck()
          startRealityCheck(time)
        } else {
          $('#rc-err').show()
        }
      })

    $('#runButton')
      .click(() => {
        $('#stopButton').show()
        $('#runButton').hide()
        this.blockly.run()
      })

    $('#resetButton')
      .click(() => {
        this.blockly.resetWorkspace()
      })

    $('.login-id-list')
      .on('click', 'a', (e) => {
        resetRealityCheck($(e.currentTarget).attr('value'))
        e.preventDefault()
        const $el = $(e.currentTarget)
        const $oldType = $el.find('li span')
        const $oldTypeText = $oldType.text()
        const $oldID = $el.find('li div')
        const $oldIDText = $oldID.text()
        const $oldValue = $el.attr('value')
        const $newType = $('.account-type')
        const $newTypeText = $newType.first().text()
        const $newID = $('.account-id')
        const $newIDText = $newID.first().text()
        const $newValue = $newID.attr('value')
        $oldType.html($newTypeText)
        $oldID.html($newIDText)
        $el.attr('value', $newValue)
        $newType.html($oldTypeText)
        $newID.html($oldIDText)
        $newID.attr('value', $oldValue)
      })

    $('#login')
      .bind('click.login', () => {
        document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' +
          `${getStorage('appId')}&l=${translator.getLanguage().toUpperCase()}`
      })
      .text('Log in')

    $(document).keydown((e) => {
      if (e.which === 189) { // -
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(false)
          e.preventDefault()
        }
      } else if (e.which === 187) { // +
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(true)
          e.preventDefault()
        }
      } else if (e.which === 39) { // right
        if (this.activeTour) {
          this.activeTour.next()
          e.preventDefault()
        }
      } else if (e.which === 27) { // Esc
        const exitButton = $('.panel:hover .panelExitButton')
        if (exitButton.length === 1) {
          exitButton.click()
          e.preventDefault()
        }
      }
    })
  }
  updateChart(info) {
    const chartToDataType = {
      area: 'ticks',
      line: 'ticks',
      candlestick: 'ohlc',
      ohlc: 'ohlc',
    }

    const isLine = () => ['area', 'line'].indexOf(this.chartType) >= 0

    const zoomInMax = (ev, chart) => {
      const {
        dataMax,
      } = chart.xAxis[0].getExtremes()
      const {
        minRange,
      } = chart.xAxis[0].options
      chart.xAxis[0].setExtremes(dataMax - minRange, dataMax)
    }

    const events = [
      {
        type: 'zoom-in-max',
        handler: zoomInMax,
      },
    ]

    if (isLine() && this.contractForChart) {
      const chartDiv = document.getElementById('trade-chart0')
      if (chartDiv) {
        chartDiv.dispatchEvent(new Event('zoom-in-max'))
      }
    }
    ReactDOM.render(
      <BinaryChart
      className="trade-chart"
      id="trade-chart0"
      contract={isLine() ? this.contractForChart : false}
      hideZoomControls={isLine() && this.contractForChart}
      pipSize={Number(Number(info.pip).toExponential().substring(3))}
      shiftMode={this.contractForChart ? 'dynamic' : 'fixed'}
      ticks={info[chartToDataType[this.chartType]]}
      type={this.chartType}
      events={events}
      hideIntervalPicker
      onTypeChange={(type) => (this.chartType = type)}
      />, $('#chart')[0])
  }
  addEventHandlers() {
    for (const errorType of ['api.error', 'BlocklyError', 'RuntimeError']) {
      observer.register(errorType, (error) => { // eslint-disable-line no-loop-func
        if (error.error && error.error.code === 'InvalidToken') {
          removeAllTokens()
          this.updateTokenList()
        }
        window.Bot.stop()
        if (window.Bot.shouldRestartOnError()) {
          ReactDOM.render(
            <RestartTimeout
            timeout="3"
            />
            , document.getElementById('restartTimeout'))
        }
      })
    }

    observer.register('bot.stop', () => {
      $('#runButton').show()
      $('#stopButton').hide()
    })

    observer.register('bot.tradeInfo', (tradeInfo) => {
      for (const key of Object.keys(tradeInfo)) {
        this.tradeInfo.tradeInfo[key] = tradeInfo[key]
      }
      this.tradeInfo.update()
    })

    observer.register('bot.tradeUpdate', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = {
        ...contract,
      }
      this.contractForChart.date_expiry = Number(this.contractForChart.date_expiry)
      this.contractForChart.date_settlement = Number(this.contractForChart.date_settlement)
      this.contractForChart.date_start = Number(this.contractForChart.date_start)
    })

    observer.register('bot.finish', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = false
    })

    observer.register('bot.tickUpdate', (info) => {
      this.updateChart(info)
    })
  }
}
<|endoftext|>


<commit_before>#ifndef __browser_DomElement__
#define __browser_DomElement__

#ifdef HIDDEN
/**
 * Some code in this file is based on Mootools.net and adapted to the
 * architecture of Bootstrap, with added changes in design and architecture
 * where deemeded necessary.
 * See http://www.bootstrap-js.net/wiki/MootoolsDifferences
 */
#endif

////////////////////////////////////////////////////////////////////////////////
// DomElements

/**
 * DomElements extends inject so that each of the functions
 * defined bellow are executed on the whole set of elements, and the
 * returned values are collected in an array and converted to a DomElements
 * array again, if it contains only html elements.
 */
DomElements = Array.extend(new function() {
	var unique = 0;

	return {
		initialize: function(els) {
			// Define this collections's unique ID. Elements that are added to it
			// get that field set too, in order to detect multiple additions of
			// elements in one go. Notice that this does not work when elements
			// are added to another collection, then again to this one.
			// But for Dom Query functions, this is enough.
			this._unique = unique++;
			// Do not use els.push to detect arrays, as we're passing pseudo
			// arrays here too (e.g. childNodes). But Option defines .length too,
			// so rul that out by checking nodeType as well.
			this.append(els && els.length != null && !els.nodeType ? els : arguments);
		},

		/**
		 * Only push wraps the added element in a DomElement. splice and unshift
		 * are not overridden to do the same. Also, it only supports one element.
		 */
		push: function(el) {
			// Try _wrapper first, for faster performance
			el = el._wrapper || DomElement.get(el);
			if (el._unique != this._unique) {
				el._unique = this._unique;
				this[this.length++] = el;
			}
			return this.length;
		},

		statics: {
			inject: function(src) {
				var collection = this;
				// For each function that is injected into DomElements, create a
				// new function that iterates that calls the function on each of
				// the collection's elements.
				// src can either be a function to be called, or a object literal.
				return this.base(EACH((src || {}), function(val, key) {
					this[key] = typeof val != 'function' ? val : function() {
						// Only collect values if calling a getter function,
						// otherwise return this
						var args = arguments, get = /^get/.test(key), values;
						this.each(function(obj) {
							// Try to use original method if it's there, in order
							// to support base, as this will be the wrapper that
							// sets it
							var ret = (obj[key] || val).apply(obj, args);
							if (get) {
								values = values || ($typeof(ret) == 'element'
									? new collection() : []);
								values.push(ret);
							}
						});
						return values || this;
					}
				}, {}));
			}
		}
	};
});

////////////////////////////////////////////////////////////////////////////////
// DomElement

DomElement = Base.extend(new function() {
	var elements = [], constructors = {}, uniqueId = 0;

	// Garbage collection - uncache elements/purge listeners on orphaned elements
	// so we don't hold a reference and cause the browser to retain them.
	function dispose(force) {
		for (var i = elements.length - 1; i >= 0; i--) {
			var el = elements[i];
	        if (force || (!el || el != window && el != document &&
				(!el.parentNode || !el.offsetParent))) {
	            if (el) {
					var obj = el._wrapper;
					if (obj && obj.dispose) obj.dispose();
					delete el._wrapper;
					delete el._children;
				}
				if (!force) elements.splice(i, 1);
	        }
		}
	}
	dispose.periodic(30000);

	// Private inject function for DomElement. It adds support for
	// _methods and _properties declarations, which forward calls and define
	// getter / setters for fields of the native DOM node.
	function inject(src) {
		// Forward method calls. Returns result if any, otherwise reference
		// to this.
		src = src || {};
		(src._methods || []).each(function(name) {
			src[name] = function(arg) {
				// .apply seems to not be present on native dom functions on
				// Safari. Just pass on the first argument and call directly.
				var ret = this.$[name] && this.$[name](arg);
				return ret === undefined ? this : ret;
			}
		});
		// Define getter / setters
		(src._properties || []).each(function(name) {
			var part = name.capitalize();
			src['get' + part] = function() {
				return this.$[name];
			}
			src['set' + part] = function(value) {
				this.$[name] = value;
			}
		});
		delete src._methods;
		delete src._properties;
		return Function.inject.call(this, src);
	}

	return {
		// Tells $typeof the type to return when encountering an element.
		_type: 'element',
		_elements: DomElements,

		initialize: function(el, props) {
			// Support element creating constructors on subclasses of DomElement
			// that define prototype._tag and can take one argument, which
			// defines the properties to be set:
			if (this._tag && $typeof(el) == 'object') {
				props = el;
				el = this._tag;
			}
			if (typeof(el) == 'string') {
				if (Browser.IE && props && (props.name || props.type))
					el = '<' + el + (props.name ? ' name="' + props.name + '"' : '')
						+ (props.type ? ' type="' + props.type + '"' : '') + '>';
				try {
					el = document.createElement(el);
				} catch (e) {
					var i = 0;
				}
			}
			// Generate special ids for document and window, so they can be found
			// too. Use # in the ids for these special objects as a hint for the
			// garbage collector to not touch these objects. '#' cannot be
			// present in normal Html elements ids.
			var id = el.id || el == document && '#doc' || el == window && '#win';
			// Does the DomElement wrapper for this element already exist?
			if (el._wrapper) return el._wrapper;
			// Store a reference to the native element.
			this.$ = el;
			this.id = el.id;
			// Store a reference in the native element to the wrapper.
			// Needs to be cleaned up by garbage collection!
			el._wrapper = this;
			elements[elements.length] = el;
			if (props) this.set(props);
		},

		statics: {
			inject: function(src) {
				var ret = inject.call(this, src);
				// Now, after src was processed in #inject, inject not only into
				// this, but also into DomElements where the functions are
				// "multiplied" for each of the elements of the collection.
				this.prototype._elements.inject(src);
				return ret;
			},

			extend: function(src) {
				// Do not pass src to base, as we weed to fix #inject first.
				var ret = this.base();
				// Undo overriding of the inject method above for subclasses,
				// as only injecting into DomElement (not subclasses) shall also
				// inject into DomElements!
				inject.call(ret, src);
				// Now reset inject. Reseting before does not work, as it would
				// be overridden during static inheritance again.
				ret.inject = inject;
				// When extending DomElement with a tag field specified, this
				// prototype will be used when wrapping elements of that type.
				// If this is a prototype for a certain tag, store it in the LUT.
				if (src && src._tag) constructors[src._tag] = ret;
				return ret;
			},

			get: function(el) {
				// Make sure we're using the right constructor. DomElement as
				// the default, HtmlElement for anything with className !== undefined
				// and special constructors based on tag names.
				return el ? el._wrapper || el._elements && el ||
					new (el.tagName && constructors[el.tagName.toLowerCase()] ||
						(el.className === undefined ? DomElement : HtmlElement))(el)
					: null;
			},

			collect: function(el) {
				elements[elements.length] = el;
			},

			unwrap: function(el) {
				return el && el.$ || el;
			},

			isAncestor: function(el, parent) {
				// TODO: See Ext for a faster implementation
				if (parent != document)
					for (el = el.parentNode; el != parent; el = el.parentNode)
						if (!el) return false;
				return true;
			},

			dispose: function() {
				dispose(true);
			}
		}
	}
});

// Use the modified inject function from above which injects both into DomElement
// and DomElements.
DomElement.inject(new function() {
	// Dom / Html to JS property mappings, as used by getProperty, setProperty
	// and removeProperty.
	var properties = {
		'class': 'className', 'for': 'htmlFor', colspan: 'colSpan',
		rowspan: 'rowSpan', accesskey: 'accessKey', tabindex: 'tabIndex',
		maxlength: 'maxLength', readonly: 'readOnly', value: 'value',
		disabled: 'disabled', checked: 'checked', multiple: 'multiple',
		selected: 'selected'
	};

	var flags = {
		href: 2, src: 2
	};

	var setterCache = {};

	function walk(el, name, start) {
		el = el[start ? start : name];
		while (el && $typeof(el) != 'element') el = el[name];
		return DomElement.get(el);
	}

	function create(where) {
		return function() {
			return this.create(arguments)['insert' + where](this);
		}
	}

	return {
		set: function(props) {
			return props ? EACH(props, function(val, key) {
				// First see if there is a setter for the given property
				var set = (key == 'events') ? this.addEvents : setterCache[key];
				// Do not call capitalize, as this is time critical and executes
				// faster (we only need to capitalize the first char here).
				if (set === undefined)
					set = setterCache[key] = this['set' +
						key.charAt(0).toUpperCase() + key.substring(1)] || null;
				// If the passed value is an array, use it as the argument
				// list for the call.
				if (set) set[val && val.push ? 'apply' : 'call'](this, val);
				else this.setProperty(key, val);
			}, this) : this;
		},

		getTag: function() {
			return this.$.tagName.toLowerCase();
		},

		getPrevious: function() {
			return walk(this.$, 'previousSibling');
		},

		getNext: function() {
			return walk(this.$, 'nextSibling');
		},

		getFirst: function() {
			return walk(this.$, 'nextSibling', 'firstChild');
		},

		getLast: function() {
			return walk(this.$, 'previousSibling', 'lastChild');
		},

		getParent: function() {
			return DomElement.get(this.$.parentNode);
		},

		getChildren: function() {
		 	return new this._elements(this.$.childNodes);
		},

		hasChildren: function() {
			return this.$.hasChildNodes();
		},

		hasParent: function(el) {
			return DomElement.isAncestor(this.$, DomElement.unwrap(el));
		},

		hasChild: function(el) {
			return DomElement.isAncestor(DomElement.unwrap(el), this.$);
		},

		appendChild: function(el) {
			this.$.appendChild(DomElement.get(el).$);
			return this;
		},

		appendText: function(text){
			this.$.appendChild(document.createTextNode(text));
			return this;
		},

		insertBefore: function(el) {
			el = DomElement.get(el);
			el.$.parentNode.insertBefore(this.$, el.$);
			return this;
		},

		insertAfter: function(el) {
			el = DomElement.get(el);
			var next = el.getNext();
			if (!next) el.$.parentNode.appendChild(this.$);
			else el.$.parentNode.insertBefore(this.$, next.$);
			return this;
		},

		insertFirst: function(el) {
			el = DomElement.get(el);
			el.$.insertBefore(this.$, el.$.firstChild);
			return this;
		},

		insertInside: function(el) {
			DomElement.get(el).appendChild(this);
			return this;
		},

		create: function(arg) {
			var values = typeof(arg) == 'string' ? arguments : arg;
			var elements = new this._elements();
			for (var i = 0; i < values.length; i += 3) {
				var el = DomElement.get(document.createElement(values[i])).set(values[i + 1]);
				var content = values[i + 2];
				if (content) {
					if (content.length) this.create(content).insertInside(el);
					else el.appendText(content);
				}
				elements.push(el);
			}
			return elements;
		},

		createBefore: create('Before'),

		createAfter: create('After'),

		createFirst: create('First'),

		createInside: create('Inside'),

		remove: function() {
			this.$.parentNode.removeChild(this.$);
			return this;
		},

		removeChild: function(el) {
			el = DomElement.get(el);
			this.$.removeChild(el.$);
			return el;
		},

		removeChildren: function() {
			this.getChildren().remove();
		},

		replaceWith: function(el) {
			el = DomElement.get(el);
			this.$.parentNode.replaceChild(el.$, this.$);
			return el;
		},

		clone: function(contents) {
			return DomElement.get(this.$.cloneNode(!!contents));
		},

		getProperty: function(name) {
			var key = properties[name];
			if (key) return this.$[key];
			var flag = flags[name];
			if (!Browser.IE || flag) return this.$.getAttribute(name, flag);
			var node = this.attributes[name];
			return node && node.nodeValue;
		},

		setProperty: function(name, value) {
			var key = properties[name];
			if (key) this.$[key] = value;
			else this.$.setAttribute(name, value);
			return this;
		},

		removeProperty: function(name) {
			var key = properties[name];
			if (key) this.$[key] = '';
			else this.$.removeAttribute(name);
			return this;
		},

		setProperties: function(src) {
			return EACH(src, function(val, name) {
				this.setProperty(name, val);
			}, this);
		}
	}
});

#endif // __browser_DomElement__
<commit_msg>Fixed an IE bug in getProperty.<commit_after>#ifndef __browser_DomElement__
#define __browser_DomElement__

#ifdef HIDDEN
/**
 * Some code in this file is based on Mootools.net and adapted to the
 * architecture of Bootstrap, with added changes in design and architecture
 * where deemeded necessary.
 * See http://www.bootstrap-js.net/wiki/MootoolsDifferences
 */
#endif

////////////////////////////////////////////////////////////////////////////////
// DomElements

/**
 * DomElements extends inject so that each of the functions
 * defined bellow are executed on the whole set of elements, and the
 * returned values are collected in an array and converted to a DomElements
 * array again, if it contains only html elements.
 */
DomElements = Array.extend(new function() {
	var unique = 0;

	return {
		initialize: function(els) {
			// Define this collections's unique ID. Elements that are added to it
			// get that field set too, in order to detect multiple additions of
			// elements in one go. Notice that this does not work when elements
			// are added to another collection, then again to this one.
			// But for Dom Query functions, this is enough.
			this._unique = unique++;
			// Do not use els.push to detect arrays, as we're passing pseudo
			// arrays here too (e.g. childNodes). But Option defines .length too,
			// so rul that out by checking nodeType as well.
			this.append(els && els.length != null && !els.nodeType ? els : arguments);
		},

		/**
		 * Only push wraps the added element in a DomElement. splice and unshift
		 * are not overridden to do the same. Also, it only supports one element.
		 */
		push: function(el) {
			// Try _wrapper first, for faster performance
			el = el._wrapper || DomElement.get(el);
			if (el._unique != this._unique) {
				el._unique = this._unique;
				this[this.length++] = el;
			}
			return this.length;
		},

		statics: {
			inject: function(src) {
				var collection = this;
				// For each function that is injected into DomElements, create a
				// new function that iterates that calls the function on each of
				// the collection's elements.
				// src can either be a function to be called, or a object literal.
				return this.base(EACH((src || {}), function(val, key) {
					this[key] = typeof val != 'function' ? val : function() {
						// Only collect values if calling a getter function,
						// otherwise return this
						var args = arguments, get = /^get/.test(key), values;
						this.each(function(obj) {
							// Try to use original method if it's there, in order
							// to support base, as this will be the wrapper that
							// sets it
							var ret = (obj[key] || val).apply(obj, args);
							if (get) {
								values = values || ($typeof(ret) == 'element'
									? new collection() : []);
								values.push(ret);
							}
						});
						return values || this;
					}
				}, {}));
			}
		}
	};
});

////////////////////////////////////////////////////////////////////////////////
// DomElement

DomElement = Base.extend(new function() {
	var elements = [], constructors = {}, uniqueId = 0;

	// Garbage collection - uncache elements/purge listeners on orphaned elements
	// so we don't hold a reference and cause the browser to retain them.
	function dispose(force) {
		for (var i = elements.length - 1; i >= 0; i--) {
			var el = elements[i];
	        if (force || (!el || el != window && el != document &&
				(!el.parentNode || !el.offsetParent))) {
	            if (el) {
					var obj = el._wrapper;
					if (obj && obj.dispose) obj.dispose();
					delete el._wrapper;
					delete el._children;
				}
				if (!force) elements.splice(i, 1);
	        }
		}
	}
	dispose.periodic(30000);

	// Private inject function for DomElement. It adds support for
	// _methods and _properties declarations, which forward calls and define
	// getter / setters for fields of the native DOM node.
	function inject(src) {
		// Forward method calls. Returns result if any, otherwise reference
		// to this.
		src = src || {};
		(src._methods || []).each(function(name) {
			src[name] = function(arg) {
				// .apply seems to not be present on native dom functions on
				// Safari. Just pass on the first argument and call directly.
				var ret = this.$[name] && this.$[name](arg);
				return ret === undefined ? this : ret;
			}
		});
		// Define getter / setters
		(src._properties || []).each(function(name) {
			var part = name.capitalize();
			src['get' + part] = function() {
				return this.$[name];
			}
			src['set' + part] = function(value) {
				this.$[name] = value;
			}
		});
		delete src._methods;
		delete src._properties;
		return Function.inject.call(this, src);
	}

	return {
		// Tells $typeof the type to return when encountering an element.
		_type: 'element',
		_elements: DomElements,

		initialize: function(el, props) {
			// Support element creating constructors on subclasses of DomElement
			// that define prototype._tag and can take one argument, which
			// defines the properties to be set:
			if (this._tag && $typeof(el) == 'object') {
				props = el;
				el = this._tag;
			}
			if (typeof(el) == 'string') {
				if (Browser.IE && props && (props.name || props.type))
					el = '<' + el + (props.name ? ' name="' + props.name + '"' : '')
						+ (props.type ? ' type="' + props.type + '"' : '') + '>';
				try {
					el = document.createElement(el);
				} catch (e) {
					var i = 0;
				}
			}
			// Generate special ids for document and window, so they can be found
			// too. Use # in the ids for these special objects as a hint for the
			// garbage collector to not touch these objects. '#' cannot be
			// present in normal Html elements ids.
			var id = el.id || el == document && '#doc' || el == window && '#win';
			// Does the DomElement wrapper for this element already exist?
			if (el._wrapper) return el._wrapper;
			// Store a reference to the native element.
			this.$ = el;
			this.id = el.id;
			// Store a reference in the native element to the wrapper.
			// Needs to be cleaned up by garbage collection!
			el._wrapper = this;
			elements[elements.length] = el;
			if (props) this.set(props);
		},

		statics: {
			inject: function(src) {
				var ret = inject.call(this, src);
				// Now, after src was processed in #inject, inject not only into
				// this, but also into DomElements where the functions are
				// "multiplied" for each of the elements of the collection.
				this.prototype._elements.inject(src);
				return ret;
			},

			extend: function(src) {
				// Do not pass src to base, as we weed to fix #inject first.
				var ret = this.base();
				// Undo overriding of the inject method above for subclasses,
				// as only injecting into DomElement (not subclasses) shall also
				// inject into DomElements!
				inject.call(ret, src);
				// Now reset inject. Reseting before does not work, as it would
				// be overridden during static inheritance again.
				ret.inject = inject;
				// When extending DomElement with a tag field specified, this
				// prototype will be used when wrapping elements of that type.
				// If this is a prototype for a certain tag, store it in the LUT.
				if (src && src._tag) constructors[src._tag] = ret;
				return ret;
			},

			get: function(el) {
				// Make sure we're using the right constructor. DomElement as
				// the default, HtmlElement for anything with className !== undefined
				// and special constructors based on tag names.
				return el ? el._wrapper || el._elements && el ||
					new (el.tagName && constructors[el.tagName.toLowerCase()] ||
						(el.className === undefined ? DomElement : HtmlElement))(el)
					: null;
			},

			collect: function(el) {
				elements[elements.length] = el;
			},

			unwrap: function(el) {
				return el && el.$ || el;
			},

			isAncestor: function(el, parent) {
				// TODO: See Ext for a faster implementation
				if (parent != document)
					for (el = el.parentNode; el != parent; el = el.parentNode)
						if (!el) return false;
				return true;
			},

			dispose: function() {
				dispose(true);
			}
		}
	}
});

// Use the modified inject function from above which injects both into DomElement
// and DomElements.
DomElement.inject(new function() {
	// Dom / Html to JS property mappings, as used by getProperty, setProperty
	// and removeProperty.
	var properties = {
		'class': 'className', 'for': 'htmlFor', colspan: 'colSpan',
		rowspan: 'rowSpan', accesskey: 'accessKey', tabindex: 'tabIndex',
		maxlength: 'maxLength', readonly: 'readOnly', value: 'value',
		disabled: 'disabled', checked: 'checked', multiple: 'multiple',
		selected: 'selected'
	};

	var flags = {
		href: 2, src: 2
	};

	var setterCache = {};

	function walk(el, name, start) {
		el = el[start ? start : name];
		while (el && $typeof(el) != 'element') el = el[name];
		return DomElement.get(el);
	}

	function create(where) {
		return function() {
			return this.create(arguments)['insert' + where](this);
		}
	}

	return {
		set: function(props) {
			return props ? EACH(props, function(val, key) {
				// First see if there is a setter for the given property
				var set = (key == 'events') ? this.addEvents : setterCache[key];
				// Do not call capitalize, as this is time critical and executes
				// faster (we only need to capitalize the first char here).
				if (set === undefined)
					set = setterCache[key] = this['set' +
						key.charAt(0).toUpperCase() + key.substring(1)] || null;
				// If the passed value is an array, use it as the argument
				// list for the call.
				if (set) set[val && val.push ? 'apply' : 'call'](this, val);
				else this.setProperty(key, val);
			}, this) : this;
		},

		getTag: function() {
			return this.$.tagName.toLowerCase();
		},

		getPrevious: function() {
			return walk(this.$, 'previousSibling');
		},

		getNext: function() {
			return walk(this.$, 'nextSibling');
		},

		getFirst: function() {
			return walk(this.$, 'nextSibling', 'firstChild');
		},

		getLast: function() {
			return walk(this.$, 'previousSibling', 'lastChild');
		},

		getParent: function() {
			return DomElement.get(this.$.parentNode);
		},

		getChildren: function() {
		 	return new this._elements(this.$.childNodes);
		},

		hasChildren: function() {
			return this.$.hasChildNodes();
		},

		hasParent: function(el) {
			return DomElement.isAncestor(this.$, DomElement.unwrap(el));
		},

		hasChild: function(el) {
			return DomElement.isAncestor(DomElement.unwrap(el), this.$);
		},

		appendChild: function(el) {
			this.$.appendChild(DomElement.get(el).$);
			return this;
		},

		appendText: function(text){
			this.$.appendChild(document.createTextNode(text));
			return this;
		},

		insertBefore: function(el) {
			el = DomElement.get(el);
			el.$.parentNode.insertBefore(this.$, el.$);
			return this;
		},

		insertAfter: function(el) {
			el = DomElement.get(el);
			var next = el.getNext();
			if (!next) el.$.parentNode.appendChild(this.$);
			else el.$.parentNode.insertBefore(this.$, next.$);
			return this;
		},

		insertFirst: function(el) {
			el = DomElement.get(el);
			el.$.insertBefore(this.$, el.$.firstChild);
			return this;
		},

		insertInside: function(el) {
			DomElement.get(el).appendChild(this);
			return this;
		},

		create: function(arg) {
			var values = typeof(arg) == 'string' ? arguments : arg;
			var elements = new this._elements();
			for (var i = 0; i < values.length; i += 3) {
				var el = DomElement.get(document.createElement(values[i])).set(values[i + 1]);
				var content = values[i + 2];
				if (content) {
					if (content.length) this.create(content).insertInside(el);
					else el.appendText(content);
				}
				elements.push(el);
			}
			return elements;
		},

		createBefore: create('Before'),

		createAfter: create('After'),

		createFirst: create('First'),

		createInside: create('Inside'),

		remove: function() {
			this.$.parentNode.removeChild(this.$);
			return this;
		},

		removeChild: function(el) {
			el = DomElement.get(el);
			this.$.removeChild(el.$);
			return el;
		},

		removeChildren: function() {
			this.getChildren().remove();
		},

		replaceWith: function(el) {
			el = DomElement.get(el);
			this.$.parentNode.replaceChild(el.$, this.$);
			return el;
		},

		clone: function(contents) {
			return DomElement.get(this.$.cloneNode(!!contents));
		},

		getProperty: function(name) {
			var key = properties[name];
			if (key) return this.$[key];
			var flag = flags[name];
			if (!Browser.IE || flag) return this.$.getAttribute(name, flag);
			var node = this.$.attributes[name];
			return node && node.nodeValue;
		},

		setProperty: function(name, value) {
			var key = properties[name];
			if (key) this.$[key] = value;
			else this.$.setAttribute(name, value);
			return this;
		},

		removeProperty: function(name) {
			var key = properties[name];
			if (key) this.$[key] = '';
			else this.$.removeAttribute(name);
			return this;
		},

		setProperties: function(src) {
			return EACH(src, function(val, name) {
				this.setProperty(name, val);
			}, this);
		}
	}
});

#endif // __browser_DomElement__
<|endoftext|>


<commit_before>import find from 'lodash/find'
import forEach from 'lodash/forEach'
import fromEvent from 'promise-toolbox/fromEvent'
import xml2js from 'xml2js'
import { ensureArray } from 'utils'
import { readVmdkGrainTable } from 'xo-vmdk-to-vhd'

// ===================================================================

// See: http://opennodecloud.com/howto/2013/12/25/howto-ON-ovf-reference.html
// See: http://www.dmtf.org/sites/default/files/standards/documents/DSP0243_1.0.0.pdf
// See: http://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf

// ===================================================================

const MEMORY_UNIT_TO_FACTOR = {
  k: 1024,
  m: 1048576,
  g: 1073741824,
  t: 1099511627776,
}

const RESOURCE_TYPE_TO_HANDLER = {
  // CPU.
  '3': (data, { 'rasd:VirtualQuantity': nCpus }) => {
    data.nCpus = +nCpus
  },
  // RAM.
  '4': (
    data,
    { 'rasd:AllocationUnits': unit, 'rasd:VirtualQuantity': quantity }
  ) => {
    data.memory = quantity * allocationUnitsToFactor(unit)
  },
  // Network.
  '10': (
    { networks },
    { 'rasd:AutomaticAllocation': enabled, 'rasd:Connection': name }
  ) => {
    if (enabled) {
      networks.push(name)
    }
  },
  // Disk.
  '17': (
    { disks },
    {
      'rasd:AddressOnParent': position,
      'rasd:Description': description = 'No description',
      'rasd:ElementName': name,
      'rasd:HostResource': resource,
    }
  ) => {
    const diskId = resource.match(/^(?:ovf:)?\/disk\/(.+)$/)
    const disk = diskId && disks[diskId[1]]

    if (disk) {
      disk.descriptionLabel = description
      disk.nameLabel = name
      disk.position = +position
    } else {
      // TODO: Log error in U.I.
      console.error(`No disk found: '${diskId}'.`)
    }
  },
}

const allocationUnitsToFactor = unit => {
  const intValue = unit.match(/\^([0-9]+)$/)
  return intValue != null
    ? Math.pow(2, intValue[1])
    : MEMORY_UNIT_TO_FACTOR[unit.charAt(0).toLowerCase()]
}

const filterDisks = disks => {
  for (const diskId in disks) {
    if (disks[diskId].position == null) {
      // TODO: Log error in U.I.
      console.error(`No position specified for '${diskId}'.`)
      delete disks[diskId]
    }
  }
}

// ===================================================================
/* global FileReader, TextDecoder */

async function readFileFragment (file, start = 0, end) {
  const reader = new FileReader()
  reader.readAsArrayBuffer(file.slice(start, end))
  return (await fromEvent(reader, 'loadend')).target.result
}

function parseTarHeader (header) {
  const textDecoder = new TextDecoder('ascii')
  const fileName = textDecoder.decode(header.slice(0, 100)).split('\0')[0]
  if (fileName.length === 0) {
    return null
  }
  const fileSize = parseInt(textDecoder.decode(header.slice(124, 124 + 11)), 8)
  return { fileName, fileSize }
}

async function parseOVF (fileFragment) {
  const textDecoder = new TextDecoder('utf-8')
  const xmlString = textDecoder.decode(await readFileFragment(fileFragment))
  return new Promise((resolve, reject) =>
    xml2js.parseString(
      xmlString,
      { mergeAttrs: true, explicitArray: false },
      (err, res) => {
        if (err) {
          reject(err)
          return
        }

        const {
          Envelope: {
            DiskSection: { Disk: disks },
            References: { File: files },
            VirtualSystem: system,
          },
        } = res

        const data = {
          disks: {},
          networks: [],
        }
        const hardware = system.VirtualHardwareSection

        // Get VM name/description.
        data.nameLabel = hardware.System['vssd:VirtualSystemIdentifier']
        data.descriptionLabel =
          (system.AnnotationSection && system.AnnotationSection.Annotation) ||
          (system.OperatingSystemSection &&
            system.OperatingSystemSection.Description)

        // Get disks.
        forEach(ensureArray(disks), disk => {
          const file = find(
            ensureArray(files),
            file => file['ovf:id'] === disk['ovf:fileRef']
          )
          const unit = disk['ovf:capacityAllocationUnits']

          data.disks[disk['ovf:diskId']] = {
            capacity:
              disk['ovf:capacity'] *
              ((unit && allocationUnitsToFactor(unit)) || 1),
            path: file && file['ovf:href'],
          }
        })

        // Get hardware info: CPU, RAM, disks, networks...
        forEach(ensureArray(hardware.Item), item => {
          const handler = RESOURCE_TYPE_TO_HANDLER[item['rasd:ResourceType']]
          if (!handler) {
            return
          }
          handler(data, item)
        })

        // Remove disks which not have a position.
        // (i.e. no info in hardware.Item section.)
        filterDisks(data.disks)
        resolve(data)
      }
    )
  )
}

async function parseTarFile (file) {
  let offset = 0
  const HEADER_SIZE = 512
  let data = { tables: {} }
  while (offset + HEADER_SIZE <= file.size) {
    const header = parseTarHeader(
      await readFileFragment(file, offset, offset + HEADER_SIZE)
    )
    offset += HEADER_SIZE
    if (header === null) {
      break
    }
    if (header.fileName.toLowerCase().endsWith('.ovf')) {
      const res = await parseOVF(file.slice(offset, offset + header.fileSize))
      data = { ...data, ...res }
    }
    if (header.fileName.toLowerCase().endsWith('.vmdk')) {
      const fileSlice = file.slice(offset, offset + header.fileSize)
      const readFile = async (start, end) =>
        readFileFragment(fileSlice, start, end)
      data.tables[header.fileName] = await readVmdkGrainTable(readFile)
    }
    offset += Math.ceil(header.fileSize / 512) * 512
  }
  return data
}

const parseOvaFile = async file => parseTarFile(file)

export { parseOvaFile as default }
<commit_msg>fix(xo-web): avoid using TextDecoder (#3718)<commit_after>import find from 'lodash/find'
import forEach from 'lodash/forEach'
import fromEvent from 'promise-toolbox/fromEvent'
import xml2js from 'xml2js'
import { ensureArray } from 'utils'
import { readVmdkGrainTable } from 'xo-vmdk-to-vhd'

// ===================================================================

// See: http://opennodecloud.com/howto/2013/12/25/howto-ON-ovf-reference.html
// See: http://www.dmtf.org/sites/default/files/standards/documents/DSP0243_1.0.0.pdf
// See: http://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf

// ===================================================================

const MEMORY_UNIT_TO_FACTOR = {
  k: 1024,
  m: 1048576,
  g: 1073741824,
  t: 1099511627776,
}

const RESOURCE_TYPE_TO_HANDLER = {
  // CPU.
  '3': (data, { 'rasd:VirtualQuantity': nCpus }) => {
    data.nCpus = +nCpus
  },
  // RAM.
  '4': (
    data,
    { 'rasd:AllocationUnits': unit, 'rasd:VirtualQuantity': quantity }
  ) => {
    data.memory = quantity * allocationUnitsToFactor(unit)
  },
  // Network.
  '10': (
    { networks },
    { 'rasd:AutomaticAllocation': enabled, 'rasd:Connection': name }
  ) => {
    if (enabled) {
      networks.push(name)
    }
  },
  // Disk.
  '17': (
    { disks },
    {
      'rasd:AddressOnParent': position,
      'rasd:Description': description = 'No description',
      'rasd:ElementName': name,
      'rasd:HostResource': resource,
    }
  ) => {
    const diskId = resource.match(/^(?:ovf:)?\/disk\/(.+)$/)
    const disk = diskId && disks[diskId[1]]

    if (disk) {
      disk.descriptionLabel = description
      disk.nameLabel = name
      disk.position = +position
    } else {
      // TODO: Log error in U.I.
      console.error(`No disk found: '${diskId}'.`)
    }
  },
}

const allocationUnitsToFactor = unit => {
  const intValue = unit.match(/\^([0-9]+)$/)
  return intValue != null
    ? Math.pow(2, intValue[1])
    : MEMORY_UNIT_TO_FACTOR[unit.charAt(0).toLowerCase()]
}

const filterDisks = disks => {
  for (const diskId in disks) {
    if (disks[diskId].position == null) {
      // TODO: Log error in U.I.
      console.error(`No position specified for '${diskId}'.`)
      delete disks[diskId]
    }
  }
}

// ===================================================================
/* global FileReader */

async function readFileFragment (file, start = 0, end) {
  const reader = new FileReader()
  reader.readAsArrayBuffer(file.slice(start, end))
  return (await fromEvent(reader, 'loadend')).target.result
}

function parseTarHeader (header) {
  const fileName = Buffer.from(header.slice(0, 100))
    .toString('ascii')
    .split('\0')[0]
  if (fileName.length === 0) {
    return null
  }
  const fileSize = parseInt(
    Buffer.from(header.slice(124, 124 + 11)).toString('ascii'),
    8
  )
  return { fileName, fileSize }
}

async function parseOVF (fileFragment) {
  const xmlString = Buffer.from(await readFileFragment(fileFragment)).toString()
  return new Promise((resolve, reject) =>
    xml2js.parseString(
      xmlString,
      { mergeAttrs: true, explicitArray: false },
      (err, res) => {
        if (err) {
          reject(err)
          return
        }

        const {
          Envelope: {
            DiskSection: { Disk: disks },
            References: { File: files },
            VirtualSystem: system,
          },
        } = res

        const data = {
          disks: {},
          networks: [],
        }
        const hardware = system.VirtualHardwareSection

        // Get VM name/description.
        data.nameLabel = hardware.System['vssd:VirtualSystemIdentifier']
        data.descriptionLabel =
          (system.AnnotationSection && system.AnnotationSection.Annotation) ||
          (system.OperatingSystemSection &&
            system.OperatingSystemSection.Description)

        // Get disks.
        forEach(ensureArray(disks), disk => {
          const file = find(
            ensureArray(files),
            file => file['ovf:id'] === disk['ovf:fileRef']
          )
          const unit = disk['ovf:capacityAllocationUnits']

          data.disks[disk['ovf:diskId']] = {
            capacity:
              disk['ovf:capacity'] *
              ((unit && allocationUnitsToFactor(unit)) || 1),
            path: file && file['ovf:href'],
          }
        })

        // Get hardware info: CPU, RAM, disks, networks...
        forEach(ensureArray(hardware.Item), item => {
          const handler = RESOURCE_TYPE_TO_HANDLER[item['rasd:ResourceType']]
          if (!handler) {
            return
          }
          handler(data, item)
        })

        // Remove disks which not have a position.
        // (i.e. no info in hardware.Item section.)
        filterDisks(data.disks)
        resolve(data)
      }
    )
  )
}

async function parseTarFile (file) {
  let offset = 0
  const HEADER_SIZE = 512
  let data = { tables: {} }
  while (offset + HEADER_SIZE <= file.size) {
    const header = parseTarHeader(
      await readFileFragment(file, offset, offset + HEADER_SIZE)
    )
    offset += HEADER_SIZE
    if (header === null) {
      break
    }
    if (header.fileName.toLowerCase().endsWith('.ovf')) {
      const res = await parseOVF(file.slice(offset, offset + header.fileSize))
      data = { ...data, ...res }
    }
    if (header.fileName.toLowerCase().endsWith('.vmdk')) {
      const fileSlice = file.slice(offset, offset + header.fileSize)
      const readFile = async (start, end) =>
        readFileFragment(fileSlice, start, end)
      data.tables[header.fileName] = await readVmdkGrainTable(readFile)
    }
    offset += Math.ceil(header.fileSize / 512) * 512
  }
  return data
}

const parseOvaFile = async file => parseTarFile(file)

export { parseOvaFile as default }
<|endoftext|>


<commit_before>describe('ima.page.renderer.ServerPageRenderer', function() {

	var param1 = 'param1';
	var param2 = 'param2';
	var params = {
		param1: param1,
		param2: Promise.resolve(param2)
	};

	var controller = new ns.ima.controller.Controller();
	controller.getMetaManager = function() {};
	var view = function() {};
	var expressResponse = {
		status: function() {},
		send: function() {}
	};

	var pageRenderer = null;
	var $Helper = oc.get('$Helper');
	var rendererFactory = oc.get('$PageRendererFactory');
	var ReactDOMServer = {
		renderToString: function() {},
		renderToStaticMarkup: function() {}
	};
	var settings = oc.get('$Settings');
	var response = oc.get('$Response');
	var cache = oc.get('$Cache');
	var routeOptions = {
		onlyUpdate: false,
		autoScroll: false,
		allowServeSPA: false,
		documentView: null
	};

	beforeEach(function() {
		response.init(expressResponse);
		pageRenderer = oc.create('ima.page.renderer.ServerPageRenderer', [rendererFactory, $Helper, ReactDOMServer, settings, response, cache, oc]);
	});

	it('should be wrap each key to promise', function() {
		spyOn(Promise, 'resolve')
			.and
			.callThrough();

		pageRenderer._wrapEachKeyToPromise(params);

		expect(Promise.resolve).toHaveBeenCalledWith(param1);
		expect(Promise.resolve.calls.count()).toEqual(1);
	});

	describe('update method', function() {

		it('should reject promise with error', function(done) {
			spyOn(pageRenderer, 'mount')
				.and
				.stub();

			pageRenderer
				.update(controller, params)
				.catch(function(error) {
					expect(error instanceof ns.ima.error.GenericError).toEqual(true);
					done();
				});
		});

	});

	describe('mount method', function() {

		var loadedPageState = {
			param1: 'param1',
			param2: Promise.resolve('param2')
		};

		it('should return already sent data to the client', function(done) {
			var responseParams = {
				content: '',
				status: 200
			};

			spyOn(response, 'isResponseSent')
				.and
				.returnValue(true);
			spyOn(response, 'getResponseParams')
				.and
				.returnValue(responseParams);

			pageRenderer
				.mount(controller, view, loadedPageState, routeOptions)
				.then(function(page) {
					expect(page).toEqual(responseParams);
					done();
				});
		});

		it('should call _renderPage method', function(done) {
			spyOn(pageRenderer, '_renderPage')
				.and
				.stub();

			pageRenderer
				.mount(controller, view, loadedPageState, routeOptions)
				.then(function(page) {
					expect(pageRenderer._renderPage).toHaveBeenCalled();
					done();
				});
		});

	});

	describe('_renderPage method', function() {
		var fetchedResource = {
			resource: 'json'
		};

		it('should return already sent data to client', function() {
			var responseParams = {
				content: '',
				status: 200
			};

			spyOn(response, 'isResponseSent')
				.and
				.returnValue(true);
			spyOn(response, 'getResponseParams')
				.and
				.returnValue(responseParams);

			expect(pageRenderer._renderPage(controller, view, fetchedResource)).toEqual(responseParams);
		});

		describe('render new page', function() {

			var responseParams = { status: 200, content: '' };
			var pageRenderResponse = null;

			beforeEach(function() {
				spyOn(controller, 'setState')
					.and
					.stub();
				spyOn(controller, 'setMetaParams')
					.and
					.stub();
				spyOn(controller, 'getHttpStatus')
					.and
					.stub();
				spyOn(pageRenderer, '_renderPageContentToString')
					.and
					.stub();
				spyOn(response, 'status')
					.and
					.returnValue(response);
				spyOn(response, 'send')
					.and
					.returnValue(response);
				spyOn(response, 'getResponseParams')
					.and
					.returnValue(responseParams);

				pageRenderResponse = pageRenderer._renderPage(controller, view, fetchedResource, routeOptions);
			});

			it('should set controller state', function() {
				expect(controller.setState).toHaveBeenCalledWith(fetchedResource);
			});

			it('should set meta params', function() {
				expect(controller.setMetaParams).toHaveBeenCalledWith(fetchedResource);
			});

			it('should send response for request', function() {
				expect(response.status).toHaveBeenCalled();
				expect(response.send).toHaveBeenCalled();
				expect(controller.getHttpStatus).toHaveBeenCalled();
				expect(pageRenderer._renderPageContentToString).toHaveBeenCalledWith(controller, view, routeOptions);
			});

			it('should return response params', function() {
				expect(pageRenderResponse).toEqual(responseParams);
			});
		});
	});

	describe('_renderPageContentToString method', function() {

		var utils = { $Utils: 'utils' };
		var state = { state: 'state' };
		var propsView = { view: view };
		var props = Object.assign({}, state, utils, propsView);
		var wrapedPageViewElement = { wrapElementView: 'wrapedPageViewElement' };
		var pageMarkup = '';
		var documentView = function() {};
		var documentViewElement = function() {};
		var documentViewFactory = function() {
			return documentViewElement;
		};
		var appMarkup = '' + pageMarkup + '';
		var revivalSettings = { revivalSettings: 'revivalSettings' };
		var metaManager = { metaManager: 'metaManager' };
		var pageContent = null;

		beforeEach(function() {
			spyOn(pageRenderer, '_generateViewProps')
				.and
				.returnValue(props);
			spyOn(controller, 'getState')
				.and
				.returnValue(state);
			spyOn(rendererFactory, 'wrapView')
				.and
				.returnValue(wrapedPageViewElement);
			spyOn(ReactDOMServer, 'renderToString')
				.and
				.returnValue(pageMarkup);
			spyOn(rendererFactory, 'reactCreateFactory')
				.and
				.returnValue(documentViewFactory);
			spyOn(rendererFactory, 'getDocumentView')
				.and
				.returnValue(documentView);
			spyOn(ReactDOMServer, 'renderToStaticMarkup')
				.and
				.returnValue(appMarkup);
			spyOn(pageRenderer, '_getRevivalSettings')
				.and
				.returnValue(revivalSettings);
			spyOn(controller, 'getMetaManager')
				.and
				.returnValue(metaManager);
			spyOn(rendererFactory, 'getUtils')
				.and
				.returnValue(utils);

			pageContent = pageRenderer._renderPageContentToString(controller, view, routeOptions);
		});

		it('should generate view props from controller state', function() {
			expect(pageRenderer._generateViewProps).toHaveBeenCalledWith(view, state);
		});

		it('should wrap page view', function() {
			expect(rendererFactory.wrapView).toHaveBeenCalledWith(props);
		});

		it('should render page view to string', function() {
			expect(ReactDOMServer.renderToString).toHaveBeenCalledWith(wrapedPageViewElement);
		});

		it('should create factory for creating React element from document view', function() {
			expect(rendererFactory.reactCreateFactory).toHaveBeenCalledWith(documentView);
		});

		it('should create factory for creating React element from document view', function() {
			var routeOptionsWithDocument = Object.assign({}, routeOptions, { documentView: ns.ima.page.AbstractDocumentView });
			pageContent = pageRenderer._renderPageContentToString(controller, view, routeOptionsWithDocument);

			expect(rendererFactory.getDocumentView).toHaveBeenCalledWith(ns.ima.page.AbstractDocumentView);
		});

		it('should render static markup from document view', function() {
			expect(rendererFactory.getUtils).toHaveBeenCalled();
			expect(controller.getMetaManager).toHaveBeenCalled();
			expect(pageRenderer._getRevivalSettings).toHaveBeenCalled();
			expect(ReactDOMServer.renderToStaticMarkup).toHaveBeenCalledWith(documentViewElement);
		});

		it('should return page content', function() {
			expect(pageContent).toEqual('<!doctype html>\n' + appMarkup);
		});
	});

});
<commit_msg>#! update test description<commit_after>describe('ima.page.renderer.ServerPageRenderer', function() {

	var param1 = 'param1';
	var param2 = 'param2';
	var params = {
		param1: param1,
		param2: Promise.resolve(param2)
	};

	var controller = new ns.ima.controller.Controller();
	controller.getMetaManager = function() {};
	var view = function() {};
	var expressResponse = {
		status: function() {},
		send: function() {}
	};

	var pageRenderer = null;
	var $Helper = oc.get('$Helper');
	var rendererFactory = oc.get('$PageRendererFactory');
	var ReactDOMServer = {
		renderToString: function() {},
		renderToStaticMarkup: function() {}
	};
	var settings = oc.get('$Settings');
	var response = oc.get('$Response');
	var cache = oc.get('$Cache');
	var routeOptions = {
		onlyUpdate: false,
		autoScroll: false,
		allowServeSPA: false,
		documentView: null
	};

	beforeEach(function() {
		response.init(expressResponse);
		pageRenderer = oc.create('ima.page.renderer.ServerPageRenderer', [rendererFactory, $Helper, ReactDOMServer, settings, response, cache, oc]);
	});

	it('should be wrap each key to promise', function() {
		spyOn(Promise, 'resolve')
			.and
			.callThrough();

		pageRenderer._wrapEachKeyToPromise(params);

		expect(Promise.resolve).toHaveBeenCalledWith(param1);
		expect(Promise.resolve.calls.count()).toEqual(1);
	});

	describe('update method', function() {

		it('should reject promise with error', function(done) {
			spyOn(pageRenderer, 'mount')
				.and
				.stub();

			pageRenderer
				.update(controller, params)
				.catch(function(error) {
					expect(error instanceof ns.ima.error.GenericError).toEqual(true);
					done();
				});
		});

	});

	describe('mount method', function() {

		var loadedPageState = {
			param1: 'param1',
			param2: Promise.resolve('param2')
		};

		it('should return already sent data to the client', function(done) {
			var responseParams = {
				content: '',
				status: 200
			};

			spyOn(response, 'isResponseSent')
				.and
				.returnValue(true);
			spyOn(response, 'getResponseParams')
				.and
				.returnValue(responseParams);

			pageRenderer
				.mount(controller, view, loadedPageState, routeOptions)
				.then(function(page) {
					expect(page).toEqual(responseParams);
					done();
				});
		});

		it('should call _renderPage method', function(done) {
			spyOn(pageRenderer, '_renderPage')
				.and
				.stub();

			pageRenderer
				.mount(controller, view, loadedPageState, routeOptions)
				.then(function(page) {
					expect(pageRenderer._renderPage).toHaveBeenCalled();
					done();
				});
		});

	});

	describe('_renderPage method', function() {
		var fetchedResource = {
			resource: 'json'
		};

		it('should return already sent data to client', function() {
			var responseParams = {
				content: '',
				status: 200
			};

			spyOn(response, 'isResponseSent')
				.and
				.returnValue(true);
			spyOn(response, 'getResponseParams')
				.and
				.returnValue(responseParams);

			expect(pageRenderer._renderPage(controller, view, fetchedResource)).toEqual(responseParams);
		});

		describe('render new page', function() {

			var responseParams = { status: 200, content: '' };
			var pageRenderResponse = null;

			beforeEach(function() {
				spyOn(controller, 'setState')
					.and
					.stub();
				spyOn(controller, 'setMetaParams')
					.and
					.stub();
				spyOn(controller, 'getHttpStatus')
					.and
					.stub();
				spyOn(pageRenderer, '_renderPageContentToString')
					.and
					.stub();
				spyOn(response, 'status')
					.and
					.returnValue(response);
				spyOn(response, 'send')
					.and
					.returnValue(response);
				spyOn(response, 'getResponseParams')
					.and
					.returnValue(responseParams);

				pageRenderResponse = pageRenderer._renderPage(controller, view, fetchedResource, routeOptions);
			});

			it('should set controller state', function() {
				expect(controller.setState).toHaveBeenCalledWith(fetchedResource);
			});

			it('should set meta params', function() {
				expect(controller.setMetaParams).toHaveBeenCalledWith(fetchedResource);
			});

			it('should send response for request', function() {
				expect(response.status).toHaveBeenCalled();
				expect(response.send).toHaveBeenCalled();
				expect(controller.getHttpStatus).toHaveBeenCalled();
				expect(pageRenderer._renderPageContentToString).toHaveBeenCalledWith(controller, view, routeOptions);
			});

			it('should return response params', function() {
				expect(pageRenderResponse).toEqual(responseParams);
			});
		});
	});

	describe('_renderPageContentToString method', function() {

		var utils = { $Utils: 'utils' };
		var state = { state: 'state' };
		var propsView = { view: view };
		var props = Object.assign({}, state, utils, propsView);
		var wrapedPageViewElement = { wrapElementView: 'wrapedPageViewElement' };
		var pageMarkup = '';
		var documentView = function() {};
		var documentViewElement = function() {};
		var documentViewFactory = function() {
			return documentViewElement;
		};
		var appMarkup = '' + pageMarkup + '';
		var revivalSettings = { revivalSettings: 'revivalSettings' };
		var metaManager = { metaManager: 'metaManager' };
		var pageContent = null;

		beforeEach(function() {
			spyOn(pageRenderer, '_generateViewProps')
				.and
				.returnValue(props);
			spyOn(controller, 'getState')
				.and
				.returnValue(state);
			spyOn(rendererFactory, 'wrapView')
				.and
				.returnValue(wrapedPageViewElement);
			spyOn(ReactDOMServer, 'renderToString')
				.and
				.returnValue(pageMarkup);
			spyOn(rendererFactory, 'reactCreateFactory')
				.and
				.returnValue(documentViewFactory);
			spyOn(rendererFactory, 'getDocumentView')
				.and
				.returnValue(documentView);
			spyOn(ReactDOMServer, 'renderToStaticMarkup')
				.and
				.returnValue(appMarkup);
			spyOn(pageRenderer, '_getRevivalSettings')
				.and
				.returnValue(revivalSettings);
			spyOn(controller, 'getMetaManager')
				.and
				.returnValue(metaManager);
			spyOn(rendererFactory, 'getUtils')
				.and
				.returnValue(utils);

			pageContent = pageRenderer._renderPageContentToString(controller, view, routeOptions);
		});

		it('should generate view props from controller state', function() {
			expect(pageRenderer._generateViewProps).toHaveBeenCalledWith(view, state);
		});

		it('should wrap page view', function() {
			expect(rendererFactory.wrapView).toHaveBeenCalledWith(props);
		});

		it('should render page view to string', function() {
			expect(ReactDOMServer.renderToString).toHaveBeenCalledWith(wrapedPageViewElement);
		});

		it('should create factory for creating React element from document view', function() {
			expect(rendererFactory.reactCreateFactory).toHaveBeenCalledWith(documentView);
		});

		it('should create factory for creating React element from route options documentView property', function() {
			var routeOptionsWithDocument = Object.assign({}, routeOptions, { documentView: ns.ima.page.AbstractDocumentView });
			pageContent = pageRenderer._renderPageContentToString(controller, view, routeOptionsWithDocument);

			expect(rendererFactory.getDocumentView).toHaveBeenCalledWith(ns.ima.page.AbstractDocumentView);
		});

		it('should render static markup from document view', function() {
			expect(rendererFactory.getUtils).toHaveBeenCalled();
			expect(controller.getMetaManager).toHaveBeenCalled();
			expect(pageRenderer._getRevivalSettings).toHaveBeenCalled();
			expect(ReactDOMServer.renderToStaticMarkup).toHaveBeenCalledWith(documentViewElement);
		});

		it('should return page content', function() {
			expect(pageContent).toEqual('<!doctype html>\n' + appMarkup);
		});
	});

});
<|endoftext|>


<commit_before>/**
 * @fileoverview stamp entity object is class for entry stamp entity canvas view.
 */

'use strict';

import { GEHelper } from '../graphicEngine/GEHelper';

/**
 * Construct stamp entity class
 * @param {!Entry.EntryObject} object
 * @param {!Entry.EntityObject} entity
 * @constructor
 */
Entry.StampEntity = class StampEntity {
    constructor(object, entity) {
        /** @type {!string} */
        this.parent = object;
        this.type = object.objectType;
        this.isClone = true;
        this.isStamp = true;
        this.width = entity.getWidth();
        this.height = entity.getHeight();
        if (this.type == 'sprite') {
            this.object = GEHelper.cloneStamp(entity);
            if (entity.effect) {
                this.effect = _.clone(entity.effect);
                this.applyFilter();
            }
        } else if (this.type == 'textBox') {
        }

        this.object.entity = entity;
    }
};

(function(p, origin) {
    ['applyFilter', 'getWidth', 'getHeight', 'getInitialEffectValue', 'destroy', 'cache'].forEach(
        (key) => {
            p[key] = origin[key];
        }
    );
})(Entry.StampEntity.prototype, Entry.EntityObject.prototype);
<commit_msg>inheritance by matching changed to extends<commit_after>/**
 * @fileoverview stamp entity object is class for entry stamp entity canvas view.
 */

'use strict';

import { GEHelper } from '../graphicEngine/GEHelper';

/**
 * Construct stamp entity class
 * @param {!Entry.EntryObject} object
 * @param {!Entry.EntityObject} entity
 * @constructor
 */
Entry.StampEntity = class StampEntity extends Entry.EntityObject {
    constructor(object, entity) {
        /** @type {!string} */
        this.parent = object;
        this.type = object.objectType;
        this.isClone = true;
        this.isStamp = true;
        this.width = entity.getWidth();
        this.height = entity.getHeight();
        if (this.type == 'sprite') {
            this.object = GEHelper.cloneStamp(entity);
            if (entity.effect) {
                this.effect = _.clone(entity.effect);
                this.applyFilter();
            }
        } else if (this.type == 'textBox') {
        }

        this.object.entity = entity;
    }
};
<|endoftext|>