<commit_before>var Tabs = require('hypertabs')
var h = require('hyperscript')
var keyscroll = require('../keyscroll')
var open = require('open-external')

function ancestor (el) {
  if(!el) return
  if(el.tagName !== 'A') return ancestor(el.parentElement)
  return el
}

exports.needs = {
  build_scroller: 'first',
  screen_view: 'first',
  search_box: 'first',
  menu: 'first',
  external_confirm:'first'
}

exports.gives = 'screen_view'

exports.create = function (api) {
  return function (path) {
    if(path !== 'tabs') return

    function setSelected (indexes) {
      var ids = indexes.map(function (index) {
        return tabs.get(index).id
      })
      if(search)
        if(ids.length > 1)
          search.value = 'split('+ids.join(',')+')'
        else
          search.value = ids[0]
    }

    var search
    var tabs = Tabs(setSelected)

    search = api.search_box(function (path, change) {

      if(tabs.has(path)) {
        tabs.select(path)
        return true
      }
      var el = api.screen_view(path)

      if(el) {
        if(!el.title) el.title = path
        el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
        tabs.add(el, change)
  //      localStorage.openTabs = JSON.stringify(tabs.tabs)
        return change
      }
    })

    //reposition hypertabs menu to inside a container...
    tabs.insertBefore(h('div.header.row',
        h('div.header__tabs.row', tabs.firstChild), //tabs
        h('div.header__search.row.end', [search, api.menu()])
    ), tabs.firstChild)
  //  tabs.insertBefore(search, tabs.firstChild.nextSibling)

    var saved = []
  //  try { saved = JSON.parse(localStorage.openTabs) }
  //  catch (_) { }

    if(!saved || saved.length < 3)
      saved = ['/public', '/private', '/notifications', '/data']

    saved.forEach(function (path) {
      var el = api.screen_view(path)
      if(!el) return
      el.id = el.id || path
      if (!el) return
      el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
      if(el) tabs.add(el, false, false)
    })

    tabs.select(0)

    //handle link clicks
    window.onclick = function (ev) {
      var link = ancestor(ev.target)
      if(!link) return
      var path = link.hash.substring(1)

      ev.preventDefault()
      ev.stopPropagation()

      //let the application handle this link
      if (link.getAttribute('href') === '#') return

      //open external links.
      //this ought to be made into something more runcible
      if(link.href && open.isExternal(link.href)) return api.external_confirm(link.href)

      if(tabs.has(path))
        return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey)

      var el = api.screen_view(path)
      if(el) {
        el.id = el.id || path
        el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
        tabs.add(el, !ev.ctrlKey, !!ev.shiftKey)
  //      localStorage.openTabs = JSON.stringify(tabs.tabs)
      }

      return false
    }

    window.addEventListener('keydown', function (ev) {
      if (ev.target.nodeName === 'INPUT' || ev.target.nodeName === 'TEXTAREA')
        return
      switch(ev.keyCode) {

        // scroll through tabs
        case 72: // h
          return tabs.selectRelative(-1)
        case 76: // l
          return tabs.selectRelative(1)

        // scroll through messages
        case 74: // j
          return tabs.get(tabs.selected[0]).scroll(1)
        case 75: // k
          return tabs.get(tabs.selected[0]).scroll(-1)

        // close a tab
        case 88: // x
          if (tabs.selected) {
            var sel = tabs.selected
            var i = sel.reduce(function (a, b) { return Math.min(a, b) })
            tabs.remove(sel)
            tabs.select(Math.max(i-1, 0))
          }
          return

        // activate the search field
        case 191: // /
          if (ev.shiftKey)
            search.activate('?', ev)
          else
            search.activate('/', ev)
          return

        // navigate to a feed
        case 50: // 2
          if (ev.shiftKey)
            search.activate('@', ev)
          return

        // navigate to a channel
        case 51: // 3
          if (ev.shiftKey)
            search.activate('#', ev)
          return

        // navigate to a message
        case 53: // 5
          if (ev.shiftKey)
            search.activate('%', ev)
          return
      }
    })

    // errors tab
    var {
      container: errors,
      content: errorsContent
    } = api.build_scroller()

    // remove loader error handler
    if (window.onError) {
      window.removeEventListener('error', window.onError)
      delete window.onError
    }

    // put errors in a tab
    window.addEventListener('error', function (ev) {
      var err = ev.error || ev
      if(!tabs.has('errors'))
        tabs.add(errors, false)
      var el = h('div.message',
        h('strong', err.message),
        h('pre', err.stack))
      if (errorsContent.firstChild)
        errorsContent.insertBefore(el, errorsContent.firstChild)
      else
        errorsContent.appendChild(el)
    })

    if (process.versions.electron) {
      var {webFrame} = require('electron')

      window.addEventListener('mousewheel', function (ev) {
        var {ctrlKey, deltaY} = ev
        if(ctrlKey){
          var direction = (deltaY / Math.abs(deltaY))
          var currentZoom = webFrame.getZoomLevel()
          webFrame.setZoomLevel(currentZoom - direction)
        }
      })

      window.addEventListener('contextmenu', function (ev) {
        ev.preventDefault()
        var remote = require('electron').remote
        var Menu = remote.Menu
        var MenuItem = remote.MenuItem
        var menu = new Menu()
        menu.append(new MenuItem({
          label: 'Inspect Element',
          click: function () {
            remote.getCurrentWindow().inspectElement(ev.x, ev.y)
          }
        }))
        menu.popup(remote.getCurrentWindow())
      })
    }

    return tabs
  }

}
<commit_msg>refactor crtl+scroll a little<commit_after>const Tabs = require('hypertabs')
const h = require('hyperscript')
const keyscroll = require('../keyscroll')
const open = require('open-external')
const { webFrame, remote } = require('electron')

function ancestor (el) {
  if(!el) return
  if(el.tagName !== 'A') return ancestor(el.parentElement)
  return el
}

exports.needs = {
  build_scroller: 'first',
  screen_view: 'first',
  search_box: 'first',
  menu: 'first',
  external_confirm:'first'
}

exports.gives = 'screen_view'

exports.create = function (api) {
  return function (path) {
    if(path !== 'tabs') return

    function setSelected (indexes) {
      var ids = indexes.map(function (index) {
        return tabs.get(index).id
      })
      if(search)
        if(ids.length > 1)
          search.value = 'split('+ids.join(',')+')'
        else
          search.value = ids[0]
    }

    var search
    var tabs = Tabs(setSelected)

    search = api.search_box(function (path, change) {

      if(tabs.has(path)) {
        tabs.select(path)
        return true
      }
      var el = api.screen_view(path)

      if(el) {
        if(!el.title) el.title = path
        el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
        tabs.add(el, change)
  //      localStorage.openTabs = JSON.stringify(tabs.tabs)
        return change
      }
    })

    //reposition hypertabs menu to inside a container...
    tabs.insertBefore(h('div.header.row',
        h('div.header__tabs.row', tabs.firstChild), //tabs
        h('div.header__search.row.end', [search, api.menu()])
    ), tabs.firstChild)
  //  tabs.insertBefore(search, tabs.firstChild.nextSibling)

    var saved = []
  //  try { saved = JSON.parse(localStorage.openTabs) }
  //  catch (_) { }

    if(!saved || saved.length < 3)
      saved = ['/public', '/private', '/notifications', '/data']

    saved.forEach(function (path) {
      var el = api.screen_view(path)
      if(!el) return
      el.id = el.id || path
      if (!el) return
      el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
      if(el) tabs.add(el, false, false)
    })

    tabs.select(0)

    //handle link clicks
    window.onclick = function (ev) {
      var link = ancestor(ev.target)
      if(!link) return
      var path = link.hash.substring(1)

      ev.preventDefault()
      ev.stopPropagation()

      //let the application handle this link
      if (link.getAttribute('href') === '#') return

      //open external links.
      //this ought to be made into something more runcible
      if(link.href && open.isExternal(link.href)) return api.external_confirm(link.href)

      if(tabs.has(path))
        return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey)

      var el = api.screen_view(path)
      if(el) {
        el.id = el.id || path
        el.scroll = keyscroll(el.querySelector('.Scroller .\\.content'))
        tabs.add(el, !ev.ctrlKey, !!ev.shiftKey)
  //      localStorage.openTabs = JSON.stringify(tabs.tabs)
      }

      return false
    }

    window.addEventListener('keydown', function (ev) {
      if (ev.target.nodeName === 'INPUT' || ev.target.nodeName === 'TEXTAREA')
        return
      switch(ev.keyCode) {

        // scroll through tabs
        case 72: // h
          return tabs.selectRelative(-1)
        case 76: // l
          return tabs.selectRelative(1)

        // scroll through messages
        case 74: // j
          return tabs.get(tabs.selected[0]).scroll(1)
        case 75: // k
          return tabs.get(tabs.selected[0]).scroll(-1)

        // close a tab
        case 88: // x
          if (tabs.selected) {
            var sel = tabs.selected
            var i = sel.reduce(function (a, b) { return Math.min(a, b) })
            tabs.remove(sel)
            tabs.select(Math.max(i-1, 0))
          }
          return

        // activate the search field
        case 191: // /
          if (ev.shiftKey)
            search.activate('?', ev)
          else
            search.activate('/', ev)
          return

        // navigate to a feed
        case 50: // 2
          if (ev.shiftKey)
            search.activate('@', ev)
          return

        // navigate to a channel
        case 51: // 3
          if (ev.shiftKey)
            search.activate('#', ev)
          return

        // navigate to a message
        case 53: // 5
          if (ev.shiftKey)
            search.activate('%', ev)
          return
      }
    })

    // errors tab
    var {
      container: errors,
      content: errorsContent
    } = api.build_scroller()

    // remove loader error handler
    if (window.onError) {
      window.removeEventListener('error', window.onError)
      delete window.onError
    }

    // put errors in a tab
    window.addEventListener('error', ev => {
      const err = ev.error || ev
      if(!tabs.has('errors'))
        tabs.add(errors, false)
      const el = h('div.message', [
        h('strong', err.message),
        h('pre', err.stack)
      ])
      if (errorsContent.firstChild)
        errorsContent.insertBefore(el, errorsContent.firstChild)
      else
        errorsContent.appendChild(el)
    })

    if (process.versions.electron) {

      window.addEventListener('mousewheel', ev => {
        const { ctrlKey, deltaY } = ev
        if (ctrlKey) {
          const direction = (deltaY / Math.abs(deltaY))
          const currentZoom = webFrame.getZoomLevel()
          webFrame.setZoomLevel(currentZoom - direction)
        }
      })

      window.addEventListener('contextmenu', ev => {
        ev.preventDefault()
        const Menu = remote.Menu
        const MenuItem = remote.MenuItem
        const menu = new Menu()
        menu.append(new MenuItem({
          label: 'Inspect Element',
          click: () => {
            remote.getCurrentWindow().inspectElement(ev.x, ev.y)
          }
        }))
        menu.popup(remote.getCurrentWindow())
      })
    }

    return tabs
  }

}
<|endoftext|>


