<commit_before>import Promise from 'bluebird'
import URL from 'url'
import querystring from 'querystring'
import { EventEmitter } from 'events'
import zlib from 'zlib'
import fetch from 'node-fetch'

const { format } = URL

Promise.promisifyAll(zlib)

const DEFAULT_SHARD = 'shard0'

export class RawAPI extends EventEmitter {
  constructor (opts = {}) {
    super()
    this.setServer(opts)
    let self = this
    this.raw = {
      version () {
        return self.req('GET', '/api/version')
      },
      authmod () {
        if (self.opts.url.match(/screeps\.com/)) {
          return Promise.resolve({ name: 'official' })
        }
        return self.req('GET', '/authmod')
      },
      history (room, tick) {
        tick = Math.round(tick / 20) * 20
        return self.req('GET', `/room-history/${room}/${tick}.json`)
      },
      auth: {
        signin (email, password) {
          return self.req('POST', '/api/auth/signin', { email, password })
        },
        steamTicket (ticket, useNativeAuth = false) {
          return self.req('POST', '/api/auth/steam-ticket', { ticket, useNativeAuth })
        },
        me () {
          return self.req('GET', '/api/auth/me')
        }
      },
      register: {
        checkEmail (email) {
          return self.req('GET', '/api/register/check-email', { email })
        },
        checkUsername (username) {
          return self.req('GET', '/api/register/check-username', { username })
        },
        setUsername (username) {
          return self.req('POST', '/api/register/set-username', { username })
        },
        submit (username, email, password, modules) {
          return self.req('POST', '/api/register/submit', { username, email, password, modules })
        }
      },
      userMessages: {
        list (respondent) {
          return self.req('GET', '/api/user-messages/list', { respondent })
        },
        index () {
          return self.req('GET', '/api/user-messages/index')
        },
        unreadCount () {
          return self.req('GET', '/api/user-messages/unread-count')
        },
        send (respondent, text) {
          return self.req('POST', '/api/user-messages/send', { respondent, text })
        },
        markRead (id) {
          return self.req('POST', '/api/user-messages/mark-read', { id })
        }
      },
      game: {
        mapStats (rooms, statName, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/map-stats', { rooms, statName, shard })
        },
        genUniqueObjectName (type, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/gen-unique-object-name', { type, shard })
        },
        checkUniqueObjectName (type, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/check-unique-object-name', { type, name, shard })
        },
        placeSpawn (room, x, y, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/place-spawn', { name, room, x, y, shard })
        },
        createFlag (room, x, y, name, color = 1, secondaryColor = 1, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-flag', { name, room, x, y, color, secondaryColor, shard })
        },
        genUniqueFlagName (shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/gen-unique-flag-name', { shard })
        },
        checkUniqueFlagName (name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/check-unique-flag-name', { name, shard })
        },
        changeFlagColor (color = 1, secondaryColor = 1, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/change-flag-color', { color, secondaryColor, shard })
        },
        removeFlag (room, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/remove-flag', { name, room, shard })
        },
        addObjectIntent (room, name, intent, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/add-object-intent', { room, name, intent, shard })
        },
        createConstruction (room, x, y, structureType, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-construction', { room, x, y, structureType, name, shard })
        },
        setNotifyWhenAttacked (_id, enabled = true, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/set-notify-when-attacked', { _id, enabled, shard })
        },
        createInvader (room, x, y, size, type, boosted = false, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-invader', { room, x, y, size, type, boosted, shard })
        },
        removeInvader (_id, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/remove-invader', { _id, shard })
        },
        time (shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/time', { shard })
        },
        worldSize (shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/world-size', { shard })
        },
        roomTerrain (room, encoded = 1, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-terrain', { room, encoded, shard })
        },
        roomStatus (room, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-status', { room, shard })
        },
        roomOverview (room, interval = 8, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-overview', { room, interval, shard })
        },
        market: {
          ordersIndex (shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/orders-index', { shard })
          },
          myOrders () {
            return self.req('GET', '/api/game/market/my-orders').then(this.mapToShard)
          },
          orders (resourceType, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/orders', { resourceType, shard })
          },
          stats (resourceType, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/stats', { resourceType, shard })
          }
        }
      },
      leaderboard: {
        list () {
          return self.req('GET', '/api/leaderboard/list')
        },
        find (username, mode = 'world', season = '') {
          return self.req('GET', '/api/leaderboard/find', { season, mode, username })
        },
        seasons () {
          return self.req('GET', '/api/leaderboard/seasons')
        }
      },
      user: {
        badge (badge) {
          return self.req('POST', '/api/user/badge', { badge })
        },
        respawn () {
          return self.req('POST', '/api/user/respawn')
        },
        setActiveBranch (branch, activeName) {
          return self.req('POST', '/api/user/set-active-branch', { branch, activeName })
        },
        cloneBranch (branch, newName, defaultModules) {
          return self.req('POST', '/api/user/clone-branch', { branch, newName, defaultModules })
        },
        deleteBranch (branch) {
          return self.req('POST', '/api/user/delete-branch', { branch })
        },
        notifyPrefs (prefs) {
          // disabled,disabledOnMessages,sendOnline,interval,errorsInterval
          return self.req('POST', '/api/user/notify-prefs', prefs)
        },
        tutorialDone () {
          return self.req('POST', '/api/user/tutorial-done')
        },
        email (email) {
          return self.req('POST', '/api/user/email', { email })
        },
        worldStartRoom (shard) {
          return self.req('GET', '/api/user/world-start-room', { shard })
        },
        worldStatus () {
          return self.req('GET', '/api/user/world-status')
        },
        branches () {
          return self.req('GET', '/api/user/branches')
        },
        code: {
          get (branch) {
            return self.req('GET', '/api/user/code', { branch })
          },
          set (branch, modules, _hash) {
            if (!_hash) _hash = Date.now()
            return self.req('POST', '/api/user/code', { branch, modules, _hash })
          }
        },
        respawnProhibitedRooms () {
          return self.req('GET', '/api/user/respawn-prohibited-rooms')
        },
        memory: {
          get (path, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/user/memory', { path, shard })
          },
          set (path, value, shard = DEFAULT_SHARD) {
            return self.req('POST', '/api/user/memory', { path, value, shard })
          },
          segment: {
            get (segment, shard = DEFAULT_SHARD) {
              return self.req('GET', '/api/user/memory-segment', { segment, shard })
            },
            set (segment, data, shard = DEFAULT_SHARD) {
              return self.req('POST', '/api/user/memory-segment', { segment, data, shard })
            }
          }
        },
        find (username) {
          return self.req('GET', '/api/user/find', { username })
        },
        findById (id) {
          return self.req('GET', '/api/user/find', { id })
        },
        stats (interval) {
          return self.req('GET', '/api/user/stats', { interval })
        },
        rooms (id) {
          return self.req('GET', '/api/user/rooms', { id }).then(this.mapToShard)
        },
        overview (interval, statName) {
          return self.req('GET', '/api/user/overview', { interval, statName })
        },
        moneyHistory (page = 0) {
          return self.req('GET', '/api/user/money-history', { page })
        },
        console (expression, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/user/console', { expression, shard })
        }
      },
      shards: {
        info () {
          return self.req('GET', '/api/shards/info')
        }
      }
    }
  }
  mapToShard (res) {
    if (!res.shards) {
      res.shards = {
        [DEFAULT_SHARD]: res.list || res.rooms
      }
    }
    return res
  }
  setServer (opts) {
    if (!this.opts) {
      this.opts = {}
    }
    Object.assign(this.opts, opts)
    if (!opts.url) {
      this.opts.pathname = this.opts.pathname || this.opts.path
      this.opts.url = format(this.opts)
    }
  }
  async auth (email, password, opts = {}) {
    this.setServer(opts)
    if (email && password) {
      Object.assign(this.opts, { email, password })
    }
    let res = await this.raw.auth.signin(email, password)
    this.emit('token', res.token)
    this.emit('auth')
    this.__authed = true
    return res
  }
  async req (method, path, body = {}) {
    let opts = {
      method,
      headers: {
        'X-Token': this.token,
        'X-Username': this.token
      }
    }
    let url = URL.resolve(this.opts.url, path)
    if (method === 'GET') {
      url += '?' + querystring.stringify(body)
    }
    if (method === 'POST') {
      opts.headers['content-type'] = 'application/json'
      opts.body = JSON.stringify(body)
    }
    let res = await fetch(url, opts)
    if (res.status === 401) {
      if (this.__authed) {
        this.__authed = false
        await this.auth(this.opts.email, this.opts.password)
      } else {
        throw new Error('Not Authorized')
      }
    }
    let token = res.headers.get('x-token')
    if (token) {
      this.emit('token', token)
    }
    this.emit('response', res)
    if (!res.ok) {
      throw new Error(await res.text())
    }
    res = await res.json()
    if (typeof res.data === 'string' && res.data.slice(0, 3) === 'gz:') {
      if (this.opts.url.match(/screeps\.com/)) { res.data = await this.gz(res.data) } else { res.data = await this.inflate(res.data) }
    }
    return res
  }
  async gz (data) {
    let buf = Buffer.from(data.slice(3), 'base64')
    let ret = await zlib.gunzipAsync(buf)
    return JSON.parse(ret.toString())
  }
  async inflate (data) { // es
    let buf = Buffer.from(data.slice(3), 'base64')
    let ret = await zlib.inflateAsync(buf)
    return JSON.parse(ret.toString())
  }
}
<commit_msg>Fix user-messages<commit_after>import Promise from 'bluebird'
import URL from 'url'
import querystring from 'querystring'
import { EventEmitter } from 'events'
import zlib from 'zlib'
import fetch from 'node-fetch'

const { format } = URL

Promise.promisifyAll(zlib)

const DEFAULT_SHARD = 'shard0'