<commit_before>// $Revision$

Ensembl.Panel.VEPResults = Ensembl.Panel.Content.extend({
  init: function () {
    var panel = this;

    this.base();

    this.el.find('a.zmenu').on('click', this.zmenu);

    this.el.find('a.filter_toggle').on('click', this.filter_toggle);

    this.el.find('input.autocomplete').on('focus', this.filter_autocomplete);
    //this.el.find('select.autocomplete').on('change', this.filter_autocomplete);
  },

  zmenu: function(e){
    var el = $(this);
    Ensembl.EventManager.trigger('makeZMenu', el.text().replace(/\W/g, '_'), { event: e, area: {a: el}});
    return false;
  },

  filter_toggle: function(e){
    $("." + this.rel).each(function() {
      this.style.display = (this.style.display == 'none' ? '' : 'none');
    });
  },

  filter_autocomplete: function(e){
      var el = $(this);
      var fieldNum = this.name.replace("field", "").replace("value", "");

      // find value and field input
      var value = $("input[name='value" + fieldNum + "']");
      var field = $("select[name='field" + fieldNum + "']");

      var autoValues = {
        Allele: [
          'A', 'C', 'G', 'T'
        ],
        Consequence: [
          'intergenic_variant',
          'intron_variant',
          'upstream_gene_variant',
          'downstream_gene_variant',
          '5_prime_utr_variant',
          '3_prime_utr_variant',
          'splice_region_variant',
          'splice_donor_variant',
          'splice_acceptor_variant',
          'frameshift_variant',
          'transcript_ablation',
          'transcript_amplification',
          'inframe_insertion',
          'inframe_deletion',
          'synonymous_variant',
          'stop_retained_variant',
          'missense_variant',
          'initiator_codon_variant',
          'stop_gained',
          'stop_lost',
          'mature_mirna_variant',
          'non_coding_exon_variant',
          'nc_transcript_variant',
          'incomplete_terminal_codon_variant',
          'nmd_transcript_variant',
          'coding_sequence_variant',
          'tfbs_ablation',
          'tfbs_amplification',
          'tf_binding_site_variant',
          'regulatory_region_variant',
          'regulatory_region_ablation',
          'regulatory_region_amplification'
        ],
        Feature_type: [
          'Transcript',
          'RegulatoryFeature',
          'MotifFeature'
        ],
        BIOTYPE: [
          'misc_RNA',
          'miRNA',
          'snRNA',
          'snoRNA',
          'rRNA',
          'protein_coding',
          'nonsense_mediated_decay',
          'retained_intron',
          'antisense',
          'pseudogene',
          'lincRNA',
          'processed_pseudogene',
          'processed_transcript',
          'sense_intronic',
          'unprocessed_pseudogene',
          'transcribed_processed_pseudogene',
          'transcribed_unprocessed_pseudogene',
          'unitary_pseudogene',
          'Mt_tRNA',
          'Mt_rRNA',
          'sense_overlapping',
          'IG_V_pseudogene',
          'TEC',
          '3prime_overlapping_ncrna',
          'non_stop_decay',
          'IG_C_pseudogene',
          'polymorphic_pseudogene',
          'IG_V_gene',
          'IG_D_gene',
          'IG_C_gene',
          'IG_J_gene',
          'TR_J_pseudogene',
          'TR_V_pseudogene',
          'TR_V_gene',
          'IG_J_pseudogene',
          'TR_J_gene',
          'TR_C_gene',
          'TR_D_gene',
          'LRG_gene'
        ],
        SIFT: [
          'tolerated',
          'deleterious'
        ],
        PolyPhen: [
          'benign',
          'possibly_damaging',
          'probably_damaging',
          'unknown'
        ]
      };

      if(autoValues[field[0].value] && autoValues[field[0].value].length) {
        value.autocomplete({
          minLength: 0,
          source: autoValues[field[0].value]
        });
      }
      else {
        value.autocomplete({});
      }

      // update placeholder
      if(field[0].value == 'Location') {
        value.attr("placeholder", "chr:start-end");
      }
      else {
        value.attr("placeholder", "defined");
      }

      return false;
  }
});
<commit_msg>get autocomplete values from js_param<commit_after>// $Revision$

Ensembl.Panel.VEPResults = Ensembl.Panel.Content.extend({
  init: function () {
    var panel = this;

    this.base();

    this.el.find('a.zmenu').on('click', this.zmenu);

    this.el.find('a.filter_toggle').on('click', this.filter_toggle);

    this.el.find('input.autocomplete').on('focus', {panel: this}, this.filter_autocomplete);
  },

  zmenu: function(e){
    var el = $(this);
    Ensembl.EventManager.trigger('makeZMenu', el.text().replace(/\W/g, '_'), { event: e, area: {a: el}});
    return false;
  },

  filter_toggle: function(e){
    $("." + this.rel).each(function() {
      this.style.display = (this.style.display == 'none' ? '' : 'none');
    });
  },

  filter_autocomplete: function(e){
      var el = $(this);
      var fieldNum = this.name.replace("field", "").replace("value", "");
      var panel = e.data.panel;

      // find value and field input
      var value = $("input[name='value" + fieldNum + "']");
      var field = $("select[name='field" + fieldNum + "']");

      var autoValues = JSON.parse(panel.params['auto_values'].replace(/\'/g, '"'));

      if(autoValues[field[0].value] && autoValues[field[0].value].length) {
        value.autocomplete({
          minLength: 0,
          source: autoValues[field[0].value]
        });
      }
      else if(value.hasClass('ui-autocomplete-input')) {
        value.autocomplete("destroy");
      }

      // update placeholder
      if(field[0].value == 'Location') {
        value.attr("placeholder", "chr:start-end");
      }
      else {
        value.attr("placeholder", "defined");
      }

      return false;
  }
});
<|endoftext|>


<commit_before>/**
enyo.TabBar is a scrolled set of radio buttons that is used by TabPanels. This bar may
be used by other kinds to provide a similar layout

Here's an example:

	enyo.kind({
		  name: "myStuff"
	});

	enyo.kind({
		name: "App",
		fit: true,
		components: [
			{name:"bar",kind: "TabBar"},
			{kind: "MyStuff"}
		],

		events: {
			onTabChanged: "switchStuff"
		},

		create: function() {
			this.$.bar.addTab(
				{
					'caption': 'greetings',
					'data' : { 'msg': 'Hello World !' } // arbitrary user data
				}
			) ;
		},

		switchStuff: function(inSender,inEvent) {
			this.log("Tapped tab with caption "+ inEvent.caption
					 + " and message " + inEvent.data.msg );
		}
	});

*/

enyo.kind (
	{
		name: 'onyx.TabBar',
		kind: "FittableColumns",
		isPanel: true,

		events: {
			/**
			 Fired when a tab different from the one currently selected is tapped.
			 inEvent contains :

			{
				'index': 3, // index of tab in tab bar
				'userId': 1234, // unique id in tab managed by user
				'caption': 'bar.js', // tab label
				'data': { 'lang': 'javascript' },
				'next': callback // call with error message if problem
			}

			 */
			onTabChanged: "",

			/**
			 * Fired when a tab is about to be removed. inEvent
			 * contains the same data as onTabChanged.
			 *
			 * if (removeOk) { inEvent.next() ;}
			 * else ( inEvent.next('not now') ;}
			 *
			 */
			onTabRemoveRequested: "",

			/**
			 * Fired when a tab is removed. inEvent contains the same
			 * data as onTabChanged (minus the next callback)
			 */
			onTabRemoved: ""
		},

		components: [
			{
				name: "scroller",
				kind: "Scroller",
				fit:true,
				maxHeight: "100px",

				// FIXME: may need to be revisited for desktop
				// activate calls scrollIntoView, which call strategy.scroll
				// this method is implemented *only* in TransitionScrollStrategy
				// which may be an enyo bug (2303)
				strategyKind: "TransitionScrollStrategy",
				//strategyKind: "TranslateScrollStrategy",

				thumb: false,
				vertical: "hidden",
				horizontal: "auto",
				classes: "onyx-tab-panel-scroller",
				components: [
					{
						name: "tabs",
						kind: "onyx.RadioGroup",
						defaultKind: "onyx.TabBar.Item",
						style: "text-align: left; white-space: nowrap;",
						onTabActivated: 'switchTab'
					}
				],
			},
			{
				kind: "onyx.MenuDecorator",
				components: [
					{
						kind: "onyx.Button",
						content: "\\/" // FIXME: is ugly
					},
					{
						name: "picker",
						kind: "onyx.ContextualPopup",
						actionButtons: [
							{content:"Button 1", classes: "onyx-button-warning"},
							{content:"Button 2"}
						]
					}
				]
			}
		],

		debug: true,

		// lastIndex is required to avoid duplicate index in the tab bar.
		lastIndex: 0,

		//* @protected
		clientTransitionStart: function(inSender, inEvent) {
			var i = inEvent.toIndex;
			var t = this.$.tabs.getClientControls()[i];
			if (t && t.hasNode()) {
				this.$.tabs.setActive(t);
				var tn = t.node;
				var tl = tn.offsetLeft;
				var tr = tl + tn.offsetWidth;
				var sb = this.$.scroller.getScrollBounds();
				if (tr < sb.left || tr > sb.left + sb.clientWidth) {
					this.$.scroller.scrollToControl(t);
				}
			}
			return true;
		},

		//* @public
		/**
		 *
		 * Append a new tab to the tab bar. inControl is an object
		 * with optional caption and data attributes. When not
		 * specified the tab will have a generated caption like
		 * 'Tab 0', 'Tab 1'. etc... data is an arbitrary object that will
		 * be given back with onTabChanged events
		 *
		 */
		addTab: function(inControl) {
			var c = inControl.caption || ("Tab " + this.lastIndex);
			var t = this.$.tabs.createComponent(
				{
					content:  c,
					userData: inControl.data || { },
					userId:   inControl.userId, // may be null
					tabIndex: this.lastIndex++
				}
			);
			this.debug && this.log("addControl add tab " + c);
			if (this.hasNode()) {
				t.render();
			}
			return t;
		},

		//* @public
		/**
		 *
		 * Remove a tab from the tab bar. target is an object with
		 * either a caption attribute or an index. The tab(s) matching
		 * the caption will be destroyed or the tab with matching
		 * index will be destroyed.
		 *
		 * Example:

			myTab.remove({'index':0}); // remove the leftmost tab
			myTab.remove({'caption':'foo.js'});

		 */

		removeTab: function(target) {
			var tab = this.resolveTab(target,'removeTab');
			tab && tab.destroy();
			this.doTabRemoved(
				{
					index:   tab.tabIndex,
					caption: tab.content,
					userId:  tab.userId,
					data:    tab.userData
				}
			);
		},

		//* @public
		/**
		 *
		 * Request to remove a tab from the tab bar. This is a bit
		 * like removeTab, except that a onTabRemoveRequested event is
		 * fired to let the application the possibility to cancel the
		 * request.
		 *
		 */

		requestRemoveTab: function(target) {
			var tab = this.resolveTab(target,'removeTab');
			var that = this ;
			if (tab) {
				target.next = function(err) {
					if (err) { throw new Error(err);   }
					else     { that.removeTab(target); }
				} ;
				this.doTabRemoveRequested( target ) ;
			}
		},

		//* @protected
		resolveTab: function(target,action_name){
			var targetTab ;
			if (target.userId) {
				enyo.forEach(
					this.$.tabs.controls,
					function(tab){
						if (tab.userId === target.userId) {
							targetTab = tab;
						}
					}
				);
			}
			else if (target.caption) {
				enyo.forEach(
					this.$.tabs.controls,
					function(tab){
						if (tab.content === target.caption) {
							targetTab = tab;
						}
					}
				);
			}
			else if (typeof target.index !== 'undefined') {
				targetTab = this.$.tabs.controls[target.index];
			}
			else {
				throw new Error("internal: " + action_name+ " called without index or caption");
			}
			return targetTab ;
		},

		//* @public
		/**
		 *
		 * Activate a tab in the tab bar. target is an object with
		 * either a caption attribute or an index. The tab(s) matching
		 * the caption will be activated or the tab with matching
		 * index will be activated
		 *
		 * Example:

			myTab.activate({'index':0}); // activate the leftmost tab
			myTab.activate({'caption':'foo.js'});

		 * Note that tabActivated event will be fired.
		 *
		 */
		activate: function(target) {
			var tab = this.resolveTab(target,'activate');
			this.debug && this.log("activate called on tab", target) ;
			if (tab) {
				tab.setActive(true) ;
				this.$.scroller.scrollIntoView(tab);
			}
		},

		//* @protected
		switchTab: function(inSender, inEvent) {
			var oldIndex = this.selectedId || 0 ;
			this.selectedId = inEvent.index;
			this.debug && this.log("switchTab called on index " , inEvent, inSender ) ;
			this.doTabChanged(
				{
					index:   inEvent.index,
					caption: inEvent.caption,
					data:    inEvent.userData,
					userId:  inEvent.userId,
					next:    enyo.bind(this,'undoSwitchOnError', oldIndex)
				}
			);
			return true ;
		},

		//* @protected
		undoSwitchOnError: function(oldIndex, err) {
			if (err) {
				this.log("app requested to activate back tab index "+ oldIndex + " because ",err);
				this.activate({ 'index': oldIndex } ) ;
			}
		},

		// resize stuff:
		// use scroller's getScrollBounds to get scroll boundaries
		resizeHandler: function() {
			this.inherited(arguments);
			this.adjustTabWidth() ;
		},

		origTabWidth: null,
		adjustTabWidth: function(inSender, inEvent) {
			var scrollWidth = this.$.scroller.hasNode().scrollWidth;
			var scrolledWidth = this.$.scroller.getBounds().width;
			this.origTabWidth = this.origTabWidth || this.$.tabs.getBounds().width;
			var tabsWidth = this.origTabWidth ;
			this.log("scroll width", scrollWidth ," scrolled ", scrolledWidth ,
					 "tab:" + tabsWidth ) ;
			var coeff = scrolledWidth > tabsWidth ? 1
			          :                             scrolledWidth / tabsWidth ;
			this.log("coeff is ", coeff) ;
			coeff = coeff < 0.5 ? 0.5 : coeff;

			enyo.forEach(
				this.$.tabs.getControls(),
				function(tab){
					tab.reduce(coeff) ;
				}
			);
		}

	}
);

<commit_msg>ENYO-2255: resize tabs when adding or removing a tab<commit_after>/**
enyo.TabBar is a scrolled set of radio buttons that is used by TabPanels. This bar may
be used by other kinds to provide a similar layout

Here's an example:

	enyo.kind({
		  name: "myStuff"
	});

	enyo.kind({
		name: "App",
		fit: true,
		components: [
			{name:"bar",kind: "TabBar"},
			{kind: "MyStuff"}
		],

		events: {
			onTabChanged: "switchStuff"
		},

		create: function() {
			this.$.bar.addTab(
				{
					'caption': 'greetings',
					'data' : { 'msg': 'Hello World !' } // arbitrary user data
				}
			) ;
		},

		switchStuff: function(inSender,inEvent) {
			this.log("Tapped tab with caption "+ inEvent.caption
					 + " and message " + inEvent.data.msg );
		}
	});

*/

enyo.kind (
	{
		name: 'onyx.TabBar',
		kind: "FittableColumns",
		isPanel: true,

		events: {
			/**
			 Fired when a tab different from the one currently selected is tapped.
			 inEvent contains :

			{
				'index': 3, // index of tab in tab bar
				'userId': 1234, // unique id in tab managed by user
				'caption': 'bar.js', // tab label
				'data': { 'lang': 'javascript' },
				'next': callback // call with error message if problem
			}

			 */
			onTabChanged: "",

			/**
			 * Fired when a tab is about to be removed. inEvent
			 * contains the same data as onTabChanged.
			 *
			 * if (removeOk) { inEvent.next() ;}
			 * else ( inEvent.next('not now') ;}
			 *
			 */
			onTabRemoveRequested: "",

			/**
			 * Fired when a tab is removed. inEvent contains the same
			 * data as onTabChanged (minus the next callback)
			 */
			onTabRemoved: ""
		},

		components: [
			{
				name: "scroller",
				kind: "Scroller",
				fit:true,
				maxHeight: "100px",

				// FIXME: may need to be revisited for desktop
				// activate calls scrollIntoView, which call strategy.scroll
				// this method is implemented *only* in TransitionScrollStrategy
				// which may be an enyo bug (2303)
				strategyKind: "TransitionScrollStrategy",
				//strategyKind: "TranslateScrollStrategy",

				thumb: false,
				vertical: "hidden",
				horizontal: "auto",
				classes: "onyx-tab-panel-scroller",
				components: [
					{
						name: "tabs",
						kind: "onyx.RadioGroup",
						defaultKind: "onyx.TabBar.Item",
						style: "text-align: left; white-space: nowrap;",
						onTabActivated: 'switchTab'
					}
				],
			},
			{
				kind: "onyx.MenuDecorator",
				components: [
					{
						kind: "onyx.Button",
						content: "\\/" // FIXME: is ugly
					},
					{
						name: "picker",
						kind: "onyx.ContextualPopup",
						actionButtons: [
							{content:"Button 1", classes: "onyx-button-warning"},
							{content:"Button 2"}
						]
					}
				]
			}
		],

		debug: true,

		// lastIndex is required to avoid duplicate index in the tab bar.
		lastIndex: 0,

		//* @protected
		clientTransitionStart: function(inSender, inEvent) {
			var i = inEvent.toIndex;
			var t = this.$.tabs.getClientControls()[i];
			if (t && t.hasNode()) {
				this.$.tabs.setActive(t);
				var tn = t.node;
				var tl = tn.offsetLeft;
				var tr = tl + tn.offsetWidth;
				var sb = this.$.scroller.getScrollBounds();
				if (tr < sb.left || tr > sb.left + sb.clientWidth) {
					this.$.scroller.scrollToControl(t);
				}
			}
			return true;
		},

		rendered: function() {
			this.inherited(arguments);
			this.resetWidth();
		},

		//* @public
		/**
		 *
		 * Append a new tab to the tab bar. inControl is an object
		 * with optional caption and data attributes. When not
		 * specified the tab will have a generated caption like
		 * 'Tab 0', 'Tab 1'. etc... data is an arbitrary object that will
		 * be given back with onTabChanged events
		 *
		 */
		addTab: function(inControl) {
			var c = inControl.caption || ("Tab " + this.lastIndex);
			var t = this.$.tabs.createComponent(
				{
					content:  c,
					userData: inControl.data || { },
					userId:   inControl.userId, // may be null
					tabIndex: this.lastIndex++
				}
			);
			this.debug && this.log("addControl add tab " + c);
			if (this.hasNode()) {
				t.render();
				this.resetWidth();
			}
			return t;
		},

		//* @public
		/**
		 *
		 * Remove a tab from the tab bar. target is an object with
		 * either a caption attribute or an index. The tab(s) matching
		 * the caption will be destroyed or the tab with matching
		 * index will be destroyed.
		 *
		 * Example:

			myTab.remove({'index':0}); // remove the leftmost tab
			myTab.remove({'caption':'foo.js'});

		 */

		removeTab: function(target) {
			var tab = this.resolveTab(target,'removeTab');
			tab && tab.destroy();
			this.resetWidth();
			this.doTabRemoved(
				{
					index:   tab.tabIndex,
					caption: tab.content,
					userId:  tab.userId,
					data:    tab.userData
				}
			);
		},

		//* @public
		/**
		 *
		 * Request to remove a tab from the tab bar. This is a bit
		 * like removeTab, except that a onTabRemoveRequested event is
		 * fired to let the application the possibility to cancel the
		 * request.
		 *
		 */

		requestRemoveTab: function(target) {
			var tab = this.resolveTab(target,'removeTab');
			var that = this ;
			if (tab) {
				target.next = function(err) {
					if (err) { throw new Error(err);   }
					else     { that.removeTab(target); }
				} ;
				this.doTabRemoveRequested( target ) ;
			}
		},

		//* @protected
		resolveTab: function(target,action_name){
			var targetTab ;
			if (target.userId) {
				enyo.forEach(
					this.$.tabs.controls,
					function(tab){
						if (tab.userId === target.userId) {
							targetTab = tab;
						}
					}
				);
			}
			else if (target.caption) {
				enyo.forEach(
					this.$.tabs.controls,
					function(tab){
						if (tab.content === target.caption) {
							targetTab = tab;
						}
					}
				);
			}
			else if (typeof target.index !== 'undefined') {
				targetTab = this.$.tabs.controls[target.index];
			}
			else {
				throw new Error("internal: " + action_name+ " called without index or caption");
			}
			return targetTab ;
		},

		//* @public
		/**
		 *
		 * Activate a tab in the tab bar. target is an object with
		 * either a caption attribute or an index. The tab(s) matching
		 * the caption will be activated or the tab with matching
		 * index will be activated
		 *
		 * Example:

			myTab.activate({'index':0}); // activate the leftmost tab
			myTab.activate({'caption':'foo.js'});

		 * Note that tabActivated event will be fired.
		 *
		 */
		activate: function(target) {
			var tab = this.resolveTab(target,'activate');
			this.debug && this.log("activate called on tab", target) ;
			if (tab) {
				tab.setActive(true) ;
				this.$.scroller.scrollIntoView(tab);
			}
		},

		//* @protected
		switchTab: function(inSender, inEvent) {
			var oldIndex = this.selectedId || 0 ;
			this.selectedId = inEvent.index;
			this.debug && this.log("switchTab called on index " , inEvent, inSender ) ;
			this.doTabChanged(
				{
					index:   inEvent.index,
					caption: inEvent.caption,
					data:    inEvent.userData,
					userId:  inEvent.userId,
					next:    enyo.bind(this,'undoSwitchOnError', oldIndex)
				}
			);
			return true ;
		},

		//* @protected
		undoSwitchOnError: function(oldIndex, err) {
			if (err) {
				this.log("app requested to activate back tab index "+ oldIndex + " because ",err);
				this.activate({ 'index': oldIndex } ) ;
			}
		},

		// resize stuff:
		// use scroller's getScrollBounds to get scroll boundaries
		resizeHandler: function() {
			this.inherited(arguments);
			this.adjustTabWidth() ;
		},

		origTabWidth: null,
		adjustTabWidth: function(inSender, inEvent) {
			var scrollWidth = this.$.scroller.hasNode().scrollWidth;
			var scrolledWidth = this.$.scroller.getBounds().width;
			this.origTabWidth = this.origTabWidth || this.$.tabs.getBounds().width;
			var tabsWidth = this.origTabWidth ;
			this.log("scroll width", scrollWidth ," scrolled ", scrolledWidth ,
					 "tab:" + tabsWidth ) ;
			var coeff = scrolledWidth > tabsWidth ? 1
			          :                             scrolledWidth / tabsWidth ;
			coeff = coeff < 0.5 ? 0.5 : coeff;
			this.applyCoeff(coeff) ;
		},

		applyCoeff: function(coeff) {
			if (this.debug) this.log("coeff is ", coeff) ;
			enyo.forEach(
				this.$.tabs.getControls(),
				function(tab){
					tab.reduce(coeff) ;
				}
			);
		},

		resetWidth: function() {
			this.applyCoeff(1) ; // restore original size to all tabs
			this.origTabWidth = this.$.tabs.getBounds().width; // measure tab width
			this.adjustTabWidth();
		}

	}
);

<|endoftext|>


<commit_before>/**
enyo.TabBar is a scrolled set of radio buttons that is used by TabPanels. This bar may
be used by other kinds to provide a similar layout

Here's an example:

	enyo.kind({
		name: "myStuff"
	});

	enyo.kind({
		name: "App",
		fit: true,
		components: [
			{name:"bar",kind: "onyx.TabBar"},
			{kind: "MyStuff"}
		],

		handlers: {
			onTabChanged: "switchStuff"
		},

		rendered: function() {
			this.inherited(arguments);
			this.$.bar.addTab(
				{
					'caption': 'greetings',
					'data' : { 'msg': 'Hello World !' } // arbitrary user data
				}
			) ;
		},

		switchStuff: function(inSender,inEvent) {
			this.log("Tapped tab with caption "+ inEvent.caption
				+ " and message " + inEvent.data.msg );
		}
	});

Tabs must be created after construction, i.e. in rendered function.

If tabs are created in 'create' function, the last created tabs will
not be selected.

*/

enyo.kind ({
	name: 'onyx.TabBar',
	kind: "FittableColumns",
	isPanel: true,
	classes: "onyx-tab-bar",

	checkBeforeClosing: false,

	debug: false,

	events: {
		/**
		Fired when a tab different from the one currently selected is tapped.
		inEvent contains :

		{
			'index': 3, // index of tab in tab bar
			'userId': 1234, // unique id in tab managed by user
			'caption': 'bar.js', // tab label
			'data': { 'lang': 'javascript' },
			'next': callback // call with error message if problem
		}

		 */
		onTabChanged: "",

		/**
		 * Fired when a tab is about to be removed. inEvent
		 * contains the same data as onTabChanged.
		 *
		 * if (removeOk) { inEvent.next() ;}
		 * else ( inEvent.next('not now') ;}
		 *
		 */
		onTabRemoveRequested: "",

		/**
		 * Fired when a tab is removed. inEvent contains the same
		 * data as onTabChanged (minus the next callback)
		 */
		onTabRemoved: ""
	},

	handlers: {
		onTabCloseRequest: "requestTabClose"
	},

	components: [
		{
			name: "scroller",
			kind: "Scroller",
			fit:true,
			maxHeight: "100px",

			// FIXME: may need to be revisited for desktop
			// activate calls scrollIntoView, which call strategy.scroll
			// this method is implemented *only* in TransitionScrollStrategy
			// which may be an enyo bug (ENYO-2303)
			strategyKind: "TransitionScrollStrategy",
			//strategyKind: "TranslateScrollStrategy",

			thumb: false,
			vertical: "hidden",
			horizontal: "auto",
			classes: "onyx-tab-bar-scroller",
			components: [
				{
					name: "tabs",
					classes: 'onyx-tab-holder',
					kind: "onyx.RadioGroup",
					defaultKind: "onyx.TabBar.Item",
					style: "text-align: left; white-space: nowrap;",
					onTabActivated: 'switchTab'
				},
				{ classes: "onyx-tab-line"},
				{ classes: "onyx-tab-rug"}
			]
		},
		{
			kind: "onyx.MenuDecorator",
			name: "tabPicker",
			components: [
				{
					kind: "onyx.IconButton",
					classes: "onyx-more-button",
					ontap: 'showPicker'
				}
			]
		}
	],

	// lastIndex is required to avoid duplicate index in the tab bar.
	lastIndex: 0,

	//* @protected
	clientTransitionStart: function(inSender, inEvent) {
		var i = inEvent.toIndex;
		var t = this.$.tabs.getClientControls()[i];
		if (t && t.hasNode()) {
			this.$.tabs.setActive(t);
			var tn = t.node;
			var tl = tn.offsetLeft;
			var tr = tl + tn.offsetWidth;
			var sb = this.$.scroller.getScrollBounds();
			if (tr < sb.left || tr > sb.left + sb.clientWidth) {
				this.$.scroller.scrollToControl(t);
			}
		}
		return true;
	},

	rendered: function() {
		this.inherited(arguments);
		this.resetWidth();
	},

	//* @public
	/**
	 *
	 * Append a new tab to the tab bar. inControl is an object
	 * with optional caption and data attributes. When not
	 * specified the tab will have a generated caption like
	 * 'Tab 0', 'Tab 1'. etc... data is an arbitrary object that will
	 * be given back with onTabChanged events
	 *
	 */
	addTab: function(inControl) {
		var c = inControl.caption || ("Tab " + this.lastIndex);
		this.selectedId = this.lastIndex++ ;
		var t = this.$.tabs.createComponent(
			{
				content:  c,
				userData: inControl.data || { },
				userId:   inControl.userId, // may be null
				tabIndex: this.selectedId,
				addBefore: this.$.line
			}
		);

		t.render();
		this.resetWidth();
		t.raise();
		t.setActive(true);
		return t;
	},

	//* @public
	/**
	 *
	 * Remove a tab from the tab bar. target is an object with
	 * either a caption attribute or an index. The tab(s) matching
	 * the caption will be destroyed or the tab with matching
	 * index will be destroyed.
	 *
	 * Example:

		myTab.removeTab({'index':0}); // remove the leftmost tab
		myTab.removeTab({'caption':'foo.js'});

	 */

	removeTab: function(target) {
		var tab = this.resolveTab(target,'removeTab');

		if (! tab) { return; }

		var activeTab = this.$.tabs.active ;
		var keepActiveTab = activeTab !== tab ;
		var gonerIndex = tab.indexInContainer();
		var tabData = {
			index:   tab.tabIndex,
			caption: tab.content,
			userId:  tab.userId,
			data:    tab.userData
		} ;

		tab.destroy();
		this.resetWidth();

		var ctrls = this.$.tabs.controls;
		var ctrlLength = ctrls.length ;
		var replacementTab
				= keepActiveTab           ? activeTab
				: gonerIndex < ctrlLength ? ctrls[gonerIndex]
				:                           ctrls[ ctrlLength - 1 ];

		// replacementTab may be undef if all tabs were removed
		if (replacementTab) {
			replacementTab.setActive(true) ;
			replacementTab.raise();
			this.$.scroller.scrollIntoView(replacementTab);
			this.doTabChanged(
				{
					index:   replacementTab.index,
					caption: replacementTab.caption,
					data:    replacementTab.userData,
					userId:  replacementTab.userId
				}
			);
		}

		this.doTabRemoved(tabData);
	},

	//* @public
	/**
	 *
	 * Request to remove a tab from the tab bar. This is a bit
	 * like removeTab, except that a onTabRemoveRequested event is
	 * fired to let the application the possibility to cancel the
	 * request.
	 *
	 */

	requestRemoveTab: function(target) {
		var tab = this.resolveTab(target,'removeTab');
		var tabData = {
			index:   tab.tabIndex,
			caption: tab.content,
			userId:  tab.userId,
			data:    tab.userData
		} ;
		var that = this ;
		if (tab) {
			tabData.next = function(err) {
				if (err) { throw new Error(err);   }
				else     { that.removeTab(target); }
			} ;
			this.doTabRemoveRequested( tabData ) ;
		}
	},

	//* @protected
	resolveTab: function(target,action_name){
		var targetTab ;
		if (target.userId) {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.userId === target.userId) {
						targetTab = tab;
					}
				}
			);
		}
		else if (target.caption) {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.content === target.caption) {
						targetTab = tab;
					}
				}
			);
		}
		else if (typeof target.index !== 'undefined') {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.tabIndex === target.index) {
						targetTab = tab;
					}
				}
			);
		}
		else {
			throw new Error("internal: " + action_name+ " called without index or caption");
		}
		return targetTab ;
	},

	//@ protected
	requestTabClose: function(inSender,inEvent) {
		if (this.checkBeforeClosing) {
			this.requestRemoveTab(inEvent) ;
		}
		else {
			this.removeTab(inEvent);
		}
	},

	//* @public
	/**
	 *
	 * Activate a tab in the tab bar. target is an object with
	 * either a caption attribute or an index. The tab(s) matching
	 * the caption will be activated or the tab with matching
	 * index will be activated
	 *
	 * Example:

		myTab.activate({'index':0}); // activate the leftmost tab
		myTab.activate({'caption':'foo.js'});

	 * Note that tabActivated event will be fired.
	 *
	 */
	activate: function(target) {
		var tab = this.resolveTab(target,'activate');
		if (tab) {
			tab.setActive(true) ;
			this.$.scroller.scrollIntoView(tab);
		}
	},

	//* @protected
	switchTab: function(inSender, inEvent) {
		var oldIndex = this.selectedId ;
		this.selectedId = inEvent.index;
		if ( this.selectedId != oldIndex ) {
			this.doTabChanged(
				{
					index:   inEvent.index,
					caption: inEvent.caption,
					data:    inEvent.userData,
					userId:  inEvent.userId,
					next:    enyo.bind(this,'undoSwitchOnError', oldIndex)
				}
			);
		}
		return true ;
	},

	//* @protected
	undoSwitchOnError: function(oldIndex, err) {
		if (err) {
			this.log("app requested to activate back tab index "+ oldIndex + " because ",err);
			this.activate({ 'index': oldIndex } ) ;
		}
	},

	// resize stuff:
	// use scroller's getScrollBounds to get scroll boundaries
	resizeHandler: function() {
		this.inherited(arguments);
		this.adjustTabWidth() ;
	},

	// compute tab width by adding width of tabs contained in tab bar.
	computeOrigTabWidth: function() {
		var result = 0;
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				var w = tab.origWidth() ;
				// must add margin and padding of inner button and outer tab-item
				result += w + 18 ;
			}
		);
		return result;
	},

	origTabWidth: null,
	adjustTabWidth: function(inSender, inEvent) {
		var scrolledWidth = this.$.scroller.getBounds().width;
		var tabsWidth = this.origTabWidth ;
		var coeff = scrolledWidth > tabsWidth ? 1 : scrolledWidth / tabsWidth ;
		coeff = coeff < 0.5 ? 0.5 : coeff;
		this.applyCoeff(coeff) ;
	},

	applyCoeff: function(coeff) {
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				tab.reduce(coeff) ;
			}
		);
	},

	resetWidth: function() {
		this.applyCoeff(1) ; // restore original size to all tabs
		this.origTabWidth = this.computeOrigTabWidth(); // measure tab width
		this.adjustTabWidth();
	},

	isEmpty: function() {
		return ! this.$.tabs.getControls().length ;
	},

	// Since action buttons of Contectual Popups are not dynamic, this
	// kind is created on the fly and destroyed once the user's click
	// on a button
	showPicker: function() {
		var actions = [];
		var that = this ;
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				actions.push({
					content: tab.content,
					ontap: enyo.bind(that, 'pickerHandler', tab)
				}) ;
			}
		);

		this.$.tabPicker.createComponent({
			name: "picker",
			kind: "onyx.ContextualPopup",
			floating: true,classes: "onyx-button-warning",
			style: 'display: block',
			actionButtons: actions
		});
		this.render();
	},

	pickerHandler: function(tab) {
		tab.setActive(true) ;
		this.$.scroller.scrollIntoView(tab);
		this.$.tabPicker.$.picker.destroy();
	}
});
<commit_msg>ENYO-2264: Right menu is showing correctly<commit_after>/**
enyo.TabBar is a scrolled set of radio buttons that is used by TabPanels. This bar may
be used by other kinds to provide a similar layout

Here's an example:

	enyo.kind({
		name: "myStuff"
	});

	enyo.kind({
		name: "App",
		fit: true,
		components: [
			{name:"bar",kind: "onyx.TabBar"},
			{kind: "MyStuff"}
		],

		handlers: {
			onTabChanged: "switchStuff"
		},

		rendered: function() {
			this.inherited(arguments);
			this.$.bar.addTab(
				{
					'caption': 'greetings',
					'data' : { 'msg': 'Hello World !' } // arbitrary user data
				}
			) ;
		},

		switchStuff: function(inSender,inEvent) {
			this.log("Tapped tab with caption "+ inEvent.caption
				+ " and message " + inEvent.data.msg );
		}
	});

Tabs must be created after construction, i.e. in rendered function.

If tabs are created in 'create' function, the last created tabs will
not be selected.

*/