export class RawAPI extends EventEmitter {
  constructor (opts = {}) {
    super()
    this.setServer(opts)
    let self = this
    this.raw = {
      version () {
        return self.req('GET', '/api/version')
      },
      authmod () {
        if (self.opts.url.match(/screeps\.com/)) {
          return Promise.resolve({ name: 'official' })
        }
        return self.req('GET', '/authmod')
      },
      history (room, tick) {
        tick = Math.round(tick / 20) * 20
        return self.req('GET', `/room-history/${room}/${tick}.json`)
      },
      auth: {
        signin (email, password) {
          return self.req('POST', '/api/auth/signin', { email, password })
        },
        steamTicket (ticket, useNativeAuth = false) {
          return self.req('POST', '/api/auth/steam-ticket', { ticket, useNativeAuth })
        },
        me () {
          return self.req('GET', '/api/auth/me')
        }
      },
      register: {
        checkEmail (email) {
          return self.req('GET', '/api/register/check-email', { email })
        },
        checkUsername (username) {
          return self.req('GET', '/api/register/check-username', { username })
        },
        setUsername (username) {
          return self.req('POST', '/api/register/set-username', { username })
        },
        submit (username, email, password, modules) {
          return self.req('POST', '/api/register/submit', { username, email, password, modules })
        }
      },
      userMessages: {
        list (respondent) {
          return self.req('GET', '/api/user/messages/list', { respondent })
        },
        index () {
          return self.req('GET', '/api/user/messages/index')
        },
        unreadCount () {
          return self.req('GET', '/api/user/messages/unread-count')
        },
        send (respondent, text) {
          return self.req('POST', '/api/user/messages/send', { respondent, text })
        },
        markRead (id) {
          return self.req('POST', '/api/user/messages/mark-read', { id })
        }
      },
      game: {
        mapStats (rooms, statName, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/map-stats', { rooms, statName, shard })
        },
        genUniqueObjectName (type, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/gen-unique-object-name', { type, shard })
        },
        checkUniqueObjectName (type, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/check-unique-object-name', { type, name, shard })
        },
        placeSpawn (room, x, y, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/place-spawn', { name, room, x, y, shard })
        },
        createFlag (room, x, y, name, color = 1, secondaryColor = 1, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-flag', { name, room, x, y, color, secondaryColor, shard })
        },
        genUniqueFlagName (shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/gen-unique-flag-name', { shard })
        },
        checkUniqueFlagName (name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/check-unique-flag-name', { name, shard })
        },
        changeFlagColor (color = 1, secondaryColor = 1, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/change-flag-color', { color, secondaryColor, shard })
        },
        removeFlag (room, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/remove-flag', { name, room, shard })
        },
        addObjectIntent (room, name, intent, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/add-object-intent', { room, name, intent, shard })
        },
        createConstruction (room, x, y, structureType, name, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-construction', { room, x, y, structureType, name, shard })
        },
        setNotifyWhenAttacked (_id, enabled = true, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/set-notify-when-attacked', { _id, enabled, shard })
        },
        createInvader (room, x, y, size, type, boosted = false, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/create-invader', { room, x, y, size, type, boosted, shard })
        },
        removeInvader (_id, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/game/remove-invader', { _id, shard })
        },
        time (shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/time', { shard })
        },
        worldSize (shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/world-size', { shard })
        },
        roomTerrain (room, encoded = 1, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-terrain', { room, encoded, shard })
        },
        roomStatus (room, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-status', { room, shard })
        },
        roomOverview (room, interval = 8, shard = DEFAULT_SHARD) {
          return self.req('GET', '/api/game/room-overview', { room, interval, shard })
        },
        market: {
          ordersIndex (shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/orders-index', { shard })
          },
          myOrders () {
            return self.req('GET', '/api/game/market/my-orders').then(this.mapToShard)
          },
          orders (resourceType, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/orders', { resourceType, shard })
          },
          stats (resourceType, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/game/market/stats', { resourceType, shard })
          }
        }
      },
      leaderboard: {
        list () {
          return self.req('GET', '/api/leaderboard/list')
        },
        find (username, mode = 'world', season = '') {
          return self.req('GET', '/api/leaderboard/find', { season, mode, username })
        },
        seasons () {
          return self.req('GET', '/api/leaderboard/seasons')
        }
      },
      user: {
        badge (badge) {
          return self.req('POST', '/api/user/badge', { badge })
        },
        respawn () {
          return self.req('POST', '/api/user/respawn')
        },
        setActiveBranch (branch, activeName) {
          return self.req('POST', '/api/user/set-active-branch', { branch, activeName })
        },
        cloneBranch (branch, newName, defaultModules) {
          return self.req('POST', '/api/user/clone-branch', { branch, newName, defaultModules })
        },
        deleteBranch (branch) {
          return self.req('POST', '/api/user/delete-branch', { branch })
        },
        notifyPrefs (prefs) {
          // disabled,disabledOnMessages,sendOnline,interval,errorsInterval
          return self.req('POST', '/api/user/notify-prefs', prefs)
        },
        tutorialDone () {
          return self.req('POST', '/api/user/tutorial-done')
        },
        email (email) {
          return self.req('POST', '/api/user/email', { email })
        },
        worldStartRoom (shard) {
          return self.req('GET', '/api/user/world-start-room', { shard })
        },
        worldStatus () {
          return self.req('GET', '/api/user/world-status')
        },
        branches () {
          return self.req('GET', '/api/user/branches')
        },
        code: {
          get (branch) {
            return self.req('GET', '/api/user/code', { branch })
          },
          set (branch, modules, _hash) {
            if (!_hash) _hash = Date.now()
            return self.req('POST', '/api/user/code', { branch, modules, _hash })
          }
        },
        respawnProhibitedRooms () {
          return self.req('GET', '/api/user/respawn-prohibited-rooms')
        },
        memory: {
          get (path, shard = DEFAULT_SHARD) {
            return self.req('GET', '/api/user/memory', { path, shard })
          },
          set (path, value, shard = DEFAULT_SHARD) {
            return self.req('POST', '/api/user/memory', { path, value, shard })
          },
          segment: {
            get (segment, shard = DEFAULT_SHARD) {
              return self.req('GET', '/api/user/memory-segment', { segment, shard })
            },
            set (segment, data, shard = DEFAULT_SHARD) {
              return self.req('POST', '/api/user/memory-segment', { segment, data, shard })
            }
          }
        },
        find (username) {
          return self.req('GET', '/api/user/find', { username })
        },
        findById (id) {
          return self.req('GET', '/api/user/find', { id })
        },
        stats (interval) {
          return self.req('GET', '/api/user/stats', { interval })
        },
        rooms (id) {
          return self.req('GET', '/api/user/rooms', { id }).then(this.mapToShard)
        },
        overview (interval, statName) {
          return self.req('GET', '/api/user/overview', { interval, statName })
        },
        moneyHistory (page = 0) {
          return self.req('GET', '/api/user/money-history', { page })
        },
        console (expression, shard = DEFAULT_SHARD) {
          return self.req('POST', '/api/user/console', { expression, shard })
        }
      },
      shards: {
        info () {
          return self.req('GET', '/api/shards/info')
        }
      }
    }
  }
  mapToShard (res) {
    if (!res.shards) {
      res.shards = {
        [DEFAULT_SHARD]: res.list || res.rooms
      }
    }
    return res
  }
  setServer (opts) {
    if (!this.opts) {
      this.opts = {}
    }
    Object.assign(this.opts, opts)
    if (!opts.url) {
      this.opts.pathname = this.opts.pathname || this.opts.path
      this.opts.url = format(this.opts)
    }
  }
  async auth (email, password, opts = {}) {
    this.setServer(opts)
    if (email && password) {
      Object.assign(this.opts, { email, password })
    }
    let res = await this.raw.auth.signin(email, password)
    this.emit('token', res.token)
    this.emit('auth')
    this.__authed = true
    return res
  }
  async req (method, path, body = {}) {
    let opts = {
      method,
      headers: {
        'X-Token': this.token,
        'X-Username': this.token
      }
    }
    let url = URL.resolve(this.opts.url, path)
    if (method === 'GET') {
      url += '?' + querystring.stringify(body)
    }
    if (method === 'POST') {
      opts.headers['content-type'] = 'application/json'
      opts.body = JSON.stringify(body)
    }
    let res = await fetch(url, opts)
    if (res.status === 401) {
      if (this.__authed) {
        this.__authed = false
        await this.auth(this.opts.email, this.opts.password)
      } else {
        throw new Error('Not Authorized')
      }
    }
    let token = res.headers.get('x-token')
    if (token) {
      this.emit('token', token)
    }
    this.emit('response', res)
    if (!res.ok) {
      throw new Error(await res.text())
    }
    res = await res.json()
    if (typeof res.data === 'string' && res.data.slice(0, 3) === 'gz:') {
      if (this.opts.url.match(/screeps\.com/)) { res.data = await this.gz(res.data) } else { res.data = await this.inflate(res.data) }
    }
    return res
  }
  async gz (data) {
    let buf = Buffer.from(data.slice(3), 'base64')
    let ret = await zlib.gunzipAsync(buf)
    return JSON.parse(ret.toString())
  }
  async inflate (data) { // es
    let buf = Buffer.from(data.slice(3), 'base64')
    let ret = await zlib.inflateAsync(buf)
    return JSON.parse(ret.toString())
  }
}
<|endoftext|>


<commit_before>var webviewGroup = function(id, options) {
	this.id = id;
	this.options = options;
	this.styles = options.styles;
	this.items = options.items;
	this.onChange = options.onChange

	this.options.index = options.index || 0;

	this.webviews = {};
	this.webviewContexts = {};
	this.currentWebview = false;
	this._init();
};

var proto = webviewGroup.prototype;

proto._init = function() {
	this._initParent();
	this._initNativeView();
	this._initWebviewContexts(this.options.index);
};
proto._initParent = function() {
	this.parent = plus.webview.getWebviewById(this.id);
	if(!this.parent) {
		this.parent = plus.webview.create(this.id, this.id);
		this.parent.show('none');
	}
};
proto._initNativeView = function() {
	this.nativeView = new plus.nativeObj.View('__W2A_TAB_NATIVE', {
		'top': '83px',
		'bottom': "0px",
		'left': '100%',
		'width': '100%',
		'backgroudColor': '#ffffff'
	});
	this.nativeView.show();
};
proto._initWebviewContexts = function() {
	for(var len = this.items.length, i = len - 1; i >= 0; i--) {
		var webviewOptions = this.items[i];
		var id = webviewOptions.id;
		var isFirst = i === 0;
		var isLast = i === (len - 1);
		var isCurrent = this.options.index === i;
		var extras = webviewOptions.extras;
		extras.__wap2app_url = webviewOptions.url;
		extras.__wap2app_index = i;

		extras.__wap2app_left = isFirst ? '' : this.items[i - 1].id;
		extras.__wap2app_right = isLast ? '' : this.items[i + 1].id;

		var styles = webviewOptions.styles || {};

		if(i > this.options.index) {
			styles.left = '100%';
		} else if(i < this.options.index) {
			styles.left = '-100%';
		} else {
			styles.left = '0';
		}
		var webviewContext = new webviewGroupContext(id, webviewOptions, this);
		this.webviewContexts[id] = webviewContext;
		if(isCurrent) {
			webviewContext.webview = plus.webview.getWebviewById(id);
			webviewContext.createWebview();
			webviewContext.webview.show("none");
			this._initDrags(webviewContext.webview);
			this.currentWebview = webviewContext.webview;
		}
	}
};
proto._onChange = function(webview) {
	this.currentWebview = webview;
	this.onChange({
		index: webview.__wap2app_index
	});
};
proto._dragCallback = function(dir, fromWebview, view, viewId) {
	if(view === this.nativeView) { //需要创建webview
		//第一步:初始化目标webview
		this.webviewContexts[viewId].createWebview('drag');
		var targetWebview = this.webviewContexts[viewId].webview;
		targetWebview.show();
		this.nativeView.setStyle({
			left: '100%'
		});
		//第二步:初始化目标webview的drag
		this._initDrags(targetWebview);
		this._onChange(targetWebview);
		//第三步:校验目标webview的左右webview的drag初始化
		this._checkDrags(targetWebview);
	} else {
		this._onChange(view);
	}
};