enyo.kind ({
	name: 'onyx.TabBar',
	kind: "FittableColumns",
	isPanel: true,
	classes: "onyx-tab-bar",

	checkBeforeClosing: false,

	debug: false,

	events: {
		/**
		Fired when a tab different from the one currently selected is tapped.
		inEvent contains :

		{
			'index': 3, // index of tab in tab bar
			'userId': 1234, // unique id in tab managed by user
			'caption': 'bar.js', // tab label
			'data': { 'lang': 'javascript' },
			'next': callback // call with error message if problem
		}

		 */
		onTabChanged: "",

		/**
		 * Fired when a tab is about to be removed. inEvent
		 * contains the same data as onTabChanged.
		 *
		 * if (removeOk) { inEvent.next() ;}
		 * else ( inEvent.next('not now') ;}
		 *
		 */
		onTabRemoveRequested: "",

		/**
		 * Fired when a tab is removed. inEvent contains the same
		 * data as onTabChanged (minus the next callback)
		 */
		onTabRemoved: ""
	},

	handlers: {
		onTabCloseRequest: "requestTabClose"
	},

	components: [
		{
			name: "scroller",
			kind: "Scroller",
			fit:true,
			maxHeight: "100px",

			// FIXME: may need to be revisited for desktop
			// activate calls scrollIntoView, which call strategy.scroll
			// this method is implemented *only* in TransitionScrollStrategy
			// which may be an enyo bug (ENYO-2303)
			strategyKind: "TransitionScrollStrategy",
			//strategyKind: "TranslateScrollStrategy",

			thumb: false,
			vertical: "hidden",
			horizontal: "auto",
			classes: "onyx-tab-bar-scroller",
			components: [
				{
					name: "tabs",
					classes: 'onyx-tab-holder',
					kind: "onyx.RadioGroup",
					defaultKind: "onyx.TabBar.Item",
					style: "text-align: left; white-space: nowrap;",
					onTabActivated: 'switchTab'
				},
				{ classes: "onyx-tab-line"},
				{ classes: "onyx-tab-rug"}
			]
		},
		{
			kind: "onyx.MenuDecorator",
			name: "tabPicker",
			components: [
				{
					kind: "onyx.IconButton",
					classes: "onyx-more-button",
					ontap: "showPopupAtEvent"
				},
				{
					kind: "onyx.Menu",
					name: "popup"
				}
			]
		}
	],

	// lastIndex is required to avoid duplicate index in the tab bar.
	lastIndex: 0,

	//* @protected
	clientTransitionStart: function(inSender, inEvent) {
		var i = inEvent.toIndex;
		var t = this.$.tabs.getClientControls()[i];
		if (t && t.hasNode()) {
			this.$.tabs.setActive(t);
			var tn = t.node;
			var tl = tn.offsetLeft;
			var tr = tl + tn.offsetWidth;
			var sb = this.$.scroller.getScrollBounds();
			if (tr < sb.left || tr > sb.left + sb.clientWidth) {
				this.$.scroller.scrollToControl(t);
			}
		}
		return true;
	},

	rendered: function() {
		this.inherited(arguments);
		this.resetWidth();
	},

	//* @public
	/**
	 *
	 * Append a new tab to the tab bar. inControl is an object
	 * with optional caption and data attributes. When not
	 * specified the tab will have a generated caption like
	 * 'Tab 0', 'Tab 1'. etc... data is an arbitrary object that will
	 * be given back with onTabChanged events
	 *
	 */
	addTab: function(inControl) {
		var c = inControl.caption || ("Tab " + this.lastIndex);
		this.selectedId = this.lastIndex++ ;
		var t = this.$.tabs.createComponent(
			{
				content:  c,
				userData: inControl.data || { },
				userId:   inControl.userId, // may be null
				tabIndex: this.selectedId,
				addBefore: this.$.line
			}
		);

		t.render();
		this.resetWidth();
		t.raise();
		t.setActive(true);
		return t;
	},

	//* @public
	/**
	 *
	 * Remove a tab from the tab bar. target is an object with
	 * either a caption attribute or an index. The tab(s) matching
	 * the caption will be destroyed or the tab with matching
	 * index will be destroyed.
	 *
	 * Example:

		myTab.removeTab({'index':0}); // remove the leftmost tab
		myTab.removeTab({'caption':'foo.js'});

	 */

	removeTab: function(target) {
		var tab = this.resolveTab(target,'removeTab');

		if (! tab) { return; }

		var activeTab = this.$.tabs.active ;
		var keepActiveTab = activeTab !== tab ;
		var gonerIndex = tab.indexInContainer();
		var tabData = {
			index:   tab.tabIndex,
			caption: tab.content,
			userId:  tab.userId,
			data:    tab.userData
		} ;

		tab.destroy();
		this.resetWidth();

		var ctrls = this.$.tabs.controls;
		var ctrlLength = ctrls.length ;
		var replacementTab
				= keepActiveTab           ? activeTab
				: gonerIndex < ctrlLength ? ctrls[gonerIndex]
				:                           ctrls[ ctrlLength - 1 ];

		// replacementTab may be undef if all tabs were removed
		if (replacementTab) {
			replacementTab.setActive(true) ;
			replacementTab.raise();
			this.$.scroller.scrollIntoView(replacementTab);
			this.doTabChanged(
				{
					index:   replacementTab.index,
					caption: replacementTab.caption,
					data:    replacementTab.userData,
					userId:  replacementTab.userId
				}
			);
		}

		this.doTabRemoved(tabData);
	},

	//* @public
	/**
	 *
	 * Request to remove a tab from the tab bar. This is a bit
	 * like removeTab, except that a onTabRemoveRequested event is
	 * fired to let the application the possibility to cancel the
	 * request.
	 *
	 */

	requestRemoveTab: function(target) {
		var tab = this.resolveTab(target,'removeTab');
		var tabData = {
			index:   tab.tabIndex,
			caption: tab.content,
			userId:  tab.userId,
			data:    tab.userData
		} ;
		var that = this ;
		if (tab) {
			tabData.next = function(err) {
				if (err) { throw new Error(err);   }
				else     { that.removeTab(target); }
			} ;
			this.doTabRemoveRequested( tabData ) ;
		}
	},

	//* @protected
	resolveTab: function(target,action_name){
		var targetTab ;
		if (target.userId) {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.userId === target.userId) {
						targetTab = tab;
					}
				}
			);
		}
		else if (target.caption) {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.content === target.caption) {
						targetTab = tab;
					}
				}
			);
		}
		else if (typeof target.index !== 'undefined') {
			enyo.forEach(
				this.$.tabs.controls,
				function(tab){
					if (tab.tabIndex === target.index) {
						targetTab = tab;
					}
				}
			);
		}
		else {
			throw new Error("internal: " + action_name+ " called without index or caption");
		}
		return targetTab ;
	},

	//@ protected
	requestTabClose: function(inSender,inEvent) {
		if (this.checkBeforeClosing) {
			this.requestRemoveTab(inEvent) ;
		}
		else {
			this.removeTab(inEvent);
		}
	},

	//* @public
	/**
	 *
	 * Activate a tab in the tab bar. target is an object with
	 * either a caption attribute or an index. The tab(s) matching
	 * the caption will be activated or the tab with matching
	 * index will be activated
	 *
	 * Example:

		myTab.activate({'index':0}); // activate the leftmost tab
		myTab.activate({'caption':'foo.js'});

	 * Note that tabActivated event will be fired.
	 *
	 */
	activate: function(target) {
		var tab = this.resolveTab(target,'activate');
		if (tab) {
			this.raiseTab(tab);
		}
	},

	raiseTab: function(tab) {
		this.log(tab);
		tab.setActive(true) ;
		this.$.scroller.scrollIntoView(tab);
	},

	//* @protected
	switchTab: function(inSender, inEvent) {
		var oldIndex = this.selectedId ;
		this.selectedId = inEvent.index;
		if ( this.selectedId != oldIndex ) {
			this.doTabChanged(
				{
					index:   inEvent.index,
					caption: inEvent.caption,
					data:    inEvent.userData,
					userId:  inEvent.userId,
					next:    enyo.bind(this,'undoSwitchOnError', oldIndex)
				}
			);
		}
		return true ;
	},

	//* @protected
	undoSwitchOnError: function(oldIndex, err) {
		if (err) {
			this.log("app requested to activate back tab index "+ oldIndex + " because ",err);
			this.activate({ 'index': oldIndex } ) ;
		}
	},

	// resize stuff:
	// use scroller's getScrollBounds to get scroll boundaries
	resizeHandler: function() {
		this.inherited(arguments);
		this.adjustTabWidth() ;
	},

	// compute tab width by adding width of tabs contained in tab bar.
	computeOrigTabWidth: function() {
		var result = 0;
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				var w = tab.origWidth() ;
				// must add margin and padding of inner button and outer tab-item
				result += w + 18 ;
			}
		);
		return result;
	},

	origTabWidth: null,
	adjustTabWidth: function(inSender, inEvent) {
		var scrolledWidth = this.$.scroller.getBounds().width;
		var tabsWidth = this.origTabWidth ;
		var coeff = scrolledWidth > tabsWidth ? 1 : scrolledWidth / tabsWidth ;
		coeff = coeff < 0.5 ? 0.5 : coeff;
		this.applyCoeff(coeff) ;
	},

	applyCoeff: function(coeff) {
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				tab.reduce(coeff) ;
			}
		);
	},

	resetWidth: function() {
		this.applyCoeff(1) ; // restore original size to all tabs
		this.origTabWidth = this.computeOrigTabWidth(); // measure tab width
		this.adjustTabWidth();
	},

	isEmpty: function() {
		return ! this.$.tabs.getControls().length ;
	},

	// Since action buttons of Contectual Popups are not dynamic, this
	// kind is created on the fly and destroyed once the user's click
	// on a button
	showPopupAtEvent: function(inSender, inEvent) {
		var that = this ;
		var popup = this.$.popup;

		for (var name in popup.$) {
			if (popup.$.hasOwnProperty(name) && /menuItem/.test(name)) {
				popup.$[name].destroy();
			}
		}

		//popup.render();
		enyo.forEach(
			this.$.tabs.getControls(),
			function(tab){
				this.log('creat button for tab' + tab.tabIndex);
				that.$.popup.createComponent({
					content: tab.content,
					value: tab.tabIndex
				}) ;
			}
		);

		popup.maxHeightChanged();
		popup.showAtPosition({top: 30, right:30});
		this.render();
		return ;
	},

	popupButtonTapped: function(inSender, inEvent) {
		this.log(inEvent);
		this.activate({ index: inEvent.originator.tabIndex} );
	}
});
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

var searchPaths = [
  path.resolve(__dirname, '..', 'build', 'Release'),
  path.resolve(__dirname, '..', 'build', 'Debug'),
  path.resolve(__dirname, '..', 'bin'),
];
function searchCommand(command) {
  if (process.platform == 'win32') {
    command += '.exe'
    var binaryPath = path.join(__dirname, '..', 'deps', 'breakpad', command)
    if (fs.existsSync(binaryPath))
      return b
  } else {
    for (var i in searchPaths) {
      var binaryPath = path.join(searchPaths[i], command);
      if (fs.existsSync(binaryPath))
        return binaryPath;
    }
  }
}

function execute(command, args, callback) {
  var stdout = new Buffer(0);
  var stderr = new Buffer(0);
  var child = spawn(command, args);
  child.stdout.on('data', function(chunk) {
    stdout = Buffer.concat([stdout, chunk]);
  });
  child.stderr.on('data', function(chunk) {
    stderr = Buffer.concat([stderr, chunk]);
  });
  child.on('close', function(code) {
    if (code != 0)
      callback(stderr);
    else
      callback(null, stdout);
  });
}

var globalSymbolPaths = [];
module.exports.addSymbolPath = Array.prototype.push.bind(globalSymbolPaths);

module.exports.walkStack = function(minidump, symbolPaths, callback) {
  if (!callback) {
    callback = symbolPaths;
    symbolPaths = [];
  }

  var stackwalk = searchCommand('minidump_stackwalk');
  if (!stackwalk) {
    callback('Unable to find the "minidump_stackwalk"');
    return;
  }

  args = [minidump].concat(symbolPaths, globalSymbolPaths)
  execute(stackwalk, args, callback);
}