proto._initDrag = function(webview, dir) {
	var flag = ('__wap2app_drag_' + dir + '_flag');
	if(webview[flag]) {
		return;
	}
	var viewId = webview['__wap2app_' + (dir === 'left' ? 'right' : 'left')];
	if(viewId) {
		var view = plus.webview.getWebviewById(viewId);
		if(!view) { //如果目标webview不存在,使用nativeView替换
			view = this.nativeView;
		} else {
			webview[flag] = true;
		}
		webview.drag({
				'direction': dir,
				'moveMode': 'followFinger'
			}, {
				'view': view,
				'moveMode': 'follow'
			},
			function(res) {
				if(res.type === 'end' && res.result) { //拖拽完成
					this._dragCallback(dir, webview, view, viewId);
				}
			}.bind(this)
		)
	} else {
		webview[flag] = true;
	}
};
proto._initDrags = function(webview) {
	this._initDrag(webview, 'left');
	this._initDrag(webview, 'right');
};
proto._checkDrags = function(webview) {
	var left = webview.__wap2app_left;
	var right = webview.__wap2app_right;
	if(left) {
		var leftWebview = plus.webview.getWebviewById(left);
		if(leftWebview && !leftWebview.__wap2app_drag_left_flag) {
			this._initDrag(leftWebview, 'left');
		}
	}
	if(right) {
		var rightWebview = plus.webview.getWebviewById(right);
		if(rightWebview && !rightWebview.__wap2app_drag_right_flag) {
			this._initDrag(rightWebview, 'right');
		}
	}
};
proto.getCurrentWebview = function() {
	return this.currentWebview;
};
proto.getCurrentWebviewContext = function() {
	if(this.currentWebview) {
		return this.webviewContexts[this.currentWebview.id];
	}
	return false;
};
proto.switchTab = function(id) {
	id = id.replace('_0', ''); //首页需要替换为appid
	var fromWebview = this.currentWebview;
	if(id === fromWebview.id) {
		return;
	}
	var toWebviewContext = this.webviewContexts[id];
	var toWebview = toWebviewContext.webview;
	var fromToLeft = '100%';
	var toFromLeft = '-100%';
	if(toWebviewContext.options.extras.__wap2app_index > fromWebview.__wap2app_index) {
		fromToLeft = '-100%';
		toFromLeft = '100%';
	}
	var isNew = false;
	if(!toWebview) {
		isNew = true;
		toWebviewContext.createWebview('startAnimation');
		toWebview = toWebviewContext.webview;
		//					toWebview.showBehind(plus.webview.getSecondWebview());
		toWebview.show();
		this._initDrags(toWebview);
		this._checkDrags(toWebview); //新建的时候均需校验
	}
	var self = this;
	//				toWebview.show("none");
	console.log("current:" + fromWebview.id + ",to:" + fromToLeft);
	console.log("next:" + toWebview.id + ",from:" + toFromLeft);

	//				console.log("current_fromToLeft:"+fromToLeft+",nexttoFromLeft:"+toFromLeft);
	plus.webview.startAnimation({
			'view': fromWebview,
			'styles': {
				'fromLeft': '0',
				'toLeft': fromToLeft
			},
			'action': 'show'
		}, {
			'view': toWebview,
			'styles': {
				'fromLeft': toFromLeft,
				'toLeft': '0'
			},
			'action': 'show'
		},
		function(e) {
			if(e.id === toWebview.id) {
				isNew && plus.nativeUI.showWaiting();
				this.currentWebview = toWebview;
				this.onChange({
					index: toWebview.__wap2app_index
				});
			}
		}.bind(this)
	)
};

/**
 * @param {Object} id
 * @param {Object} webviewOptions
 */
var webviewGroupContext = function(id, webviewOptions, groupContext) {
	this.id = id;
	this.url = webviewOptions.url;
	this.options = webviewOptions;
	this.groupContext = groupContext;

	this.webview = false;
	this.inited = false;
};

var _proto = webviewGroupContext.prototype;

_proto.createWebview = function(from) {
	var options = this.options;
	options.styles = options.styles || {
		top: "83px",
		bottom: "0px",
		render: "always"
	};
	options.styles.popGesture = 'none';
	if(this.webview) {
		this.webview.setStyle(options.styles);
		for(var key in options.extras) {
			this.webview[key] = options.extras[key];
		}
	} else {
		options.styles.left = '100%';
		if(from !== 'startAnimation') {
			options.styles.left = '0';
			plus.nativeUI.showWaiting();
		}
		this.webview = plus.webview.create(this.url, this.id, options.styles, options.extras);
		//append进去，避免返回时闪屏
		plus.webview.currentWebview().append(this.webview);
	}
	this._initWebview();
	this.inited = true;
};
_proto._initWebview = function() {
	var options = this.options;
	if(!this.webview) {
		return;
	}
	this.webview.addEventListener('rendering', function() {
		setTimeout(function() {
			plus.nativeUI.closeWaiting();
		}, 500);
	});
	if(options.pullToRefresh && options.pullToRefresh.support && support.pullToRefresh()) {
		var callback = options.pullToRefresh.callback;
		this.webview.setPullToRefresh(options.pullToRefresh, function() {
			if(callback) { //如果指定了下拉回调
				callback(this.webview);
			} else { //下拉刷新回调，默认reload当前页面
				var self = this;
				var titleUpdate = function() {
					setTimeout(function() {
						self.webview.endPullToRefresh();
					}.bind(this), 1000);
					self.webview.removeEventListener('titleUpdate', titleUpdate);
				};
				this.webview.addEventListener('titleUpdate', titleUpdate);
				this.webview.reload();
			}
		}.bind(this));
	}
};<commit_msg>更新webviewGroup，动态计算nativeView高度<commit_after>var webviewGroup = function(id, options) {
	this.id = id;
	this.options = options;
	this.styles = options.styles;
	this.items = options.items;
	this.onChange = options.onChange

	this.options.index = options.index || 0;

	this.webviews = {};
	this.webviewContexts = {};
	this.currentWebview = false;
	this._init();
};

var proto = webviewGroup.prototype;

proto._init = function() {
	this._initParent();
	this._initNativeView();
	this._initWebviewContexts(this.options.index);
};
proto._initParent = function() {
	this.parent = plus.webview.getWebviewById(this.id);
	if(!this.parent) {
		this.parent = plus.webview.create(this.id, this.id);
		this.parent.show('none');
	}
};
proto._initNativeView = function() {
	this.nativeView = new plus.nativeObj.View('__MUI_TAB_NATIVE', {
		'top': '83px',//这个需要根据顶部导航及顶部选项卡高度自动调整
		'height': (window.screen.height - 83)+"px",
		'left': '100%',
		'width': '100%',
		"backgroundColor":"#ffffff"
	});
	this.nativeView.show();
};
proto._initWebviewContexts = function() {
	for(var len = this.items.length, i = len - 1; i >= 0; i--) {
		var webviewOptions = this.items[i];
		var id = webviewOptions.id;
		var isFirst = i === 0;
		var isLast = i === (len - 1);
		var isCurrent = this.options.index === i;
		var extras = webviewOptions.extras;
		extras.__mui_url = webviewOptions.url;
		extras.__mui_index = i;

		extras.__mui_left = isFirst ? '' : this.items[i - 1].id;
		extras.__mui_right = isLast ? '' : this.items[i + 1].id;

		var styles = webviewOptions.styles || {};

		if(i > this.options.index) {
			styles.left = '100%';
		} else if(i < this.options.index) {
			styles.left = '-100%';
		} else {
			styles.left = '0';
		}
		var webviewContext = new webviewGroupContext(id, webviewOptions, this);
		this.webviewContexts[id] = webviewContext;
		if(isCurrent) {
			webviewContext.webview = plus.webview.getWebviewById(id);
			webviewContext.createWebview();
			webviewContext.webview.show("none");
			this._initDrags(webviewContext.webview);
			this.currentWebview = webviewContext.webview;
		}
	}
};
proto._onChange = function(webview) {
	this.currentWebview = webview;
	this.onChange({
		index: webview.__mui_index
	});
};
proto._dragCallback = function(dir, fromWebview, view, viewId) {
	if(view === this.nativeView) { //需要创建webview
		//第一步:初始化目标webview
		this.webviewContexts[viewId].createWebview('drag');
		var targetWebview = this.webviewContexts[viewId].webview;
		targetWebview.show();
		this.nativeView.setStyle({
			left: '100%'
		});
		//第二步:初始化目标webview的drag
		this._initDrags(targetWebview);
		this._onChange(targetWebview);
		//第三步:校验目标webview的左右webview的drag初始化
		this._checkDrags(targetWebview);
	} else {
		this._onChange(view);
	}
};

proto._initDrag = function(webview, dir) {
	var flag = ('__mui_drag_' + dir + '_flag');
	if(webview[flag]) {
		return;
	}
	var viewId = webview['__mui_' + (dir === 'left' ? 'right' : 'left')];
	if(viewId) {
		var view = plus.webview.getWebviewById(viewId);
		if(!view) { //如果目标webview不存在,使用nativeView替换
			view = this.nativeView;
		} else {
			webview[flag] = true;
		}
		webview.drag({
				'direction': dir,
				'moveMode': 'followFinger'
			}, {
				'view': view,
				'moveMode': 'follow'
			},
			function(res) {
				if(res.type === 'end' && res.result) { //拖拽完成
					this._dragCallback(dir, webview, view, viewId);
				}
			}.bind(this)
		)
	} else {
		webview[flag] = true;
	}
};
proto._initDrags = function(webview) {
	this._initDrag(webview, 'left');
	this._initDrag(webview, 'right');
};
proto._checkDrags = function(webview) {
	var left = webview.__mui_left;
	var right = webview.__mui_right;
	if(left) {
		var leftWebview = plus.webview.getWebviewById(left);
		if(leftWebview && !leftWebview.__mui_drag_left_flag) {
			this._initDrag(leftWebview, 'left');
		}
	}
	if(right) {
		var rightWebview = plus.webview.getWebviewById(right);
		if(rightWebview && !rightWebview.__mui_drag_right_flag) {
			this._initDrag(rightWebview, 'right');
		}
	}
};
proto.getCurrentWebview = function() {
	return this.currentWebview;
};
proto.getCurrentWebviewContext = function() {
	if(this.currentWebview) {
		return this.webviewContexts[this.currentWebview.id];
	}
	return false;
};
proto.switchTab = function(id) {
	id = id.replace('_0', ''); //首页需要替换为appid
	var fromWebview = this.currentWebview;
	if(id === fromWebview.id) {
		return;
	}
	var toWebviewContext = this.webviewContexts[id];
	var toWebview = toWebviewContext.webview;
	var fromToLeft = '100%';
	var toFromLeft = '-100%';
	if(toWebviewContext.options.extras.__mui_index > fromWebview.__mui_index) {
		fromToLeft = '-100%';
		toFromLeft = '100%';
	}
	var isNew = false;
	if(!toWebview) {
		isNew = true;
		toWebviewContext.createWebview('startAnimation');
		toWebview = toWebviewContext.webview;
		//					toWebview.showBehind(plus.webview.getSecondWebview());
		toWebview.show();
		this._initDrags(toWebview);
		this._checkDrags(toWebview); //新建的时候均需校验
	}
	var self = this;
//	console.log("current:" + fromWebview.id + ",to:" + fromToLeft);
//	console.log("next:" + toWebview.id + ",from:" + toFromLeft);

	plus.webview.startAnimation({
			'view': fromWebview,
			'styles': {
				'fromLeft': '0',
				'toLeft': fromToLeft
			},
			'action': 'show'
		}, {
			'view': toWebview,
			'styles': {
				'fromLeft': toFromLeft,
				'toLeft': '0'
			},
			'action': 'show'
		},
		function(e) {
			//console.log("startAnimation callback...");
			if(e.id === toWebview.id) {
				isNew && plus.nativeUI.showWaiting();
				this.currentWebview = toWebview;
				this.onChange({
					index: toWebview.__mui_index
				});
			}
		}.bind(this)
	)
};

/**
 * @param {Object} id
 * @param {Object} webviewOptions
 */
var webviewGroupContext = function(id, webviewOptions, groupContext) {
	this.id = id;
	this.url = webviewOptions.url;
	this.options = webviewOptions;
	this.groupContext = groupContext;

	this.webview = false;
	this.inited = false;
};

var _proto = webviewGroupContext.prototype;

_proto.createWebview = function(from) {
	var options = this.options;
	options.styles = options.styles || {
		top: "83px",
		bottom: "0px",
		render: "always"
	};
	options.styles.popGesture = 'none';
	if(this.webview) {
		this.webview.setStyle(options.styles);
		for(var key in options.extras) {
			this.webview[key] = options.extras[key];
		}
	} else {
		options.styles.left = '100%';
		if(from !== 'startAnimation') {
			options.styles.left = '0';
			plus.nativeUI.showWaiting();
		}
		this.webview = plus.webview.create(this.url, this.id, options.styles, options.extras);
		//append进去，避免返回时闪屏
		plus.webview.currentWebview().append(this.webview);
	}
	this._initWebview();
	this.inited = true;
};
_proto._initWebview = function() {
	var options = this.options;
	if(!this.webview) {
		return;
	}
	this.webview.addEventListener('rendering', function() {
		setTimeout(function() {
			plus.nativeUI.closeWaiting();
		}, 500);
	});
	if(options.pullToRefresh && options.pullToRefresh.support && support.pullToRefresh()) {
		var callback = options.pullToRefresh.callback;
		this.webview.setPullToRefresh(options.pullToRefresh, function() {
			if(callback) { //如果指定了下拉回调
				callback(this.webview);
			} else { //下拉刷新回调，默认reload当前页面
				var self = this;
				var titleUpdate = function() {
					setTimeout(function() {
						self.webview.endPullToRefresh();
					}.bind(this), 1000);
					self.webview.removeEventListener('titleUpdate', titleUpdate);
				};
				this.webview.addEventListener('titleUpdate', titleUpdate);
				this.webview.reload();
			}
		}.bind(this));
	}
};<|endoftext|>


<commit_before>////////////////// Widget Directives ///////////////////

linuxDash.directive('diskSpace', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/disk-space.html',
        link: function (scope) {

            scope.heading =  "Disk Partitions";

            scope.getData = function () {
                server.get('disk_partitions', function (serverResponseData) {
                    scope.diskSpaceData = serverResponseData;
                });

                scope.lastGet = new Date().getTime();
            };

            scope.getData();

            scope.getKB = function (stringSize) {
                var lastChar = stringSize.slice(-1),
                    size = parseInt(stringSize);

                switch (lastChar){
                    case 'M': return size * Math.pow(1024, 1);
                    case 'G': return size * Math.pow(1024, 2);
                    case 'T': return size * Math.pow(1024, 3);
                    case 'P': return size * Math.pow(1024, 4);
                    case 'E': return size * Math.pow(1024, 5);
                    case 'Z': return size * Math.pow(1024, 6);
                    case 'Y': return size * Math.pow(1024, 7);
                    default: return size;
                }
            };
        }
    };
}]);

linuxDash.directive('ramChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/ram-chart.html',
        link: function (scope) {

            // get max ram available on machine before we
            // can start charting
            server.get('current_ram', function (resp) {
                scope.maxRam = resp['total'];
                scope.minRam = 0;
            });

            scope.ramToDisplay = function (serverResponseData) {
                return serverResponseData['used'];
            };

            scope.ramMetrics = [
                {
                    name: 'Used',
                    generate: function (serverResponseData) {
                        var ratio = serverResponseData['used'] / serverResponseData['total'];
                        var percentage = parseInt(ratio * 100);

                        return serverResponseData['used'] + ' MB ('
                                + percentage.toString() + '%)';
                    }
                },
                {
                    name: 'Free',
                    generate: function (serverResponseData) {
                        return serverResponseData['free'].toString()
                                + ' MB of '
                                + serverResponseData['total']
                                + 'MB';
                    }
                }
            ];
        }
    };
}]);

linuxDash.directive('cpuLoadChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/cpu-load.html',
        link: function (scope) {
            scope.units = '%';
        }
    };
}]);

linuxDash.directive('uploadTransferRateChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/upload-transfer-rate.html',
        link: function (scope) {
            scope.units = 'KB/s';
        }
    };
}]);

linuxDash.directive('downloadTransferRateChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/download-transfer-rate.html',
        link: function (scope) {
            scope.units = 'KB/s';
        }
    };
}]);

/////////////// Table Data Modules ////////////////////
var simpleTableModules = [
    {
        name: 'machineInfo',
        template: '<key-value-list heading="General Info." module-name="general_info"></key-value-list>'
    },
    {
        name: 'ipAddresses',
        template: '<table-data heading="IP Addresses" module-name="ip_addresses"></table-data>'
    },
    {
        name: 'ramIntensiveProcesses',
        template: '<table-data heading="RAM Intensive Processes" module-name="ram_intensive_processes"></table-data>'
    },
    {
        name: 'cpuIntensiveProcesses',
        template: '<table-data heading="CPU Intensive Processes" module-name="cpu_intensive_processes"></table-data>'
    },
    {
        name: 'networkConnections',
        template: '<table-data heading="Network Connections" module-name="network_connections"></table-data>'
    },
    {
        name: 'serverAccounts',
        template: '<table-data heading="Accounts" module-name="user_accounts"></table-data>'
    },
    {
        name: 'loggedInAccounts',
        template: '<table-data heading="Logged In Accounts" module-name="logged_in_users"></table-data>'
    },
    {
        name: 'recentLogins',
        template: '<table-data heading="Recent Logins" module-name="recent_account_logins"></table-data>'
    },
    {
        name: 'arpCacheTable',
        template: '<table-data heading="ARP Cache Table" module-name="arp_cache"></table-data>'
    },
    {
        name: 'commonApplications',
        template: '<table-data heading="Common Applications" module-name="common_applications"></table-data>'
    },
    {
        name: 'pingSpeeds',
        template: '<table-data heading="Ping Speeds" module-name="ping"></table-data>'
    },
    {
        name: 'bandwidth',
        template: '<table-data heading="Bandwidth" module-name="bandwidth"></table-data>'
    },
    {
        name: 'swapUsage',
        template: '<table-data heading="Swap Usage" module-name="swap"></table-data>'
    },
    {
        name: 'internetSpeed',
        template: '<key-value-list heading="Internet Speed" module-name="internet_speed"></key-value-list>'
    },
    {
        name: 'memcached',
        template: '<key-value-list heading="Memcached" module-name="memcached"></key-value-list>'
    },
    {
        name: 'redis',
        template: '<key-value-list heading="Redis" module-name="redis"></key-value-list>'
    },
    {
        name: 'memoryInfo',
        template: '<key-value-list heading="Memory Info" module-name="memory_info"></key-value-list>'
    },
    {
        name: 'cpuInfo',
        template: '<key-value-list heading="CPU Info" module-name="cpu_info"></key-value-list>'
    },
    {
        name: 'ioStats',
        template: '<table-data heading="IO Stats" module-name="io_stats"></table-data>'
    },
    {
        name: 'scheduledCrons',
        template: '<table-data heading="Scheduled Cron Jobs" module-name="scheduled_crons"></table-data>'
    },
    {
        name: 'cronHistory',
        template: '<table-data heading="Cron Job History" module-name="cron_history"></table-data>'
    },
];

simpleTableModules.forEach(function (module, key) {

    linuxDash.directive(module.name, ['server', function(server) {
        var moduleDirective = {
            restrict: 'E',
            scope: {},
        };

        if (module.templateUrl) {
            moduleDirective['templateUrl'] = 'templates/modules/' + module.templateUrl
        }

        if (module.template) {
            moduleDirective['template'] = module.template;
        }

        return moduleDirective;
    }]);

});
<commit_msg>Delay added for Network Charts.<commit_after>////////////////// Widget Directives ///////////////////