module.exports.dumpSymbol = function(binary, callback) {
  var dumpsyms = searchCommand('dump_syms');
  if (!dumpsyms) {
    callback('Unable to find the "dump_syms"');
    return;
  }

  execute(dumpsyms, ['-r', '-c', binary], callback)
}
<commit_msg>Search for .dSYM on OS X.<commit_after>var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;

var searchPaths = [
  path.resolve(__dirname, '..', 'build', 'Release'),
  path.resolve(__dirname, '..', 'build', 'Debug'),
  path.resolve(__dirname, '..', 'bin'),
];
function searchCommand(command) {
  if (process.platform == 'win32') {
    command += '.exe'
    var binaryPath = path.join(__dirname, '..', 'deps', 'breakpad', command)
    if (fs.existsSync(binaryPath))
      return b
  } else {
    for (var i in searchPaths) {
      var binaryPath = path.join(searchPaths[i], command);
      if (fs.existsSync(binaryPath))
        return binaryPath;
    }
  }
}

function execute(command, args, callback) {
  var stdout = new Buffer(0);
  var stderr = new Buffer(0);
  var child = spawn(command, args);
  child.stdout.on('data', function(chunk) {
    stdout = Buffer.concat([stdout, chunk]);
  });
  child.stderr.on('data', function(chunk) {
    stderr = Buffer.concat([stderr, chunk]);
  });
  child.on('close', function(code) {
    if (code != 0)
      callback(stderr);
    else
      callback(null, stdout);
  });
}

var globalSymbolPaths = [];
module.exports.addSymbolPath = Array.prototype.push.bind(globalSymbolPaths);

module.exports.walkStack = function(minidump, symbolPaths, callback) {
  if (!callback) {
    callback = symbolPaths;
    symbolPaths = [];
  }

  var stackwalk = searchCommand('minidump_stackwalk');
  if (!stackwalk) {
    callback('Unable to find the "minidump_stackwalk"');
    return;
  }

  args = [minidump].concat(symbolPaths, globalSymbolPaths)
  execute(stackwalk, args, callback);
}

module.exports.dumpSymbol = function(binary, callback) {
  var dumpsyms = searchCommand('dump_syms');
  if (!dumpsyms) {
    callback('Unable to find the "dump_syms"');
    return;
  }

  // Search for binary.dSYM on OS X.
  dsymPath = binary + '.dSYM';
  if (process.platform == 'darwin' && fs.existsSync(dsymPath))
    binary = dsymPath;

  execute(dumpsyms, ['-r', '-c', binary], callback)
}
<|endoftext|>


<commit_before>/*:
 * @plugindesc This plugin allows you to change the damage digits for Korean <RS_HangulDamages>
 * @author biud436
 *
 * @param damageBitmapName
 * @text Damage bitmap name
 * @desc
 * @require 1
 * @dir img/system/
 * @type file
 * @default Damage_1
 *
 * @param hangulDigitsTable
 * @text Hangul digits table
 * @type note
 * @desc allows you to consist of Hangul Digits Table.
 * @default ""
 *
 * @param hangulBaseRow
 * @text Hangul base row
 * @type number
 * @desc Specify the line index of the numeral adjective for Korean Hangul in the default image.
 * @default 5
 *
 * @param missBaseRow
 * @text Miss base row
 * @type number
 * @desc Specify the line index of image for MISS in the default image.
 * @default 4
 *
 * @help
 * ===================================================================
 * Test Sripts
 * ===================================================================
 * var target = $gameTroop._enemies[0];
 * target.gainHp(-100101150);
 * BattleManager._logWindow.clear()
 * BattleManager._logWindow.displayHpDamage(target);
 * target.startDamagePopup();
 * ===================================================================
 * Change Log
 * ===================================================================
 * 2018.07.07 (v1.0.0) - First Release
 * 2018.07.08 (v1.0.1) :
 * - 데미지 비트맵을 미리 불러옵니다.
 * - 표기법을 수 표기법 맞춤법에 맞춰 수정하였습니다.
 */