linuxDash.directive('diskSpace', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/disk-space.html',
        link: function (scope) {

            scope.heading =  "Disk Partitions";

            scope.getData = function () {
                server.get('disk_partitions', function (serverResponseData) {
                    scope.diskSpaceData = serverResponseData;
                });

                scope.lastGet = new Date().getTime();
            };

            scope.getData();

            scope.getKB = function (stringSize) {
                var lastChar = stringSize.slice(-1),
                    size = parseInt(stringSize);

                switch (lastChar){
                    case 'M': return size * Math.pow(1024, 1);
                    case 'G': return size * Math.pow(1024, 2);
                    case 'T': return size * Math.pow(1024, 3);
                    case 'P': return size * Math.pow(1024, 4);
                    case 'E': return size * Math.pow(1024, 5);
                    case 'Z': return size * Math.pow(1024, 6);
                    case 'Y': return size * Math.pow(1024, 7);
                    default: return size;
                }
            };
        }
    };
}]);

linuxDash.directive('ramChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/ram-chart.html',
        link: function (scope) {

            // get max ram available on machine before we
            // can start charting
            server.get('current_ram', function (resp) {
                scope.maxRam = resp['total'];
                scope.minRam = 0;
            });

            scope.ramToDisplay = function (serverResponseData) {
                return serverResponseData['used'];
            };

            scope.ramMetrics = [
                {
                    name: 'Used',
                    generate: function (serverResponseData) {
                        var ratio = serverResponseData['used'] / serverResponseData['total'];
                        var percentage = parseInt(ratio * 100);

                        return serverResponseData['used'] + ' MB ('
                                + percentage.toString() + '%)';
                    }
                },
                {
                    name: 'Free',
                    generate: function (serverResponseData) {
                        return serverResponseData['free'].toString()
                                + ' MB of '
                                + serverResponseData['total']
                                + 'MB';
                    }
                }
            ];
        }
    };
}]);

linuxDash.directive('cpuLoadChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/cpu-load.html',
        link: function (scope) {
            scope.units = '%';
        }
    };
}]);

linuxDash.directive('uploadTransferRateChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/upload-transfer-rate.html',
        link: function (scope) {
            scope.delay = 2000;
            scope.units = 'KB/s';
        }
    };
}]);

linuxDash.directive('downloadTransferRateChart', ['server', function(server) {
    return {
        restrict: 'E',
        scope: {},
        templateUrl: 'templates/modules/download-transfer-rate.html',
        link: function (scope) {
            scope.delay = 2000;
            scope.units = 'KB/s';
        }
    };
}]);

/////////////// Table Data Modules ////////////////////
var simpleTableModules = [
    {
        name: 'machineInfo',
        template: '<key-value-list heading="General Info." module-name="general_info"></key-value-list>'
    },
    {
        name: 'ipAddresses',
        template: '<table-data heading="IP Addresses" module-name="ip_addresses"></table-data>'
    },
    {
        name: 'ramIntensiveProcesses',
        template: '<table-data heading="RAM Intensive Processes" module-name="ram_intensive_processes"></table-data>'
    },
    {
        name: 'cpuIntensiveProcesses',
        template: '<table-data heading="CPU Intensive Processes" module-name="cpu_intensive_processes"></table-data>'
    },
    {
        name: 'networkConnections',
        template: '<table-data heading="Network Connections" module-name="network_connections"></table-data>'
    },
    {
        name: 'serverAccounts',
        template: '<table-data heading="Accounts" module-name="user_accounts"></table-data>'
    },
    {
        name: 'loggedInAccounts',
        template: '<table-data heading="Logged In Accounts" module-name="logged_in_users"></table-data>'
    },
    {
        name: 'recentLogins',
        template: '<table-data heading="Recent Logins" module-name="recent_account_logins"></table-data>'
    },
    {
        name: 'arpCacheTable',
        template: '<table-data heading="ARP Cache Table" module-name="arp_cache"></table-data>'
    },
    {
        name: 'commonApplications',
        template: '<table-data heading="Common Applications" module-name="common_applications"></table-data>'
    },
    {
        name: 'pingSpeeds',
        template: '<table-data heading="Ping Speeds" module-name="ping"></table-data>'
    },
    {
        name: 'bandwidth',
        template: '<table-data heading="Bandwidth" module-name="bandwidth"></table-data>'
    },
    {
        name: 'swapUsage',
        template: '<table-data heading="Swap Usage" module-name="swap"></table-data>'
    },
    {
        name: 'internetSpeed',
        template: '<key-value-list heading="Internet Speed" module-name="internet_speed"></key-value-list>'
    },
    {
        name: 'memcached',
        template: '<key-value-list heading="Memcached" module-name="memcached"></key-value-list>'
    },
    {
        name: 'redis',
        template: '<key-value-list heading="Redis" module-name="redis"></key-value-list>'
    },
    {
        name: 'memoryInfo',
        template: '<key-value-list heading="Memory Info" module-name="memory_info"></key-value-list>'
    },
    {
        name: 'cpuInfo',
        template: '<key-value-list heading="CPU Info" module-name="cpu_info"></key-value-list>'
    },
    {
        name: 'ioStats',
        template: '<table-data heading="IO Stats" module-name="io_stats"></table-data>'
    },
    {
        name: 'scheduledCrons',
        template: '<table-data heading="Scheduled Cron Jobs" module-name="scheduled_crons"></table-data>'
    },
    {
        name: 'cronHistory',
        template: '<table-data heading="Cron Job History" module-name="cron_history"></table-data>'
    },
];

simpleTableModules.forEach(function (module, key) {

    linuxDash.directive(module.name, ['server', function(server) {
        var moduleDirective = {
            restrict: 'E',
            scope: {},
        };

        if (module.templateUrl) {
            moduleDirective['templateUrl'] = 'templates/modules/' + module.templateUrl
        }

        if (module.template) {
            moduleDirective['template'] = module.template;
        }

        return moduleDirective;
    }]);

});
<|endoftext|>


<commit_before>import { format } from 'url'
import Promise from 'bluebird'
import URL from 'url'
import querystring from 'querystring'
import { EventEmitter } from 'events'
import zlib from 'zlib'

Promise.promisifyAll(zlib)
import fetch from 'node-fetch'

export class RawAPI extends EventEmitter {
  constructor(opts={}){
    super()
    this.setServer(opts)
    let self = this
    this.raw = {
      version(){
        return self.req('GET','/api/version')
      },
      authmod(){
        if(this.opts.url.match(/screeps\.com/))
          return Promise.resolve({ name: 'official' })
        return self.req('GET','/authmod')
      },
      history(room,tick){
        tick = Math.round(tick/20)*20
        return self.req('GET','/room-history/${room}/${tick}.json')
      },
      auth: {
        signin(email,password){
          return self.req('POST','/api/auth/signin', { email,password })
        },
        steamTicket(ticket,useNativeAuth=false){
          return self.req('POST','/api/auth/steam-ticket', { ticket, useNativeAuth })
        },
        me(){
          return self.req('GET','/api/auth/me')
        },
      },
      register: {
        checkEmail(email){
          return self.req('GET','/api/register/check-email', { email })
        },
        checkUsername(username){
          return self.req('GET','/api/register/check-username', { username })
        },
        setUsername(username){
          return self.req('POST','/api/register/set-username', { username })
        },
        submit(username,email,password,modules){
          return self.req('POST','/api/register/submit', { username, email, password, modules })
        }
      },
      userMessages: {
        list(respondent){
          return self.req('GET','/api/user-messages/list', { respondent })
        },
        index(){
          return self.req('GET','/api/user-messages/index')
        },
        unreadCount(){
          return self.req('GET','/api/user-messages/unread-count')
        },
        send(respondent,text){
          return self.req('POST','/api/user-messages/send', { respondent, text })
        },
        markRead(id){
          return self.req('POST','/api/user-messages/mark-read', { id })
        },
      },
      game: {
        mapStats(rooms,statName){
          return self.req('POST','/api/game/map-stats', { rooms, statName })
        },
        genUniqueObjectName(type){
          return self.req('POST','/api/game/gen-unique-object-name', { type })
        },
        checkUniqueObjectName(type,name){
          return self.req('POST','/api/game/check-unique-object-name', { type, name })
        },
        placeSpawn(room,x,y,name){
          return self.req('POST','/api/game/place-spawn', { name, room, x, y })
        },
        createFlag(room,x,y,name,color=1,secondaryColor=1){
          return self.req('POST','/api/game/create-flag', { name, room, x, y, color, secondaryColor })
        },
        genUniqueFlagName(){
          return self.req('POST','/api/game/gen-unique-flag-name')
        },
        checkUniqueFlagName(name){
          return self.req('POST','/api/game/check-unique-flag-name', { name })
        },
        changeFlagColor(color=1,secondaryColor=1){
          return self.req('POST','/api/game/change-flag-color', { color, secondaryColor })
        },
        removeFlag(room,name){
          return self.req('POST','/api/game/remove-flag',{ name, room })
        },
        addObjectIntent(room,name,intent){
          return self.req('POST','/api/game/add-object-intent', { room, name, intent })
        },
        createConstruction(room,x,y,structureType,name){
          return self.req('POST','/api/game/create-construction', { room, x, y, structureType, name })
        },
        setNotifyWhenAttacked(_id, enabled=true){
          return self.req('POST','/api/game/set-notify-when-attacked', { _id, enabled })
        },
        createInvader(room,x,y,size,type,boosted=false){
          return self.req('POST','/api/game/create-invader', { room, x, y, size, type, boosted })
        },
        removeInvader(_id){
          return self.req('POST','/api/game/remove-invader',{ _id })
        },
        time(){
          return self.req('GET','/api/game/time')
        },
        worldSize(){
          return self.req('GET','/api/game/world-size')
        },
        roomTerrain(room,encoded=1){
          return self.req('GET','/api/game/room-terrain', { room, encoded })
        },
        roomStatus(room){
          return self.req('GET','/api/game/room-status', { room })
        },
        roomOverview(room,interval=8){
          return self.req('GET','/api/game/room-overview', { room, interval })
        },
        market: {
          ordersIndex(){
            return self.req('GET','/api/game/market/orders-index')
          },
          myOrders(){
            return self.req('GET','/api/game/market/my-orders')
          },
          orders(resourceType){
            return self.req('GET','/api/game/market/orders', { resourceType })
          },
          stats(resourceType){
            return self.req('GET','/api/game/market/stats', { resourceType })
          }
        },
      },
      leaderboard: {
        list(){
          return self.req('GET','/api/leaderboard/list')
        },
        find(username,mode='world',season=''){
          return self.req('GET','/api/leaderboard/find', { season, mode, username })
        },
        seasons(){
          return self.req('GET','/api/leaderboard/seasons')
        }
      },
      user:{
        badge(badge){
          return self.req('POST','/api/user/badge', { badge })
        },
        respawn(){
          return self.req('POST','/api/user/respawn')
        },
        setActiveBranch(branch,activeName){
          return self.req('POST','/api/user/set-active-branch', { branch, activeName })
        },
        cloneBranch(branch,newName,defaultModules){
          return self.req('POST','/api/user/clone-branch', { branch, newName, defaultModules })
        },
        deleteBranch(branch){
          return self.req('POST','/api/user/delete-branch', { branch })
        },
        notifyPrefs(prefs){
          // disabled,disabledOnMessages,sendOnline,interval,errorsInterval
          return self.req('POST','/api/user/notify-prefs', prefs)
        },
        tutorialDone(){
          return self.req('POST','/api/user/tutorial-done')
        },
        email(email){
          return self.req('POST','/api/user/email', { email })
        },
        worldStartRoom(){
          return self.req('GET','/api/user/world-start-room')
        },
        worldStatus(){
          return self.req('GET','/api/user/world-status')
        },
        branches(){
          return self.req('GET','/api/user/branches')
        },
        code: {
          get(branch){
            return self.req('GET','/api/user/code', { branch })
          },
          set(branch,modules,_hash){
            if(!_hash) _hash = Date.now()
            return self.req('POST','/api/user/code', { branch, modules, _hash })
          }
        },
        respawnProhibitedRooms(){
          return self.req('GET','/api/user/respawn-prohibited-rooms')
        },
        memory:{
          get(path){
            return self.req('GET','/api/user/memory', { path })
          },
          set(path,value){
            return self.req('POST','/api/user/memory', { path, value })
          },
          segment:{
            get(segment){
              return self.req('GET','/api/user/memory-segment', { segment })
            },
            set(segment,data){
              return self.req('POST','/api/user/memory-segment', { segment, data })
            },
          }
        },
        find(username){
          return self.req('GET','/api/user/find', { username })
        },
        findById(id){
          return self.req('GET','/api/user/find', { id })
        },
        stats(interval){
          return self.req('GET','/api/user/stats', { interval })
        },
        rooms(id){
          return self.req('GET','/api/user/rooms', { id })
        },
        overview(interval,statName){
          return self.req('GET','/api/user/overview', { interval, statName })
        },
        moneyHistory(page=0){
          return self.req('GET','/api/user/money-history', { page })
        },
        console(expression){
          return self.req('POST','/api/user/console', { expression })
        },
      }
    }
  }
  setServer(opts){
    if(!this.opts) {
      this.opts = {}
    }
    Object.assign(this.opts,opts)
    if(!opts.url){
      this.opts.pathname = this.opts.pathname || this.opts.path
      this.opts.url = format(this.opts)
    }
  }
  async auth(email,password,opts={}){
    this.setServer(opts)
    if(email && password){
      Object.assign(this.opts,{ email, password })
    }
    let res = await this.raw.auth.signin(email,password)
    this.emit('token',res.token)
    this.emit('auth')
    this.__authed = true
    return res
  }
  async req(method,path,body={}) {
    let opts = {
      method,
      headers: {
        'X-Token':this.token,
        'X-Username':this.token
      },
    }
    let url = URL.resolve(this.opts.url,path)
    if(method == 'GET') {
      url += '?' + querystring.stringify(body)
    }
    if(method == 'POST'){
      opts.headers['content-type'] = 'application/json'
      opts.body = JSON.stringify(body)
    }
    let res = await fetch(url,opts)
    if(res.status == 401){
      if(this.__authed){
        this.__authed = false
        await this.auth(this.opts.email,this.opts.password)
      }else{
        throw new Error('Not Authorized')
      }
    }
    let token = res.headers.get('x-token')
    if(token){
      this.emit('token',token)
    }
    this.emit('response',res)
    if (!res.ok){
      throw new Error(await res.text())
    }
    res = await res.json()
    if (typeof res.data == 'string' && res.data.slice(0,3) == 'gz:') {
      if(this.opts.url.match(/screeps\.com/))
        res.data = await this.gz(res.data)
      else
        res.data = await this.inflate(res.data)
    }
    return res
  }
  async gz(data) {
    let buf = new Buffer(data.slice(3), 'base64')
    let ret = await zlib.gunzipAsync(buf)
    return JSON.parse(ret.toString())
  }
  async inflate(data) {
    let buf = new Buffer(data.slice(3), 'base64')
    let ret = await zlib.inflateAsync(buf)
    return JSON.parse(ret.toString())
  }
}
<commit_msg>fixes an error in authmod() where screeps URL was never checked.<commit_after>import { format } from 'url'
import Promise from 'bluebird'
import URL from 'url'
import querystring from 'querystring'
import { EventEmitter } from 'events'
import zlib from 'zlib'