/*:ko
 * @plugindesc <RS_HangulDamages>
 * @author biud436
 *
 * @param damageBitmapName
 * @text 데미지 표시 비트맵 이름
 * @desc 데미지 표시 비트맵을 설정하여 게임 배포 시 제거되지 않게 합니다.
 * @require 1
 * @dir img/system/
 * @type file
 * @default Damage_1
 *
 * @param hangulDigitsTable
 * @text 한글 숫자 테이블
 * @type note
 * @desc 커스텀 한글 숫자 테이블을 설정할 수 있습니다. 지정하지 않으면 기본 값.
 * @default ""
 *
 * @param hangulBaseRow
 * @text Hangul base row
 * @type number
 * @desc 한국어 수사(數詞)가 있는 라인을 설정합니다. (만,억,조,경)
 * @default 5
 *
 * @param missBaseRow
 * @text Miss base row
 * @type number
 * @desc 미스 스프라이트가 있는 라인을 설정합니다.
 * @default 4
 *
 * @help
 * ===================================================================
 * 소개
 * ===================================================================
 *
 * ===================================================================
 * 테스트 스크립트
 * ===================================================================
 * var target = $gameTroop._enemies[0];
 * target.gainHp(-100101150);
 * BattleManager._logWindow.clear()
 * BattleManager._logWindow.displayHpDamage(target);
 * target.startDamagePopup();
 *
 * ===================================================================
 * Change Log
 * ===================================================================
 * 2018.07.07 (v1.0.0) - First Release
 * 2018.07.08 (v1.0.1) :
 * - 데미지 비트맵을 미리 불러옵니다.
 * - 표기법을 수 표기법 맞춤법에 맞춰 수정하였습니다.
 */

var Imported = Imported || {};
Imported.RS_HangulDamages = true;

var RS = RS || {};
RS.HangulDamages = RS.HangulDamages || {};
RS.HangulDamages.Params = RS.HangulDamages.Params || {};

(function($) {

    var parameters = $plugins.filter(function (i) {
        return i.description.contains('<RS_HangulDamages>');
    });

    parameters = (parameters.length > 0) && parameters[0].parameters;

    $.Params.damageBitmapName = parameters["damageBitmapName"] || "Damage_1";

    $.jsonParse = function (str) {
        var retData = JSON.parse(str, function (k, v) {
          try { return $.jsonParse(v); } catch (e) { return v; }
        });
        return retData;
    };

    String.prototype.toArray = function() {
        return this.split("");
      };

    String.prototype.reverse = function() {
        return this.toArray().reverse().join("");
    };

    String.prototype.toCommaAlpha = function(){
        return this.reverse().match(/.{1,4}/g).join(",").reverse();
    };

    $.Params.HANGUL_DIGITS_INDEX = $.jsonParse(parameters["hangulDigitsTable"]) || {
        // "천": 0,
        "만": 1,
        "억": 2,
        "조": 3,
        "경": 4,
        "X": 6
    };

    $.Params.HANGUL_BASE_ROW = Number(parameters["hangulBaseRow"]) || 5;
    $.Params.MISS_BASE_ROW = Number(parameters["missBaseRow"]) || 4;

    Sprite_Damage.prototype.initialize = function() {
        Sprite.prototype.initialize.call(this);
        this._duration = 90;
        this._flashColor = [0, 0, 0, 0];
        this._flashDuration = 0;
        this._damageBitmap = ImageManager.loadSystem(RS.HangulDamages.Params.damageBitmapName);
    };

    Sprite_Damage.prototype.digitWidth = function(n) {
        n = n || 10;
        return this._damageBitmap ? this._damageBitmap.width / n : 0;
    };

    Sprite_Damage.prototype.digitHeight = function() {
        return this._damageBitmap ? this._damageBitmap.height / 6 : 0;
    };

    Sprite_Damage.prototype.createMiss = function() {
        var w = this.digitWidth();
        var h = this.digitHeight();
        var sprite = this.createChildSprite();
        sprite.setFrame(0, $.Params.MISS_BASE_ROW * h, 4 * w, h);
        sprite.dy = 0;
    };

    /**
     * 자릿수를 찾아서 배열을 늘린다.
     * @param {String} strings
     */
    Sprite_Damage.prototype.whereDigits = function(strings) {

        var digits = [];
        var ret = [];
        var len = 0;

        ret = strings.toCommaAlpha().split(",");
        len = ret.length;

        for(var i = 0; i < len; i++) {
            var n = Number(ret[i]);
            // '한글 맞춤법' 제5장 띄어쓰기, 제2절, 제44항에 의하면, 수를 표기할 때,
            // '12억 3456만 7898', '3243조 7867억 8927만 6354'와 같이 표기해야 한다.
            // '12억 7898'에서 만 단위가 없을 수도 있다.
            if(n === 0 || !n) continue;
            digits.push(n);
            if((len - 1) !== i) { // 천 단위 생략
                switch(i) {
                    case (len - 2):
                    digits.push("만");
                    break;
                    case (len - 3):
                    digits.push("억");
                    break;
                    case (len - 4):
                    digits.push("조");
                    break;
                    case (len - 5):
                    digits.push("경");
                    break;
                }
                digits.push("X"); // 띄어쓰기 추가
            }
        }

        return digits.join(""); // 문자열로 변환

    };

    Sprite_Damage.prototype.createDigits = function(baseRow, value) {
        var string = Math.abs(value).toString();
        var row = baseRow + (value < 0 ? 1 : 0);

        var w = this.digitWidth();
        var h = this.digitHeight();

        string = this.whereDigits(string); // 배열을 변환한다.

        for (var i = 0; i < string.length; i++) {
            var sprite = this.createChildSprite();
            var n = Number(string[i]);
            row = baseRow + (value < 0 ? 1 : 0);
            if(isNaN(n)) { // 만, 억, 조, 경
                row = $.Params.HANGUL_BASE_ROW;
                n = $.Params.HANGUL_DIGITS_INDEX[string[i]];
            }
            sprite.setFrame(n * w, row * h, w, h);
            sprite.x = (i - (string.length - 1) / 2) * w;
            sprite.dy = -i;
        }
    };

    //===================================================================
    // Scene_Boot
    //===================================================================

    var alias_Scene_Boot_loadSystemImages = Scene_Boot.loadSystemImages;
    Scene_Boot.loadSystemImages = function() {
        alias_Scene_Boot_loadSystemImages.call(this);
        ImageManager.reserveSystem($.Params.damageBitmapName);
    };

})(RS.HangulDamages);<commit_msg>change a plugin description.<commit_after>/*:
 * @plugindesc This plugin allows you to change the damage digits for Korean <RS_HangulDamages>
 * @author biud436
 *
 * @param damageBitmapName
 * @text Damage bitmap name
 * @desc
 * @require 1
 * @dir img/system/
 * @type file
 * @default Damage_1
 *
 * @param hangulDigitsTable
 * @text Hangul digits table
 * @type note
 * @desc allows you to consist of Hangul Digits Table.
 * @default ""
 *
 * @param hangulBaseRow
 * @text Hangul base row
 * @type number
 * @desc Specify the line index of the numeral adjective for Korean Hangul in the default image.
 * @default 5
 *
 * @param missBaseRow
 * @text Miss base row
 * @type number
 * @desc Specify the line index of image for MISS in the default image.
 * @default 4
 *
 * @help
 * ===================================================================
 * Test Sripts
 * ===================================================================
 * var target = $gameTroop._enemies[0];
 * target.gainHp(-100101150);
 * BattleManager._logWindow.clear()
 * BattleManager._logWindow.displayHpDamage(target);
 * target.startDamagePopup();
 * ===================================================================
 * Change Log
 * ===================================================================
 * 2018.07.07 (v1.0.0) - First Release
 * 2018.07.08 (v1.0.1) :
 * - 데미지 비트맵을 미리 불러옵니다.
 * - 표기법을 수 표기법 맞춤법에 맞춰 수정하였습니다.
 */
/*:ko
 * @plugindesc 데미지를 수 표기법에 맞춰서 표시합니다 <RS_HangulDamages>
 * @author biud436
 *
 * @param damageBitmapName
 * @text 데미지 표시 비트맵 이름
 * @desc 데미지 표시 비트맵을 설정하여 게임 배포 시 제거되지 않게 합니다.
 * @require 1
 * @dir img/system/
 * @type file
 * @default Damage_1
 *
 * @param hangulDigitsTable
 * @text 한글 숫자 테이블
 * @type note
 * @desc 커스텀 한글 숫자 테이블을 설정할 수 있습니다. 지정하지 않으면 기본 값.
 * @default ""
 *
 * @param hangulBaseRow
 * @text Hangul base row
 * @type number
 * @desc 한국어 수사(數詞)가 있는 라인을 설정합니다. (만,억,조,경)
 * @default 5
 *
 * @param missBaseRow
 * @text Miss base row
 * @type number
 * @desc 미스 스프라이트가 있는 라인을 설정합니다.
 * @default 4
 *
 * @help
 * ===================================================================
 * 소개
 * ===================================================================
 *
 * ===================================================================
 * 테스트 스크립트
 * ===================================================================
 * var target = $gameTroop._enemies[0];
 * target.gainHp(-100101150);
 * BattleManager._logWindow.clear()
 * BattleManager._logWindow.displayHpDamage(target);
 * target.startDamagePopup();
 *
 * ===================================================================
 * Change Log
 * ===================================================================
 * 2018.07.07 (v1.0.0) - First Release
 * 2018.07.08 (v1.0.1) :
 * - 데미지 비트맵을 미리 불러옵니다.
 * - 표기법을 수 표기법 맞춤법에 맞춰 수정하였습니다.
 */

var Imported = Imported || {};
Imported.RS_HangulDamages = true;

var RS = RS || {};
RS.HangulDamages = RS.HangulDamages || {};
RS.HangulDamages.Params = RS.HangulDamages.Params || {};