Promise.promisifyAll(zlib)
import fetch from 'node-fetch'

export class RawAPI extends EventEmitter {
  constructor(opts={}){
    super()
    this.setServer(opts)
    let self = this
    this.raw = {
      version(){
        return self.req('GET','/api/version')
      },
      authmod(){
        if(self.opts.url.match(/screeps\.com/))
          return Promise.resolve({ name: 'official' })
        return self.req('GET','/authmod')
      },
      history(room,tick){
        tick = Math.round(tick/20)*20
        return self.req('GET','/room-history/${room}/${tick}.json')
      },
      auth: {
        signin(email,password){
          return self.req('POST','/api/auth/signin', { email,password })
        },
        steamTicket(ticket,useNativeAuth=false){
          return self.req('POST','/api/auth/steam-ticket', { ticket, useNativeAuth })
        },
        me(){
          return self.req('GET','/api/auth/me')
        },
      },
      register: {
        checkEmail(email){
          return self.req('GET','/api/register/check-email', { email })
        },
        checkUsername(username){
          return self.req('GET','/api/register/check-username', { username })
        },
        setUsername(username){
          return self.req('POST','/api/register/set-username', { username })
        },
        submit(username,email,password,modules){
          return self.req('POST','/api/register/submit', { username, email, password, modules })
        }
      },
      userMessages: {
        list(respondent){
          return self.req('GET','/api/user-messages/list', { respondent })
        },
        index(){
          return self.req('GET','/api/user-messages/index')
        },
        unreadCount(){
          return self.req('GET','/api/user-messages/unread-count')
        },
        send(respondent,text){
          return self.req('POST','/api/user-messages/send', { respondent, text })
        },
        markRead(id){
          return self.req('POST','/api/user-messages/mark-read', { id })
        },
      },
      game: {
        mapStats(rooms,statName){
          return self.req('POST','/api/game/map-stats', { rooms, statName })
        },
        genUniqueObjectName(type){
          return self.req('POST','/api/game/gen-unique-object-name', { type })
        },
        checkUniqueObjectName(type,name){
          return self.req('POST','/api/game/check-unique-object-name', { type, name })
        },
        placeSpawn(room,x,y,name){
          return self.req('POST','/api/game/place-spawn', { name, room, x, y })
        },
        createFlag(room,x,y,name,color=1,secondaryColor=1){
          return self.req('POST','/api/game/create-flag', { name, room, x, y, color, secondaryColor })
        },
        genUniqueFlagName(){
          return self.req('POST','/api/game/gen-unique-flag-name')
        },
        checkUniqueFlagName(name){
          return self.req('POST','/api/game/check-unique-flag-name', { name })
        },
        changeFlagColor(color=1,secondaryColor=1){
          return self.req('POST','/api/game/change-flag-color', { color, secondaryColor })
        },
        removeFlag(room,name){
          return self.req('POST','/api/game/remove-flag',{ name, room })
        },
        addObjectIntent(room,name,intent){
          return self.req('POST','/api/game/add-object-intent', { room, name, intent })
        },
        createConstruction(room,x,y,structureType,name){
          return self.req('POST','/api/game/create-construction', { room, x, y, structureType, name })
        },
        setNotifyWhenAttacked(_id, enabled=true){
          return self.req('POST','/api/game/set-notify-when-attacked', { _id, enabled })
        },
        createInvader(room,x,y,size,type,boosted=false){
          return self.req('POST','/api/game/create-invader', { room, x, y, size, type, boosted })
        },
        removeInvader(_id){
          return self.req('POST','/api/game/remove-invader',{ _id })
        },
        time(){
          return self.req('GET','/api/game/time')
        },
        worldSize(){
          return self.req('GET','/api/game/world-size')
        },
        roomTerrain(room,encoded=1){
          return self.req('GET','/api/game/room-terrain', { room, encoded })
        },
        roomStatus(room){
          return self.req('GET','/api/game/room-status', { room })
        },
        roomOverview(room,interval=8){
          return self.req('GET','/api/game/room-overview', { room, interval })
        },
        market: {
          ordersIndex(){
            return self.req('GET','/api/game/market/orders-index')
          },
          myOrders(){
            return self.req('GET','/api/game/market/my-orders')
          },
          orders(resourceType){
            return self.req('GET','/api/game/market/orders', { resourceType })
          },
          stats(resourceType){
            return self.req('GET','/api/game/market/stats', { resourceType })
          }
        },
      },
      leaderboard: {
        list(){
          return self.req('GET','/api/leaderboard/list')
        },
        find(username,mode='world',season=''){
          return self.req('GET','/api/leaderboard/find', { season, mode, username })
        },
        seasons(){
          return self.req('GET','/api/leaderboard/seasons')
        }
      },
      user:{
        badge(badge){
          return self.req('POST','/api/user/badge', { badge })
        },
        respawn(){
          return self.req('POST','/api/user/respawn')
        },
        setActiveBranch(branch,activeName){
          return self.req('POST','/api/user/set-active-branch', { branch, activeName })
        },
        cloneBranch(branch,newName,defaultModules){
          return self.req('POST','/api/user/clone-branch', { branch, newName, defaultModules })
        },
        deleteBranch(branch){
          return self.req('POST','/api/user/delete-branch', { branch })
        },
        notifyPrefs(prefs){
          // disabled,disabledOnMessages,sendOnline,interval,errorsInterval
          return self.req('POST','/api/user/notify-prefs', prefs)
        },
        tutorialDone(){
          return self.req('POST','/api/user/tutorial-done')
        },
        email(email){
          return self.req('POST','/api/user/email', { email })
        },
        worldStartRoom(){
          return self.req('GET','/api/user/world-start-room')
        },
        worldStatus(){
          return self.req('GET','/api/user/world-status')
        },
        branches(){
          return self.req('GET','/api/user/branches')
        },
        code: {
          get(branch){
            return self.req('GET','/api/user/code', { branch })
          },
          set(branch,modules,_hash){
            if(!_hash) _hash = Date.now()
            return self.req('POST','/api/user/code', { branch, modules, _hash })
          }
        },
        respawnProhibitedRooms(){
          return self.req('GET','/api/user/respawn-prohibited-rooms')
        },
        memory:{
          get(path){
            return self.req('GET','/api/user/memory', { path })
          },
          set(path,value){
            return self.req('POST','/api/user/memory', { path, value })
          },
          segment:{
            get(segment){
              return self.req('GET','/api/user/memory-segment', { segment })
            },
            set(segment,data){
              return self.req('POST','/api/user/memory-segment', { segment, data })
            },
          }
        },
        find(username){
          return self.req('GET','/api/user/find', { username })
        },
        findById(id){
          return self.req('GET','/api/user/find', { id })
        },
        stats(interval){
          return self.req('GET','/api/user/stats', { interval })
        },
        rooms(id){
          return self.req('GET','/api/user/rooms', { id })
        },
        overview(interval,statName){
          return self.req('GET','/api/user/overview', { interval, statName })
        },
        moneyHistory(page=0){
          return self.req('GET','/api/user/money-history', { page })
        },
        console(expression){
          return self.req('POST','/api/user/console', { expression })
        },
      }
    }
  }
  setServer(opts){
    if(!this.opts) {
      this.opts = {}
    }
    Object.assign(this.opts,opts)
    if(!opts.url){
      this.opts.pathname = this.opts.pathname || this.opts.path
      this.opts.url = format(this.opts)
    }
  }
  async auth(email,password,opts={}){
    this.setServer(opts)
    if(email && password){
      Object.assign(this.opts,{ email, password })
    }
    let res = await this.raw.auth.signin(email,password)
    this.emit('token',res.token)
    this.emit('auth')
    this.__authed = true
    return res
  }
  async req(method,path,body={}) {
    let opts = {
      method,
      headers: {
        'X-Token':this.token,
        'X-Username':this.token
      },
    }
    let url = URL.resolve(this.opts.url,path)
    if(method == 'GET') {
      url += '?' + querystring.stringify(body)
    }
    if(method == 'POST'){
      opts.headers['content-type'] = 'application/json'
      opts.body = JSON.stringify(body)
    }
    let res = await fetch(url,opts)
    if(res.status == 401){
      if(this.__authed){
        this.__authed = false
        await this.auth(this.opts.email,this.opts.password)
      }else{
        throw new Error('Not Authorized')
      }
    }
    let token = res.headers.get('x-token')
    if(token){
      this.emit('token',token)
    }
    this.emit('response',res)
    if (!res.ok){
      throw new Error(await res.text())
    }
    res = await res.json()
    if (typeof res.data == 'string' && res.data.slice(0,3) == 'gz:') {
      if(this.opts.url.match(/screeps\.com/))
        res.data = await this.gz(res.data)
      else
        res.data = await this.inflate(res.data)
    }
    return res
  }
  async gz(data) {
    let buf = new Buffer(data.slice(3), 'base64')
    let ret = await zlib.gunzipAsync(buf)
    return JSON.parse(ret.toString())
  }
  async inflate(data) {
    let buf = new Buffer(data.slice(3), 'base64')
    let ret = await zlib.inflateAsync(buf)
    return JSON.parse(ret.toString())
  }
}
<|endoftext|>


<commit_before>(function($, $$) {

var _ = Mavo.ImplicitCollection = $.Class({
	extends: Mavo.Node,
	nodeType: "ImplicitCollection",
	constructor: function (element, mavo, o) {
		/*
		 * Create the template, remove it from the DOM and store it
		 */
		this.children = [];
		this.liveData = new Mavo.Data(this, []);

		this.add(element);
		this.postInit();

		Mavo.hooks.run("implicit-collection-init-end", this);
	},

	get length() {
		return this.children.length;
	},

	getData: function(o = {}) {
		var env = {
			context: this,
			options: o,
			data: []
		};

		this.children.forEach(node => {
			if (!node.isDataNull()) {
				env.data.push(node.getData(o));
			}
		});

		if (this.data) {
			// Maybe rendered data had more items than we could show? Add it back.
			var rendered = Mavo.toArray(Mavo.subset(this.data, this.inPath));

			if (rendered.length > env.data.length) {
				env.data = env.data.concat(rendered.slice(env.data.length));
			}
		}

		if (Array.isArray(env.data) && env.data.length <= 1) {
			env.data = env.data.length === 1? env.data[0] : null;
		}

		env.data = Mavo.subset(this.data, this.inPath, env.data);

		Mavo.hooks.run("node-getdata-end", env);

		return env.data;
	},

	/**
	 * Add a new item to this collection
	 * @param item Element or Mavo object for the new item
	 */
	add: function(element) {
		var item = Mavo.Node.create(element, this.mavo, {
			collection: this,
			template: this.template && this.template.children[this.length] || null,
			property: this.property,
			type: this.type
		});

		item.index = this.length;
		this.children.push(item);

		return item;
	},

	edit: function(o = {}) {
		if (this.super.edit.call(this) === false) {
			return false;
		}

		// Edit items
		return Promise.all(this.children.map(item => item.edit(o)));
	},

	propagated: ["save"],

	dataRender: function(data, o = {}) {
		if (data !== undefined) {
			data = data === null? [] : Mavo.toArray(data).filter(i => i !== null);
			var changed = data.length !== this.liveData.length;

			this.children.forEach((item, i) => changed = item.render(data && data[i], o) || changed);
		}

		this.liveData.update();
	}
});

})(Bliss, Bliss.$);
<commit_msg>Update ImplicitCollection.liveData after adding node (#399)<commit_after>(function($, $$) {

var _ = Mavo.ImplicitCollection = $.Class({
	extends: Mavo.Node,
	nodeType: "ImplicitCollection",
	constructor: function (element, mavo, o) {
		/*
		 * Create the template, remove it from the DOM and store it
		 */
		this.children = [];
		this.liveData = new Mavo.Data(this, []);

		this.add(element);
		this.postInit();

		Mavo.hooks.run("implicit-collection-init-end", this);
	},

	get length() {
		return this.children.length;
	},

	getData: function(o = {}) {
		var env = {
			context: this,
			options: o,
			data: []
		};

		this.children.forEach(node => {
			if (!node.isDataNull()) {
				env.data.push(node.getData(o));
			}
		});

		if (this.data) {
			// Maybe rendered data had more items than we could show? Add it back.
			var rendered = Mavo.toArray(Mavo.subset(this.data, this.inPath));

			if (rendered.length > env.data.length) {
				env.data = env.data.concat(rendered.slice(env.data.length));
			}
		}

		if (Array.isArray(env.data) && env.data.length <= 1) {
			env.data = env.data.length === 1? env.data[0] : null;
		}

		env.data = Mavo.subset(this.data, this.inPath, env.data);

		Mavo.hooks.run("node-getdata-end", env);

		return env.data;
	},

	/**
	 * Add a new item to this collection
	 * @param item Element or Mavo object for the new item
	 */
	add: function(element) {
		var item = Mavo.Node.create(element, this.mavo, {
			collection: this,
			template: this.template && this.template.children[this.length] || null,
			property: this.property,
			type: this.type
		});

		item.index = this.length;
		this.children.push(item);

		// item may have tried to propagate updates to us when we created it,
		// but that wouldn't have worked since item was not yet in
		// this.children, so we need to update manually.
		this.liveData.update();

		return item;
	},

	edit: function(o = {}) {
		if (this.super.edit.call(this) === false) {
			return false;
		}

		// Edit items
		return Promise.all(this.children.map(item => item.edit(o)));
	},

	propagated: ["save"],

	dataRender: function(data, o = {}) {
		if (data !== undefined) {
			data = data === null? [] : Mavo.toArray(data).filter(i => i !== null);
			var changed = data.length !== this.liveData.length;

			this.children.forEach((item, i) => changed = item.render(data && data[i], o) || changed);
		}

		this.liveData.update();
	}
});

})(Bliss, Bliss.$);
<|endoftext|>


<commit_before>/**
 * vufind.typeahead.js 0.2
 * ~ @crhallberg
 */