(function($) {

    var parameters = $plugins.filter(function (i) {
        return i.description.contains('<RS_HangulDamages>');
    });

    parameters = (parameters.length > 0) && parameters[0].parameters;

    $.Params.damageBitmapName = parameters["damageBitmapName"] || "Damage_1";

    $.jsonParse = function (str) {
        var retData = JSON.parse(str, function (k, v) {
          try { return $.jsonParse(v); } catch (e) { return v; }
        });
        return retData;
    };

    String.prototype.toArray = function() {
        return this.split("");
      };

    String.prototype.reverse = function() {
        return this.toArray().reverse().join("");
    };

    String.prototype.toCommaAlpha = function(){
        return this.reverse().match(/.{1,4}/g).join(",").reverse();
    };

    $.Params.HANGUL_DIGITS_INDEX = $.jsonParse(parameters["hangulDigitsTable"]) || {
        // "천": 0,
        "만": 1,
        "억": 2,
        "조": 3,
        "경": 4,
        "X": 6
    };

    $.Params.HANGUL_BASE_ROW = Number(parameters["hangulBaseRow"]) || 5;
    $.Params.MISS_BASE_ROW = Number(parameters["missBaseRow"]) || 4;

    Sprite_Damage.prototype.initialize = function() {
        Sprite.prototype.initialize.call(this);
        this._duration = 90;
        this._flashColor = [0, 0, 0, 0];
        this._flashDuration = 0;
        this._damageBitmap = ImageManager.loadSystem(RS.HangulDamages.Params.damageBitmapName);
    };

    Sprite_Damage.prototype.digitWidth = function(n) {
        n = n || 10;
        return this._damageBitmap ? this._damageBitmap.width / n : 0;
    };

    Sprite_Damage.prototype.digitHeight = function() {
        return this._damageBitmap ? this._damageBitmap.height / 6 : 0;
    };

    Sprite_Damage.prototype.createMiss = function() {
        var w = this.digitWidth();
        var h = this.digitHeight();
        var sprite = this.createChildSprite();
        sprite.setFrame(0, $.Params.MISS_BASE_ROW * h, 4 * w, h);
        sprite.dy = 0;
    };

    /**
     * 자릿수를 찾아서 배열을 늘린다.
     * @param {String} strings
     */
    Sprite_Damage.prototype.whereDigits = function(strings) {

        var digits = [];
        var ret = [];
        var len = 0;

        ret = strings.toCommaAlpha().split(",");
        len = ret.length;

        for(var i = 0; i < len; i++) {
            var n = Number(ret[i]);
            // '한글 맞춤법' 제5장 띄어쓰기, 제2절, 제44항에 의하면, 수를 표기할 때,
            // '12억 3456만 7898', '3243조 7867억 8927만 6354'와 같이 표기해야 한다.
            // '12억 7898'에서 만 단위가 없을 수도 있다.
            if(n === 0 || !n) continue;
            digits.push(n);
            if((len - 1) !== i) { // 천 단위 생략
                switch(i) {
                    case (len - 2):
                    digits.push("만");
                    break;
                    case (len - 3):
                    digits.push("억");
                    break;
                    case (len - 4):
                    digits.push("조");
                    break;
                    case (len - 5):
                    digits.push("경");
                    break;
                }
                digits.push("X"); // 띄어쓰기 추가
            }
        }

        return digits.join(""); // 문자열로 변환

    };

    Sprite_Damage.prototype.createDigits = function(baseRow, value) {
        var string = Math.abs(value).toString();
        var row = baseRow + (value < 0 ? 1 : 0);

        var w = this.digitWidth();
        var h = this.digitHeight();

        string = this.whereDigits(string); // 배열을 변환한다.

        for (var i = 0; i < string.length; i++) {
            var sprite = this.createChildSprite();
            var n = Number(string[i]);
            row = baseRow + (value < 0 ? 1 : 0);
            if(isNaN(n)) { // 만, 억, 조, 경
                row = $.Params.HANGUL_BASE_ROW;
                n = $.Params.HANGUL_DIGITS_INDEX[string[i]];
            }
            sprite.setFrame(n * w, row * h, w, h);
            sprite.x = (i - (string.length - 1) / 2) * w;
            sprite.dy = -i;
        }
    };

    //===================================================================
    // Scene_Boot
    //===================================================================

    var alias_Scene_Boot_loadSystemImages = Scene_Boot.loadSystemImages;
    Scene_Boot.loadSystemImages = function() {
        alias_Scene_Boot_loadSystemImages.call(this);
        ImageManager.reserveSystem($.Params.damageBitmapName);
    };

})(RS.HangulDamages);<|endoftext|>


<commit_before>var clc = require('cli-color');

var levelColors = { debug: clc.blue, info: clc.yellow, error: clc.red };
var levels = { debug: 0, info: 1, error: 2 };
var sep = ' - ';

function printLevel(level) {
  return (levelColors[level] || clc.white)(level) + sep;
}

module.exports = function defaultLogger (filterLevel) {
  return function (level, message, meta) {
    if (levels[level] >= levels[filterLevel]) {
      var optMeta = (meta) ? sep + JSON.stringify(meta) /*require('util').inspect(meta, { depth: 5 })*/ : '';
      console.log(printLevel(level) +  message + optMeta);
    }
  };
};
<commit_msg>update logging to use timestamps<commit_after>var clc = require('cli-color');

var levelColors = { debug: clc.blue, info: clc.yellow, error: clc.red };
var levels = { debug: 0, info: 1, error: 2 };
var sep = ' - ';

function printLevel(level) {
  return (levelColors[level] || clc.white)(level) + sep;
}

module.exports = function defaultLogger (filterLevel) {
  return function (level, message, meta) {
    if (levels[level] >= levels[filterLevel]) {
      var optMeta = (meta) ? sep + JSON.stringify(meta) /*require('util').inspect(meta, { depth: 5 })*/ : '';
      timestamp = '['+  new Date().toISOString() +']';

      console.log(timestamp + sep + printLevel(level) +  message + optMeta);
    }
  };
};
<|endoftext|>


<commit_before>"use strict";

const path = require('path');
const fs = require('fs');
const webpack = require('webpack');

function isUndefined(s) {
    return {}.toString.call(s) === `[object Undefined]`;
}

module.exports = function (options, webpackConfig) {
    const REG_SCRIPT_FILE = /\.(js|jsx)$/i;

    if (isUndefined(webpackConfig.output.filename)) {
        webpackConfig.output.filename = options.env === 'production' ? '[name]-[chunkhash].js' : '[name].js';
    }
    webpackConfig.output.filename = path.join(webpackConfig.output.jsDir, webpackConfig.output.filename);

    if (isUndefined(webpackConfig.output.chunkFilename)) {
        webpackConfig.output.chunkFilename = options.env === 'production' ? '[name]-chunk-[chunkhash].js' : '[name]-chunk.js';
    }
    webpackConfig.output.chunkFilename = path.join(webpackConfig.output.jsDir, webpackConfig.output.chunkFilename);

    const REG_EXCLUDE = /node_modules/i;

    let scriptModuleRules = [{
        test: REG_SCRIPT_FILE,
        exclude: REG_EXCLUDE,
        use: [{
            loader: 'babel-loader',
            options: require(path.join(__dirname, '../config/babel'))(webpackConfig)
        }]
    }];

    if (fs.existsSync(path.join(webpackConfig.context, '.eslintrc'))) {
        scriptModuleRules.unshift({
            test: REG_SCRIPT_FILE,
            exclude: REG_EXCLUDE,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader',
                options: require(path.join(__dirname, '../config/eslint'))(webpackConfig)
            }]
        });
    }

    webpackConfig.module.rules = webpackConfig.module.rules.concat(scriptModuleRules);

    if (options.env === 'production') {
        webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
            output: {
                ascii_only: true
            },
            beautify: false, // 最紧凑的输出
            comments: false, // 删除所有的注释
            compress: {
                warnings: false, // 在UglifyJs删除没有用到的代码时不输出警告
                drop_console: true, // 删除所有的 `console` 语句, 还可以兼容ie浏览器
                collapse_vars: true, // 内嵌定义了但是只用到一次的变量
                reduce_vars: true // 提取出出现多次但是没有定义成变量去引用的静态值
            }
        }));
    }

    return webpackConfig;
};
<commit_msg>feat: js代码压缩改用webpack-parallel-uglify-plugin<commit_after>"use strict";

const path = require('path');
const fs = require('fs');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

function isUndefined(s) {
    return {}.toString.call(s) === `[object Undefined]`;
}

module.exports = function (options, webpackConfig) {
    const REG_SCRIPT_FILE = /\.(js|jsx)$/i;

    if (isUndefined(webpackConfig.output.filename)) {
        webpackConfig.output.filename = options.env === 'production' ? '[name]-[chunkhash].js' : '[name].js';
    }
    webpackConfig.output.filename = path.join(webpackConfig.output.jsDir, webpackConfig.output.filename);

    if (isUndefined(webpackConfig.output.chunkFilename)) {
        webpackConfig.output.chunkFilename = options.env === 'production' ? '[name]-chunk-[chunkhash].js' : '[name]-chunk.js';
    }
    webpackConfig.output.chunkFilename = path.join(webpackConfig.output.jsDir, webpackConfig.output.chunkFilename);

    const REG_EXCLUDE = /node_modules/i;

    let scriptModuleRules = [{
        test: REG_SCRIPT_FILE,
        exclude: REG_EXCLUDE,
        use: [{
            loader: 'babel-loader',
            options: require(path.join(__dirname, '../config/babel'))(options, webpackConfig)
        }]
    }];

    if (fs.existsSync(path.join(webpackConfig.context, '.eslintrc'))) {
        scriptModuleRules.unshift({
            test: REG_SCRIPT_FILE,
            exclude: REG_EXCLUDE,
            enforce: 'pre',
            use: [{
                loader: 'eslint-loader',
                options: require(path.join(__dirname, '../config/eslint'))(webpackConfig)
            }]
        });
    }

    webpackConfig.module.rules = webpackConfig.module.rules.concat(scriptModuleRules);

    if (options.env === 'production') {
        webpackConfig.plugins.push(new ParallelUglifyPlugin({
            cacheDir: options.tmpCacheDir,
            uglifyJS: {
                // https://www.npmjs.com/package/uglify-js#output-options
                output: {
                    ascii_only: true,
                    beautify: false, // 最紧凑的输出
                    comments: false, // 删除所有的注释
                },

                // https://www.npmjs.com/package/uglify-js#compress-options
                compress: {
                    warnings: false, // 在UglifyJs删除没有用到的代码时不输出警告
                    drop_console: false, // 删除所有的 `console` 语句, 还可以兼容ie浏览器
                    drop_debugger: true, // 移除 `debugger;` 声明
                    collapse_vars: true, // 内嵌定义了但是只用到一次的变量
                    reduce_vars: true // 提取出出现多次但是没有定义成变量去引用的静态值
                }
            }
        }));
    }

    return webpackConfig;
};
<|endoftext|>