(function ( $ ) {

  $.fn.autocomplete = function(settings) {

    var options = $.extend( {}, $.fn.autocomplete.options, settings );

    function show() {
      $.fn.autocomplete.element.removeClass(options.hidingClass);
    }
    function hide() {
      $.fn.autocomplete.element.addClass(options.hidingClass);
    }

    function populate(value, input, eventType) {
      input.val(value);
      hide();
      if (typeof options.onselection !== 'undefined') {
        options.onselection(value, input, eventType);
      }
    }

    function createList(data, input) {
      var shell = $('
');
      for (var i=0, len=Math.min(options.maxResults, data.length); i<len; i++) {
        if (typeof data[i] === 'string') {
          data[i] = {val: data[i]};
        }
        var item = typeof data[i].href === 'undefined'
          ? $('
').attr('data-value', data[i].val)
                      .html(data[i].val)
                      .addClass('item')
          : $('').attr('href', data[i].href)
                    .attr('data-value', data[i].val)
                    .html(data[i].val)
                    .addClass('item')
        if (typeof data[i].description !== 'undefined') {
          item.append($('').text(data[i].description));
        }
        shell.append(item);
      }
      $.fn.autocomplete.element.html(shell.html());
      $.fn.autocomplete.element.find('.item').mousedown(function() {
        populate($(this).attr('data-value'), input, {mouse: true})
      });
      align(input, $.fn.autocomplete.element);
    }

    $.fn.autocomplete.cache = {};
    function search(input, element) {
      if (xhr) xhr.abort();
      if (input.val().length >= options.minLength) {
        element.html(''+options.loadingString+'');
        show();
        align(input, $.fn.autocomplete.element);
        var term = input.val();
        var cid = parseInt(input.data('cache-id'));
        if (options.cache && typeof $.fn.autocomplete.cache[cid][term] !== "undefined") {
          if ($.fn.autocomplete.cache[cid][term].length === 0) {
            hide();
          } else {
            createList($.fn.autocomplete.cache[cid][term], input, element);
          }
        } else if (typeof options.handler !== "undefined") {
          options.handler(input.val(), function(data) {
            $.fn.autocomplete.cache[cid][term] = data;
            if (data.length === 0) {
              hide();
            } else {
              createList(data, input, element);
            }
          });
        } else {
          console.error('handler function not provided for autocomplete');
        }
        input.data('selected', -1);
      } else {
        hide();
      }
    }

    function align(input, element) {
      var offset = input[0].getBoundingClientRect();
      element.css({
        position: 'absolute',
        top: offset.top + offset.height,
        left: offset.left,
        maxWidth: offset.width * 2,
        minWidth: offset.width,
        zIndex: 50
      });
    }

    function setup(input, element) {
      if (typeof element === 'undefined') {
        element = $('
')
          .addClass('autocomplete-results hidden')
          .text(''+options.loadingString+'');
        align(input, element);
        $('body').append(element);
      }

      input.data('selected', -1);

      if (options.cache) {
        var cid = Math.floor(Math.random()*1000);
        input.data('cache-id', cid);
        $.fn.autocomplete.cache[cid] = {};
      }

      input.blur(function(e) {
        if (e.target.acitem) {
          setTimeout(hide, 10);
        } else {
          hide();
        }
      });
      input.click(function() {
        search(input, element);
      });
      input.focus(function() {
        search(input, element);
      });
      input.keyup(function(event) {
        if (event.ctrlKey) {
          return;
        }
        switch (event.which) {
          case 37:
          case 38:
          case 39:
          case 9:
          case 13: {
            return;
          }
          case 40: {
            if ($(this).data('selected') === -1) {
              search(input, element)
              return;
            }
          }
          default: {
            if (
              event.which === 8  ||   // backspace
              event.which === 46 ||   // delete
              (event.which >= 48 &&   // letters
               event.which <= 90) ||
              (event.which >= 96 &&   // numpad
               event.which <= 111)
            ) {
              search(input, element);
            }
          }
        }
      });
      input.keydown(function(event) {
        var element = $.fn.autocomplete.element;
        var position = $(this).data('selected');
        switch (event.which) {
          // arrow keys through items
          case 38: {
            event.preventDefault();
            element.find('.item.selected').removeClass('selected');
            if (position > 0) {
              position--;
              element.find('.item:eq('+position+')').addClass('selected');
              $(this).data('selected', position);
            } else {
              $(this).data('selected', -1);
            }
            break;
          }
          case 40: {
            show();
            event.preventDefault();
            if (position < options.maxResults) {
              position++;
              element.find('.item.selected').removeClass('selected');
              element.find('.item:eq('+position+')').addClass('selected');
              $(this).data('selected', position);
            }
            break;
          }
          // enter to nav or populate
          case 9:
          case 13: {
            var selected = element.find('.item.selected');
            if (selected.length > 0) {
              event.preventDefault();
              if (event.which === 13 && selected.attr('href')) {
                location.assign(selected.attr('href'));
              } else {
                populate(selected.attr('data-value'), $(this), element, {key: true});
                element.find('.item.selected').removeClass('selected');
              }
            }
            break;
          }
          // hide on escape
          case 27: {
            hide(element);
            $(this).data('selected', -1);
            break;
          }
        }
      });

      if (
        typeof options.data    === "undefined" &&
        typeof options.handler === "undefined" &&
        typeof options.preload === "undefined" &&
        typeof options.remote  === "undefined"
      ) {
        return input;
      }

      return element;
    }

    return this.each(function() {

      var input = $(this);

      if (typeof settings === "string") {
        if (settings === "show") {
          show();
          align(input, $.fn.autocomplete.element);
        } else if (settings === "hide") {
          hide();
        } else if (settings === "clear cache" && options.cache) {
          var cid = parseInt(input.data('cache-id'));
          $.fn.autocomplete.cache[cid] = {};
        }
        return input;
      } else {
        if (!$.fn.autocomplete.element) {
          $.fn.autocomplete.element = setup(input);
        } else {
          setup(input, $.fn.autocomplete.element);
        }
      }

      return input;

    });
  };

  $.fn.autocomplete.element = false;
  $.fn.autocomplete.options = {
    ajaxDelay: 200,
    cache: true,
    hidingClass: 'hidden',
    highlight: true,
    loadingString: 'Loading...',
    maxResults: 20,
    minLength: 3,
    minResults: 1
  };

  var xhr = false;
  var timer = false;
  $.fn.autocomplete.ajax = function(ops) {
    if (timer) clearTimeout(timer);
    if (xhr) xhr.abort();
    timer = setTimeout(
      function() { xhr = $.ajax(ops); },
      $.fn.autocomplete.options.ajaxDelay
    );
  }

}( jQuery ));
<commit_msg>v0.3 - highlighting working.<commit_after>/**
 * vufind.typeahead.js 0.3
 * ~ @crhallberg
 */
(function ( $ ) {

  $.fn.autocomplete = function(settings) {

    var options = $.extend( {}, $.fn.autocomplete.options, settings );

    function show() {
      $.fn.autocomplete.element.removeClass(options.hidingClass);
    }
    function hide() {
      $.fn.autocomplete.element.addClass(options.hidingClass);
    }

    function populate(value, input, eventType) {
      input.val(value);
      hide();
      if (typeof options.onselection !== 'undefined') {
        options.onselection(value, input, eventType);
      }
    }

    function createList(data, input) {
      var shell = $('
');
      for (var i=0, len=Math.min(options.maxResults, data.length); i<len; i++) {
        if (typeof data[i] === 'string') {
          data[i] = {val: data[i]};
        }
        var content = data[i].val;
        if (options.highlight) {
          var regex = new RegExp('('+input.val()+')', 'ig');
          content = content.replace(regex, '$1');
        }
        var item = typeof data[i].href === 'undefined'
          ? $('
').addClass('item')
                       .attr('data-value', data[i].val)
                       .html(content)
          : $('').attr('href', data[i].href)
                     .attr('data-value', data[i].val)
                     .html(content)
        if (typeof data[i].description !== 'undefined') {
          item.append($('').text(data[i].description));
        }
        shell.append(item);
      }
      $.fn.autocomplete.element.html(shell.html());
      $.fn.autocomplete.element.find('.item').mousedown(function() {
        populate($(this).attr('data-value'), input, {mouse: true})
      });
      align(input, $.fn.autocomplete.element);
    }

    $.fn.autocomplete.cache = {};
    function search(input, element) {
      if (xhr) xhr.abort();
      if (input.val().length >= options.minLength) {
        element.html(''+options.loadingString+'');
        show();
        align(input, $.fn.autocomplete.element);
        var term = input.val();
        var cid = parseInt(input.data('cache-id'));
        if (options.cache && typeof $.fn.autocomplete.cache[cid][term] !== "undefined") {
          if ($.fn.autocomplete.cache[cid][term].length === 0) {
            hide();
          } else {
            createList($.fn.autocomplete.cache[cid][term], input, element);
          }
        } else if (typeof options.handler !== "undefined") {
          options.handler(input.val(), function(data) {
            $.fn.autocomplete.cache[cid][term] = data;
            if (data.length === 0) {
              hide();
            } else {
              createList(data, input, element);
            }
          });
        } else {
          console.error('handler function not provided for autocomplete');
        }
        input.data('selected', -1);
      } else {
        hide();
      }
    }

    function align(input, element) {
      var offset = input[0].getBoundingClientRect();
      element.css({
        position: 'absolute',
        top: offset.top + offset.height,
        left: offset.left,
        maxWidth: offset.width * 2,
        minWidth: offset.width,
        zIndex: 50
      });
    }

    function setup(input, element) {
      if (typeof element === 'undefined') {
        element = $('
')
          .addClass('autocomplete-results hidden')
          .text(''+options.loadingString+'');
        align(input, element);
        $('body').append(element);
      }

      input.data('selected', -1);

      if (options.cache) {
        var cid = Math.floor(Math.random()*1000);
        input.data('cache-id', cid);
        $.fn.autocomplete.cache[cid] = {};
      }

      input.blur(function(e) {
        if (e.target.acitem) {
          setTimeout(hide, 10);
        } else {
          hide();
        }
      });
      input.click(function() {
        search(input, element);
      });
      input.focus(function() {
        search(input, element);
      });
      input.keyup(function(event) {
        if (event.ctrlKey) {
          return;
        }
        switch (event.which) {
          case 37:
          case 38:
          case 39:
          case 9:
          case 13: {
            return;
          }
          case 40: {
            if ($(this).data('selected') === -1) {
              search(input, element)
              return;
            }
          }
          default: {
            if (
              event.which === 8  ||   // backspace
              event.which === 46 ||   // delete
              (event.which >= 48 &&   // letters
               event.which <= 90) ||
              (event.which >= 96 &&   // numpad
               event.which <= 111)
            ) {
              search(input, element);
            }
          }
        }
      });
      input.keydown(function(event) {
        var element = $.fn.autocomplete.element;
        var position = $(this).data('selected');
        switch (event.which) {
          // arrow keys through items
          case 38: {
            event.preventDefault();
            element.find('.item.selected').removeClass('selected');
            if (position > 0) {
              position--;
              element.find('.item:eq('+position+')').addClass('selected');
              $(this).data('selected', position);
            } else {
              $(this).data('selected', -1);
            }
            break;
          }
          case 40: {
            show();
            event.preventDefault();
            if (position < options.maxResults) {
              position++;
              element.find('.item.selected').removeClass('selected');
              element.find('.item:eq('+position+')').addClass('selected');
              $(this).data('selected', position);
            }
            break;
          }
          // enter to nav or populate
          case 9:
          case 13: {
            var selected = element.find('.item.selected');
            if (selected.length > 0) {
              event.preventDefault();
              if (event.which === 13 && selected.attr('href')) {
                location.assign(selected.attr('href'));
              } else {
                populate(selected.attr('data-value'), $(this), element, {key: true});
                element.find('.item.selected').removeClass('selected');
              }
            }
            break;
          }
          // hide on escape
          case 27: {
            hide(element);
            $(this).data('selected', -1);
            break;
          }
        }
      });

      if (
        typeof options.data    === "undefined" &&
        typeof options.handler === "undefined" &&
        typeof options.preload === "undefined" &&
        typeof options.remote  === "undefined"
      ) {
        return input;
      }

      return element;
    }

    return this.each(function() {

      var input = $(this);

      if (typeof settings === "string") {
        if (settings === "show") {
          show();
          align(input, $.fn.autocomplete.element);
        } else if (settings === "hide") {
          hide();
        } else if (settings === "clear cache" && options.cache) {
          var cid = parseInt(input.data('cache-id'));
          $.fn.autocomplete.cache[cid] = {};
        }
        return input;
      } else {
        if (!$.fn.autocomplete.element) {
          $.fn.autocomplete.element = setup(input);
        } else {
          setup(input, $.fn.autocomplete.element);
        }
      }

      return input;

    });
  };

  $.fn.autocomplete.element = false;
  $.fn.autocomplete.options = {
    ajaxDelay: 200,
    cache: true,
    hidingClass: 'hidden',
    highlight: true,
    loadingString: 'Loading...',
    maxResults: 20,
    minLength: 3
  };

  var xhr = false;
  var timer = false;
  $.fn.autocomplete.ajax = function(ops) {
    if (timer) clearTimeout(timer);
    if (xhr) xhr.abort();
    timer = setTimeout(
      function() { xhr = $.ajax(ops); },
      $.fn.autocomplete.options.ajaxDelay
    );
  }

}( jQuery ));
<|endoftext|>