<commit_before>var Brew = require('./brew');
var expect = require('chai').expect;

describe('Brew model', function() {
  describe('get actual phase', function() {
    it('should find the phase in progress', function() {
      var brew = new Brew({
        name: 'Very IPA',
        startDate: new Date(),
        phases: [{
          min: 10,
          temp: 50,
          status: 'active'
        }, {
          min: 20,
          temp: 70,
          status: 'scheduled'
        }]
      });

      var actual = brew.getActualPhase();
      expect(actual).to.be.eql({
        min: 10,
        temp: 50,
        status: 'active'
      });
    });

    it('should handle when there is no phase in progress', function() {
      var brew = new Brew({
        name: 'Very IPA',
        startDate: new Date(),
        phases: [{
          min: 10,
          temp: 50,
          status: 'scheduled'
        }, {
          min: 20,
          temp: 70,
          status: 'scheduled'
        }]
      });

      var actual = brew.getActualPhase();
      expect(actual).to.be.undefined;
    });
  });
});
<commit_msg>Use BrewPhase in Brew tests<commit_after>var Brew = require('./brew');
var BrewPhase = require('./BrewPhase');
var expect = require('chai').expect;

describe('Brew model', function() {
  describe('get actual phase', function() {
    it('should find the phase in progress', function() {
      var actualPhase = new BrewPhase({
        min: 10,
        temp: 50,
        status: 'active'
      });

      var scheduledPhase = new BrewPhase({
        min: 20,
        temp: 70,
        status: 'scheduled'
      });

      var brew = new Brew({
        name: 'Very IPA',
        startDate: new Date(),
        phases: [actualPhase, scheduledPhase]
      });

      var actual = brew.getActualPhase();
      expect(actual).to.be.eql(actualPhase);
    });

    it('should handle when there is no phase in progress', function() {
      var scheduledPhase1 = new BrewPhase({
        min: 10,
        temp: 50,
        status: 'scheduled'
      });

      var scheduledPhase2 = new BrewPhase({
        min: 20,
        temp: 70,
        status: 'scheduled'
      });

      var brew = new Brew({
        name: 'Very IPA',
        startDate: new Date(),
        phases: [scheduledPhase1, scheduledPhase2]
      });

      var actual = brew.getActualPhase();
      expect(actual).to.be.undefined;
    });
  });
});
<|endoftext|>


<commit_before>import {metadata} from 'aurelia-metadata';
import {Container} from 'aurelia-dependency-injection';
import {TemplateRegistryEntry} from 'aurelia-loader';
import {ValueConverterResource, BindingBehaviorResource} from 'aurelia-binding';
import {ViewEngineHooksResource} from './view-engine-hooks-resource';
import {HtmlBehaviorResource} from './html-behavior';
import {viewStrategy, TemplateRegistryViewStrategy} from './view-strategy';
import {ViewResources} from './view-resources';
import {ResourceLoadContext} from './instructions';
import {_hyphenate} from './util';

/**
* Represents a module with view resources.
*/
export class ResourceModule {
  /**
  * Creates an instance of ResourceModule.
  * @param moduleId The id of the module that contains view resources.
  */
  constructor(moduleId: string) {
    this.id = moduleId;
    this.moduleInstance = null;
    this.mainResource = null;
    this.resources = null;
    this.viewStrategy = null;
    this.isInitialized = false;
    this.onLoaded = null;
    this.loadContext = null;
  }

  /**
  * Initializes the resources within the module.
  * @param container The dependency injection container usable during resource initialization.
  */
  initialize(container: Container): void {
    let current = this.mainResource;
    let resources = this.resources;
    let vs = this.viewStrategy;

    if (this.isInitialized) {
      return;
    }

    this.isInitialized = true;

    if (current !== undefined) {
      current.metadata.viewStrategy = vs;
      current.initialize(container);
    }

    for (let i = 0, ii = resources.length; i < ii; ++i) {
      current = resources[i];
      current.metadata.viewStrategy = vs;
      current.initialize(container);
    }
  }

  /**
  * Registers the resources in the module with the view resources.
  * @param registry The registry of view resources to regiser within.
  * @param name The name to use in registering the default resource.
  */
  register(registry:ViewResources, name?:string): void {
    let main = this.mainResource;
    let resources = this.resources;

    if (main !== undefined) {
      main.register(registry, name);
      name = null;
    }

    for (let i = 0, ii = resources.length; i < ii; ++i) {
      resources[i].register(registry, name);
      name = null;
    }
  }

  /**
  * Loads any dependencies of the resources within this module.
  * @param container The DI container to use during dependency resolution.
  * @param loadContext The loading context used for loading all resources and dependencies.
  * @return A promise that resolves when all loading is complete.
  */
  load(container: Container, loadContext?: ResourceLoadContext): Promise<void> {
    if (this.onLoaded !== null) {
      //if it's trying to load the same thing again during the same load, this is a circular dep, so just resolve
      return this.loadContext === loadContext ? Promise.resolve() : this.onLoaded;
    }

    let main = this.mainResource;
    let resources = this.resources;
    let loads;

    if (main !== undefined) {
      loads = new Array(resources.length + 1);
      loads[0] = main.load(container, loadContext);
      for (let i = 0, ii = resources.length; i < ii; ++i) {
        loads[i + 1] = resources[i].load(container, loadContext);
      }
    } else {
      loads = new Array(resources.length);
      for (let i = 0, ii = resources.length; i < ii; ++i) {
        loads[i] = resources[i].load(container, loadContext);
      }
    }

    this.loadContext = loadContext;
    this.onLoaded = Promise.all(loads);
    return this.onLoaded;
  }
}

/**
* Represents a single view resource with a ResourceModule.
*/
export class ResourceDescription {
  /**
  * Creates an instance of ResourceDescription.
  * @param key The key that the resource was exported as.
  * @param exportedValue The exported resource.
  * @param resourceTypeMeta The metadata located on the resource.
  */
  constructor(key: string, exportedValue: any, resourceTypeMeta?: Object) {
    if (!resourceTypeMeta) {
      resourceTypeMeta = metadata.get(metadata.resource, exportedValue);

      if (!resourceTypeMeta) {
        resourceTypeMeta = new HtmlBehaviorResource();
        resourceTypeMeta.elementName = _hyphenate(key);
        metadata.define(metadata.resource, resourceTypeMeta, exportedValue);
      }
    }

    if (resourceTypeMeta instanceof HtmlBehaviorResource) {
      if (resourceTypeMeta.elementName === undefined) {
        //customeElement()
        resourceTypeMeta.elementName = _hyphenate(key);
      } else if (resourceTypeMeta.attributeName === undefined) {
        //customAttribute()
        resourceTypeMeta.attributeName = _hyphenate(key);
      } else if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
        //no customeElement or customAttribute but behavior added by other metadata
        HtmlBehaviorResource.convention(key, resourceTypeMeta);
      }
    } else if (!resourceTypeMeta.name) {
      resourceTypeMeta.name = _hyphenate(key);
    }

    this.metadata = resourceTypeMeta;
    this.value = exportedValue;
  }

  /**
  * Initializes the resource.
  * @param container The dependency injection container usable during resource initialization.
  */
  initialize(container: Container): void {
    this.metadata.initialize(container, this.value);
  }

  /**
  * Registrers the resource with the view resources.
  * @param registry The registry of view resources to regiser within.
  * @param name The name to use in registering the resource.
  */
  register(registry: ViewResources, name?: string): void {
    this.metadata.register(registry, name);
  }

  /**
  * Loads any dependencies of the resource.
  * @param container The DI container to use during dependency resolution.
  * @param loadContext The loading context used for loading all resources and dependencies.
  * @return A promise that resolves when all loading is complete.
  */
  load(container: Container, loadContext?: ResourceLoadContext): Promise<void> | void {
    return this.metadata.load(container, this.value, loadContext);
  }
}

/**
* Analyzes a module in order to discover the view resources that it exports.
*/
export class ModuleAnalyzer {
  /**
  * Creates an instance of ModuleAnalyzer.
  */
  constructor() {
    this.cache = Object.create(null);
  }

  /**
  * Retrieves the ResourceModule analysis for a previously analyzed module.
  * @param moduleId The id of the module to lookup.
  * @return The ResouceModule if found, undefined otherwise.
  */
  getAnalysis(moduleId: string): ResourceModule {
    return this.cache[moduleId];
  }

  /**
  * Analyzes a module.
  * @param moduleId The id of the module to analyze.
  * @param moduleInstance The module instance to analyze.
  * @param mainResourceKey The name of the main resource.
  * @return The ResouceModule representing the analysis.
  */
  analyze(moduleId: string, moduleInstance: any, mainResourceKey?: string): ResourceModule {
    let mainResource;
    let fallbackValue;
    let fallbackKey;
    let resourceTypeMeta;
    let key;
    let exportedValue;
    let resources = [];
    let conventional;
    let vs;
    let resourceModule;

    resourceModule = this.cache[moduleId];
    if (resourceModule) {
      return resourceModule;
    }

    resourceModule = new ResourceModule(moduleId);
    this.cache[moduleId] = resourceModule;

    if (typeof moduleInstance === 'function') {
      moduleInstance = {'default': moduleInstance};
    }

    if (mainResourceKey) {
      mainResource = new ResourceDescription(mainResourceKey, moduleInstance[mainResourceKey]);
    }

    for (key in moduleInstance) {
      exportedValue = moduleInstance[key];

      if (key === mainResourceKey || typeof exportedValue !== 'function') {
        continue;
      }

      resourceTypeMeta = metadata.get(metadata.resource, exportedValue);

      if (resourceTypeMeta) {
        if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
          //no customeElement or customAttribute but behavior added by other metadata
          HtmlBehaviorResource.convention(key, resourceTypeMeta);
        }

        if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
          //no convention and no customeElement or customAttribute but behavior added by other metadata
          resourceTypeMeta.elementName = _hyphenate(key);
        }

        if (!mainResource && resourceTypeMeta instanceof HtmlBehaviorResource && resourceTypeMeta.elementName !== null) {
          mainResource = new ResourceDescription(key, exportedValue, resourceTypeMeta);
        } else {
          resources.push(new ResourceDescription(key, exportedValue, resourceTypeMeta));
        }
      } else if (viewStrategy.decorates(exportedValue)) {
        vs = exportedValue;
      } else if (exportedValue instanceof TemplateRegistryEntry) {
        vs = new TemplateRegistryViewStrategy(moduleId, exportedValue);
      } else {
        if (conventional = HtmlBehaviorResource.convention(key)) {
          if (conventional.elementName !== null && !mainResource) {
            mainResource = new ResourceDescription(key, exportedValue, conventional);
          } else {
            resources.push(new ResourceDescription(key, exportedValue, conventional));
          }

          metadata.define(metadata.resource, conventional, exportedValue);
        } else if (conventional = ValueConverterResource.convention(key)
          || BindingBehaviorResource.convention(key)
          || ViewEngineHooksResource.convention(key)) {
          resources.push(new ResourceDescription(key, exportedValue, conventional));
          metadata.define(metadata.resource, conventional, exportedValue);
        } else if (!fallbackValue) {
          fallbackValue = exportedValue;
          fallbackKey = key;
        }
      }
    }

    if (!mainResource && fallbackValue) {
      mainResource = new ResourceDescription(fallbackKey, fallbackValue);
    }

    resourceModule.moduleInstance = moduleInstance;
    resourceModule.mainResource = mainResource;
    resourceModule.resources = resources;
    resourceModule.viewStrategy = vs;

    return resourceModule;
  }
}
<commit_msg>feat(alias): process aliases for custom attributes<commit_after>import {metadata} from 'aurelia-metadata';
import {Container} from 'aurelia-dependency-injection';
import {TemplateRegistryEntry} from 'aurelia-loader';
import {ValueConverterResource, BindingBehaviorResource} from 'aurelia-binding';
import {ViewEngineHooksResource} from './view-engine-hooks-resource';
import {HtmlBehaviorResource} from './html-behavior';
import {viewStrategy, TemplateRegistryViewStrategy} from './view-strategy';
import {ViewResources} from './view-resources';
import {ResourceLoadContext} from './instructions';
import {_hyphenate} from './util';

/**
* Represents a module with view resources.
*/
export class ResourceModule {
  /**
  * Creates an instance of ResourceModule.
  * @param moduleId The id of the module that contains view resources.
  */
  constructor(moduleId: string) {
    this.id = moduleId;
    this.moduleInstance = null;
    this.mainResource = null;
    this.resources = null;
    this.viewStrategy = null;
    this.isInitialized = false;
    this.onLoaded = null;
    this.loadContext = null;
  }

  /**
  * Initializes the resources within the module.
  * @param container The dependency injection container usable during resource initialization.
  */
  initialize(container: Container): void {
    let current = this.mainResource;
    let resources = this.resources;
    let vs = this.viewStrategy;

    if (this.isInitialized) {
      return;
    }

    this.isInitialized = true;

    if (current !== undefined) {
      current.metadata.viewStrategy = vs;
      current.initialize(container);
    }

    for (let i = 0, ii = resources.length; i < ii; ++i) {
      current = resources[i];
      current.metadata.viewStrategy = vs;
      current.initialize(container);
    }
  }

  /**
  * Registers the resources in the module with the view resources.
  * @param registry The registry of view resources to regiser within.
  * @param name The name to use in registering the default resource.
  */
  register(registry:ViewResources, name?:string): void {
    let main = this.mainResource;
    let resources = this.resources;

    if (main !== undefined) {
      main.register(registry, name);
      name = null;
    }

    for (let i = 0, ii = resources.length; i < ii; ++i) {
      const resource = resources[i];
      resource.register(registry, name);
      name = null;

      /* register any aliases present */
      if (resource.metadata &&
          resource.metadata.attributeName !== undefined &&
          resource.value &&
          Array.isArray(resource.value.aliases)) {
        resource.value.aliases.forEach( (alias) => {
          registry.registerAttribute(alias, resource.metadata, resource.metadata.attributeName);
        });
      }
    }
  }

  /**
  * Loads any dependencies of the resources within this module.
  * @param container The DI container to use during dependency resolution.
  * @param loadContext The loading context used for loading all resources and dependencies.
  * @return A promise that resolves when all loading is complete.
  */
  load(container: Container, loadContext?: ResourceLoadContext): Promise<void> {
    if (this.onLoaded !== null) {
      //if it's trying to load the same thing again during the same load, this is a circular dep, so just resolve
      return this.loadContext === loadContext ? Promise.resolve() : this.onLoaded;
    }

    let main = this.mainResource;
    let resources = this.resources;
    let loads;

    if (main !== undefined) {
      loads = new Array(resources.length + 1);
      loads[0] = main.load(container, loadContext);
      for (let i = 0, ii = resources.length; i < ii; ++i) {
        loads[i + 1] = resources[i].load(container, loadContext);
      }
    } else {
      loads = new Array(resources.length);
      for (let i = 0, ii = resources.length; i < ii; ++i) {
        loads[i] = resources[i].load(container, loadContext);
      }
    }

    this.loadContext = loadContext;
    this.onLoaded = Promise.all(loads);
    return this.onLoaded;
  }
}

/**
* Represents a single view resource with a ResourceModule.
*/
export class ResourceDescription {
  /**
  * Creates an instance of ResourceDescription.
  * @param key The key that the resource was exported as.
  * @param exportedValue The exported resource.
  * @param resourceTypeMeta The metadata located on the resource.
  */
  constructor(key: string, exportedValue: any, resourceTypeMeta?: Object) {
    if (!resourceTypeMeta) {
      resourceTypeMeta = metadata.get(metadata.resource, exportedValue);

      if (!resourceTypeMeta) {
        resourceTypeMeta = new HtmlBehaviorResource();
        resourceTypeMeta.elementName = _hyphenate(key);
        metadata.define(metadata.resource, resourceTypeMeta, exportedValue);
      }
    }

    if (resourceTypeMeta instanceof HtmlBehaviorResource) {
      if (resourceTypeMeta.elementName === undefined) {
        //customeElement()
        resourceTypeMeta.elementName = _hyphenate(key);
      } else if (resourceTypeMeta.attributeName === undefined) {
        //customAttribute()
        resourceTypeMeta.attributeName = _hyphenate(key);
      } else if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
        //no customeElement or customAttribute but behavior added by other metadata
        HtmlBehaviorResource.convention(key, resourceTypeMeta);
      }
    } else if (!resourceTypeMeta.name) {
      resourceTypeMeta.name = _hyphenate(key);
    }

    this.metadata = resourceTypeMeta;
    this.value = exportedValue;
  }

  /**
  * Initializes the resource.
  * @param container The dependency injection container usable during resource initialization.
  */
  initialize(container: Container): void {
    this.metadata.initialize(container, this.value);
  }

  /**
  * Registrers the resource with the view resources.
  * @param registry The registry of view resources to regiser within.
  * @param name The name to use in registering the resource.
  */
  register(registry: ViewResources, name?: string): void {
    this.metadata.register(registry, name);
  }

  /**
  * Loads any dependencies of the resource.
  * @param container The DI container to use during dependency resolution.
  * @param loadContext The loading context used for loading all resources and dependencies.
  * @return A promise that resolves when all loading is complete.
  */
  load(container: Container, loadContext?: ResourceLoadContext): Promise<void> | void {
    return this.metadata.load(container, this.value, loadContext);
  }
}

/**
* Analyzes a module in order to discover the view resources that it exports.
*/
export class ModuleAnalyzer {
  /**
  * Creates an instance of ModuleAnalyzer.
  */
  constructor() {
    this.cache = Object.create(null);
  }

  /**
  * Retrieves the ResourceModule analysis for a previously analyzed module.
  * @param moduleId The id of the module to lookup.
  * @return The ResouceModule if found, undefined otherwise.
  */
  getAnalysis(moduleId: string): ResourceModule {
    return this.cache[moduleId];
  }

  /**
  * Analyzes a module.
  * @param moduleId The id of the module to analyze.
  * @param moduleInstance The module instance to analyze.
  * @param mainResourceKey The name of the main resource.
  * @return The ResouceModule representing the analysis.
  */
  analyze(moduleId: string, moduleInstance: any, mainResourceKey?: string): ResourceModule {
    let mainResource;
    let fallbackValue;
    let fallbackKey;
    let resourceTypeMeta;
    let key;
    let exportedValue;
    let resources = [];
    let conventional;
    let vs;
    let resourceModule;

    resourceModule = this.cache[moduleId];
    if (resourceModule) {
      return resourceModule;
    }

    resourceModule = new ResourceModule(moduleId);
    this.cache[moduleId] = resourceModule;

    if (typeof moduleInstance === 'function') {
      moduleInstance = {'default': moduleInstance};
    }

    if (mainResourceKey) {
      mainResource = new ResourceDescription(mainResourceKey, moduleInstance[mainResourceKey]);
    }

    for (key in moduleInstance) {
      exportedValue = moduleInstance[key];

      if (key === mainResourceKey || typeof exportedValue !== 'function') {
        continue;
      }

      resourceTypeMeta = metadata.get(metadata.resource, exportedValue);

      if (resourceTypeMeta) {
        if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
          //no customeElement or customAttribute but behavior added by other metadata
          HtmlBehaviorResource.convention(key, resourceTypeMeta);
        }

        if (resourceTypeMeta.attributeName === null && resourceTypeMeta.elementName === null) {
          //no convention and no customeElement or customAttribute but behavior added by other metadata
          resourceTypeMeta.elementName = _hyphenate(key);
        }

        if (!mainResource && resourceTypeMeta instanceof HtmlBehaviorResource && resourceTypeMeta.elementName !== null) {
          mainResource = new ResourceDescription(key, exportedValue, resourceTypeMeta);
        } else {
          resources.push(new ResourceDescription(key, exportedValue, resourceTypeMeta));
        }
      } else if (viewStrategy.decorates(exportedValue)) {
        vs = exportedValue;
      } else if (exportedValue instanceof TemplateRegistryEntry) {
        vs = new TemplateRegistryViewStrategy(moduleId, exportedValue);
      } else {
        if (conventional = HtmlBehaviorResource.convention(key)) {
          if (conventional.elementName !== null && !mainResource) {
            mainResource = new ResourceDescription(key, exportedValue, conventional);
          } else {
            resources.push(new ResourceDescription(key, exportedValue, conventional));
          }

          metadata.define(metadata.resource, conventional, exportedValue);
        } else if (conventional = ValueConverterResource.convention(key)
          || BindingBehaviorResource.convention(key)
          || ViewEngineHooksResource.convention(key)) {
          resources.push(new ResourceDescription(key, exportedValue, conventional));
          metadata.define(metadata.resource, conventional, exportedValue);
        } else if (!fallbackValue) {
          fallbackValue = exportedValue;
          fallbackKey = key;
        }
      }
    }

    if (!mainResource && fallbackValue) {
      mainResource = new ResourceDescription(fallbackKey, fallbackValue);
    }

    resourceModule.moduleInstance = moduleInstance;
    resourceModule.mainResource = mainResource;
    resourceModule.resources = resources;
    resourceModule.viewStrategy = vs;

    return resourceModule;
  }
}
<|endoftext|>


<commit_before>var GM = KityMinder.Geometry;

// 矩形的变形动画定义
var AreaAnimator = kity.createClass( "AreaAnimator", {
	base: kity.Animator,
	constructor: function ( startArea, endArea ) {
		startArea.opacity = 0;
		endArea.opacity = 0.8;
		this.callBase( startArea, endArea, function ( target, value ) {
			target.setPosition( value.x, value.y );
			target.setSize( value.width, value.height );
			target.setOpacity( value.opacity );
		} );
	}
} );

var MoveToParentCommand = kity.createClass( 'MoveToParentCommand', {
	base: Command,
	execute: function ( minder, nodes, parent ) {
		var node;
		for ( var i = nodes.length - 1; i >= 0; i-- ) {
			node = nodes[ i ];
			if ( node.getParent() ) {
				node.getParent().removeChild( node );
				parent.appendChild( node );
			}
		}
		minder.initStyle( minder.getRoot() );
		minder.select( nodes, true );
	}
} );

function boxMapper( node ) {
	return node.getRenderContainer().getRenderBox( 'top' );
}

// 对拖动对象的一个替代盒子，控制整个拖放的逻辑，包括：
//    1. 从节点列表计算出拖动部分
//    2. 产生替代矩形包围拖动部分
//    3. 动画收缩替代矩形到固定大小，成为替代盒子
//    4. 控制替代盒子的移动
//    5. 计算可以 drop 的节点，产生 drop 交互提示
var DragBox = kity.createClass( "DragBox", {
	base: kity.Group,

	constructor: function ( minder ) {
		this.callBase();
		this._minder = minder;
		this._draw();
	},

	// 绘制显示拖放范围的矩形和显示拖放信息的文本
	_draw: function () {
		this._rect = new kity.Rect()
			.setRadius( 5 )
			.fill( 'white' )
			.stroke( '#3399ff', 1 );
		this._text = new kity.Text()
			.setSize( 14 )
			.setTextAnchor( 'middle' )
			.fill( 'black' )
			.setStyle( 'cursor', 'default' );
		this.addShapes( [ this._rect, this._text ] );
	},

	// 从选中的节点计算拖放源
	//    并不是所有选中的节点都作为拖放源，如果选中节点中存在 A 和 B，
	//    并且 A 是 B 的祖先，则 B 不作为拖放源
	//
	//    计算过程：
	//       1. 将节点按照树高排序，排序后只可能是前面节点是后面节点的祖先
	//       2. 从后往前枚举排序的结果，如果发现枚举目标之前存在其祖先，
	//          则排除枚举目标作为拖放源，否则加入拖放源
	_calcDragSources: function () {
		this._dragSources = this._minder.getSelectedAncestors();
	},

	// 计算拖放目标可以释放的节点列表（释放意味着成为其子树），存在这条限制规则：
	//    - 不能拖放到拖放目标的子树上（允许拖放到自身，因为多选的情况下可以把其它节点加入）
	//
	//    1. 加入当前节点（初始为根节点）到允许列表
	//    2. 对于当前节点的每一个子节点：
	//       (1) 如果是拖放目标的其中一个节点，忽略（整棵子树被剪枝）
	//       (2) 如果不是拖放目标之一，以当前子节点为当前节点，回到 1 计算
	//    3. 返回允许列表
	//
	_calcDropTargets: function () {

		function findAvailableParents( nodes, root ) {
			var availables = [],
				i;
			availables.push( root );
			root.getChildren().forEach( function ( test ) {
				for ( i = 0; i < nodes.length; i++ ) {
					if ( nodes[ i ] == test ) return;
				}
				availables = availables.concat( findAvailableParents( nodes, test ) );
			} );
			return availables;
		}

		this._dropTargets = findAvailableParents( this._dragSources, this._minder.getRoot() );
		this._dropTargetBoxes = this._dropTargets.map( boxMapper );
	},

	// 进入拖放模式：
	//    1. 计算拖放源和允许的拖放目标
	//    2. 渲染拖放盒子
	//    3. 启动收缩动画
	//    4. 标记已启动
	_enterDragMode: function () {
		this._calcDragSources();
		if ( !this._dragSources.length ) {
			this._startPosition = null;
			return false;
		}
		this._calcDropTargets();
		this._drawForDragMode();
		this._shrink();
		this._dragMode = true;
		return true;
	},
	_leaveDragMode: function () {
		this.remove();
		this._dragMode = false;
		this._dropSucceedTarget = null;
	},
	_drawForDragMode: function () {
		this._text.setContent( this._dragSources.length + ' items' );
		this._text.setPosition( this._startPosition.x, this._startPosition.y + 5 );
		this._minder.getPaper().addShape( this );
	},
	_shrink: function () {
		// 合并所有拖放源图形的矩形即可
		function calcSourceArea( boxArray ) {
			var area = boxArray.pop();
			while ( boxArray.length ) {
				area = GM.mergeBox( area, boxArray.pop() );
			}
			return {
				x: area.left,
				y: area.top,
				width: area.width,
				height: area.height
			};
		}
		// 从焦点发散出一个固定的矩形即可
		function calcFocusArea( focusPoint ) {
			var width = 80,
				height = 30;
			return {
				x: focusPoint.x - width / 2,
				y: focusPoint.y - height / 2,
				width: width,
				height: height
			};
		}

		var sourceArea = calcSourceArea( this._dragSources.map( boxMapper ) );
		var focusArea = calcFocusArea( this._startPosition );
		var animator = new AreaAnimator( sourceArea, focusArea );
		animator.start( this._rect, 400, 'easeOutQuint' );
	},
	// 此处可用线段树优化，但考虑到节点不多，必要性不到，就用暴力测试
	_dropTest: function () {
		var dragBox = this.getRenderBox(),
			test;

		this._dropSucceedTarget = null;
		for ( var i = 0; i < this._dropTargetBoxes.length; i++ ) {
			test = this._dropTargetBoxes[ i ];
			if ( GM.isBoxIntersect( dragBox, test ) ) {
				this._dropSucceedTarget = this._dropTargets[ i ];
				return;
			}
		}
	},
	_updateDropHint: function () {
		var target = this._dropSucceedTarget,
			lastTarget = this._lastSucceedTarget;
		if ( target && target == lastTarget ) return;
		if ( lastTarget ) {
			this._removeDropStyle( lastTarget );
		}
		if ( target ) {
			this._addDropStyle( target );
		}
		this._lastSucceedTarget = target;
	},

	_removeDropStyle: function ( node ) {
		node._layout.bgRect.stroke( 'none' );
		this._rect.stroke( '#3399ff', 1 );
	},

	_addDropStyle: function ( node ) {
		node._layout.bgRect.stroke( 'rgb(254, 219, 0)', 3 );
		node.getRenderContainer().fxScale( 1.25, 1.25, 150, 'ease' ).fxScale( 0.8, 0.8, 150, 'ease' );
	},

	dragStart: function ( position ) {
		// 只记录开始位置，不马上开启拖放模式
		// 这个位置同时是拖放范围收缩时的焦点位置（中心）
		this._startPosition = position;
	},

	dragMove: function ( position ) {
		// 启动拖放模式需要最小的移动距离
		var DRAG_MOVE_THRESHOLD = 10;

		if ( !this._startPosition ) return;

		this._dragPosition = position;

		if ( !this._dragMode ) {
			// 判断拖放模式是否该启动
			if ( GM.getDistance( this._dragPosition, this._startPosition ) < DRAG_MOVE_THRESHOLD ) {
				return;
			}
			if ( !this._enterDragMode() ) {
				return;
			}
		}

		var movement = kity.Vector.fromPoints( this._startPosition, this._dragPosition );

		this.setTransform( new kity.Matrix().translate( movement.x, movement.y ) );

		this._dropTest();
		this._updateDropHint();
	},

	dragEnd: function () {
		this._startPosition = null;
		if ( !this._dragMode ) {
			return;
		}
		if ( this._dropSucceedTarget ) {
			this._minder.execCommand( 'movetoparent', this._dragSources, this._dropSucceedTarget );
		}
		this._leaveDragMode();
	}

} );

KityMinder.registerModule( "DragTree", function () {
	var dragStartPosition, dragBox, dragTargets, dropTargets, dragTargetBoxes, dropTarget;

	return {
		init: function () {
			this._dragBox = new DragBox( this );
		},
		events: {
			mousedown: function ( e ) {
				// 单选中根节点也不触发拖拽
				if ( e.getTargetNode() && e.getTargetNode() != this.getRoot() ) {
					this._dragBox.dragStart( e.getPosition() );
				}
			},
			'mousemove': function ( e ) {
				this._dragBox.dragMove( e.getPosition() );
			},
			'mouseup': function ( e ) {
				this._dragBox.dragEnd();
				this.rollbackStatus();
			}
		},
		commands: {
			'movetoparent': MoveToParentCommand
		}
	};
} );<commit_msg>hand bug fix<commit_after>var GM = KityMinder.Geometry;

// 矩形的变形动画定义
var AreaAnimator = kity.createClass( "AreaAnimator", {
	base: kity.Animator,
	constructor: function ( startArea, endArea ) {
		startArea.opacity = 0;
		endArea.opacity = 0.8;
		this.callBase( startArea, endArea, function ( target, value ) {
			target.setPosition( value.x, value.y );
			target.setSize( value.width, value.height );
			target.setOpacity( value.opacity );
		} );
	}
} );

var MoveToParentCommand = kity.createClass( 'MoveToParentCommand', {
	base: Command,
	execute: function ( minder, nodes, parent ) {
		var node;
		for ( var i = nodes.length - 1; i >= 0; i-- ) {
			node = nodes[ i ];
			if ( node.getParent() ) {
				node.getParent().removeChild( node );
				parent.appendChild( node );
			}
		}
		minder.initStyle( minder.getRoot() );
		minder.select( nodes, true );
	}
} );

function boxMapper( node ) {
	return node.getRenderContainer().getRenderBox( 'top' );
}

// 对拖动对象的一个替代盒子，控制整个拖放的逻辑，包括：
//    1. 从节点列表计算出拖动部分
//    2. 产生替代矩形包围拖动部分
//    3. 动画收缩替代矩形到固定大小，成为替代盒子
//    4. 控制替代盒子的移动
//    5. 计算可以 drop 的节点，产生 drop 交互提示
var DragBox = kity.createClass( "DragBox", {
	base: kity.Group,

	constructor: function ( minder ) {
		this.callBase();
		this._minder = minder;
		this._draw();
	},

	// 绘制显示拖放范围的矩形和显示拖放信息的文本
	_draw: function () {
		this._rect = new kity.Rect()
			.setRadius( 5 )
			.fill( 'white' )
			.stroke( '#3399ff', 1 );
		this._text = new kity.Text()
			.setSize( 14 )
			.setTextAnchor( 'middle' )
			.fill( 'black' )
			.setStyle( 'cursor', 'default' );
		this.addShapes( [ this._rect, this._text ] );
	},

	// 从选中的节点计算拖放源
	//    并不是所有选中的节点都作为拖放源，如果选中节点中存在 A 和 B，
	//    并且 A 是 B 的祖先，则 B 不作为拖放源
	//
	//    计算过程：
	//       1. 将节点按照树高排序，排序后只可能是前面节点是后面节点的祖先
	//       2. 从后往前枚举排序的结果，如果发现枚举目标之前存在其祖先，
	//          则排除枚举目标作为拖放源，否则加入拖放源
	_calcDragSources: function () {
		this._dragSources = this._minder.getSelectedAncestors();
	},

	// 计算拖放目标可以释放的节点列表（释放意味着成为其子树），存在这条限制规则：
	//    - 不能拖放到拖放目标的子树上（允许拖放到自身，因为多选的情况下可以把其它节点加入）
	//
	//    1. 加入当前节点（初始为根节点）到允许列表
	//    2. 对于当前节点的每一个子节点：
	//       (1) 如果是拖放目标的其中一个节点，忽略（整棵子树被剪枝）
	//       (2) 如果不是拖放目标之一，以当前子节点为当前节点，回到 1 计算
	//    3. 返回允许列表
	//
	_calcDropTargets: function () {

		function findAvailableParents( nodes, root ) {
			var availables = [],
				i;
			availables.push( root );
			root.getChildren().forEach( function ( test ) {
				for ( i = 0; i < nodes.length; i++ ) {
					if ( nodes[ i ] == test ) return;
				}
				availables = availables.concat( findAvailableParents( nodes, test ) );
			} );
			return availables;
		}

		this._dropTargets = findAvailableParents( this._dragSources, this._minder.getRoot() );
		this._dropTargetBoxes = this._dropTargets.map( boxMapper );
	},

	// 进入拖放模式：
	//    1. 计算拖放源和允许的拖放目标
	//    2. 渲染拖放盒子
	//    3. 启动收缩动画
	//    4. 标记已启动
	_enterDragMode: function () {
		this._calcDragSources();
		if ( !this._dragSources.length ) {
			this._startPosition = null;
			return false;
		}
		this._calcDropTargets();
		this._drawForDragMode();
		this._shrink();
		this._dragMode = true;
		return true;
	},
	_leaveDragMode: function () {
		this.remove();
		this._dragMode = false;
		this._dropSucceedTarget = null;
	},
	_drawForDragMode: function () {
		this._text.setContent( this._dragSources.length + ' items' );
		this._text.setPosition( this._startPosition.x, this._startPosition.y + 5 );
		this._minder.getPaper().addShape( this );
	},
	_shrink: function () {
		// 合并所有拖放源图形的矩形即可
		function calcSourceArea( boxArray ) {
			var area = boxArray.pop();
			while ( boxArray.length ) {
				area = GM.mergeBox( area, boxArray.pop() );
			}
			return {
				x: area.left,
				y: area.top,
				width: area.width,
				height: area.height
			};
		}
		// 从焦点发散出一个固定的矩形即可
		function calcFocusArea( focusPoint ) {
			var width = 80,
				height = 30;
			return {
				x: focusPoint.x - width / 2,
				y: focusPoint.y - height / 2,
				width: width,
				height: height
			};
		}

		var sourceArea = calcSourceArea( this._dragSources.map( boxMapper ) );
		var focusArea = calcFocusArea( this._startPosition );
		var animator = new AreaAnimator( sourceArea, focusArea );
		animator.start( this._rect, 400, 'easeOutQuint' );
	},
	// 此处可用线段树优化，但考虑到节点不多，必要性不到，就用暴力测试
	_dropTest: function () {
		var dragBox = this.getRenderBox(),
			test;

		this._dropSucceedTarget = null;
		for ( var i = 0; i < this._dropTargetBoxes.length; i++ ) {
			test = this._dropTargetBoxes[ i ];
			if ( GM.isBoxIntersect( dragBox, test ) ) {
				this._dropSucceedTarget = this._dropTargets[ i ];
				return;
			}
		}
	},
	_updateDropHint: function () {
		var target = this._dropSucceedTarget,
			lastTarget = this._lastSucceedTarget;
		if ( target && target == lastTarget ) return;
		if ( lastTarget ) {
			this._removeDropStyle( lastTarget );
		}
		if ( target ) {
			this._addDropStyle( target );
		}
		this._lastSucceedTarget = target;
	},

	_removeDropStyle: function ( node ) {
		node._layout.bgRect.stroke( 'none' );
		this._rect.stroke( '#3399ff', 1 );
	},

	_addDropStyle: function ( node ) {
		node._layout.bgRect.stroke( 'rgb(254, 219, 0)', 3 );
		node.getRenderContainer().fxScale( 1.25, 1.25, 150, 'ease' ).fxScale( 0.8, 0.8, 150, 'ease' );
	},

	dragStart: function ( position ) {
		// 只记录开始位置，不马上开启拖放模式
		// 这个位置同时是拖放范围收缩时的焦点位置（中心）
		this._startPosition = position;
	},

	dragMove: function ( position ) {
		// 启动拖放模式需要最小的移动距离
		var DRAG_MOVE_THRESHOLD = 10;

		if ( !this._startPosition ) return;

		this._dragPosition = position;

		if ( !this._dragMode ) {
			// 判断拖放模式是否该启动
			if ( GM.getDistance( this._dragPosition, this._startPosition ) < DRAG_MOVE_THRESHOLD ) {
				return;
			}
			if ( !this._enterDragMode() ) {
				return;
			}
		}

		var movement = kity.Vector.fromPoints( this._startPosition, this._dragPosition );

		this.setTransform( new kity.Matrix().translate( movement.x, movement.y ) );

		this._dropTest();
		this._updateDropHint();
	},

	dragEnd: function () {
		this._startPosition = null;
		if ( !this._dragMode ) {
			return;
		}
		if ( this._dropSucceedTarget ) {
			this._minder.execCommand( 'movetoparent', this._dragSources, this._dropSucceedTarget );
		}
		this._leaveDragMode();
	}

} );

KityMinder.registerModule( "DragTree", function () {
	var dragStartPosition, dragBox, dragTargets, dropTargets, dragTargetBoxes, dropTarget;

	return {
		init: function () {
			this._dragBox = new DragBox( this );
		},
		events: {
			mousedown: function ( e ) {
				// 单选中根节点也不触发拖拽
				if ( e.getTargetNode() && e.getTargetNode() != this.getRoot() ) {
					this._dragBox.dragStart( e.getPosition() );
				}
			},
			'mousemove': function ( e ) {
				this._dragBox.dragMove( e.getPosition() );
			},
			'mouseup': function ( e ) {
				this._dragBox.dragEnd();
			}
		},
		commands: {
			'movetoparent': MoveToParentCommand
		}
	};
} );<|endoftext|>


<commit_before>export class NameExpression {
  constructor(name, mode){
    this.property = name;
    this.discrete = true;
    this.mode = mode.replace(/-([a-z])/g, (m, w) => w.toUpperCase());
  }

  createBinding(target){
    return new NameBinder(this.property, target, this.mode);
  }
}

class NameBinder {
  constructor(property, target, mode){
    this.property = property;

    switch (mode) {
      case 'element':
        this.target = target;
        break;
      case 'view-model':
        this.target = target.primaryBehavior.executionContext;
        break;
      default:
        this.target = target[mode];

        if(this.target === undefined){
          throw new Error(`Attempted to reference "${mode}", but it was not found on the target element.`)
        }else{
          this.target = this.target.executionContext || this.target;
        }

        break;
    }
  }

  bind(source){
    if(this.source){
      if(this.source === source){
        return;
      }

      this.unbind();
    }

    this.source = source;
    source[this.property] = this.target;
  }

  unbind(){
    this.source[this.property] = null;
  }
}
<commit_msg>fix(name-expression): no longer transform the mode<commit_after>export class NameExpression {
  constructor(name, mode){
    this.property = name;
    this.discrete = true;
    this.mode = mode;
  }

  createBinding(target){
    return new NameBinder(this.property, target, this.mode);
  }
}

class NameBinder {
  constructor(property, target, mode){
    this.property = property;

    switch (mode) {
      case 'element':
        this.target = target;
        break;
      case 'view-model':
        this.target = target.primaryBehavior.executionContext;
        break;
      default:
        this.target = target[mode];

        if(this.target === undefined){
          throw new Error(`Attempted to reference "${mode}", but it was not found on the target element.`)
        }else{
          this.target = this.target.executionContext || this.target;
        }

        break;
    }
  }

  bind(source){
    if(this.source){
      if(this.source === source){
        return;
      }

      this.unbind();
    }

    this.source = source;
    source[this.property] = this.target;
  }

  unbind(){
    this.source[this.property] = null;
  }
}
<|endoftext|>


<commit_before>/* @flow */
import { REHYDRATE } from 'redux-persist/constants';

import type { NavigationState, Action } from '../types';
import { getFirstIfDeepEqual } from '../utils/immutability';
import { navigateToChat } from './navActions';
import { getStateForRoute, getInitialNavState } from './navSelectors';
import AppNavigator from './AppNavigator';
import {
  INITIAL_FETCH_COMPLETE,
  ACCOUNT_SWITCH,
  LOGIN_SUCCESS,
  LOGOUT,
  SWITCH_NARROW,
} from '../actionConstants';

export default (
  state: NavigationState = getStateForRoute('loading'),
  action: Action,
): NavigationState => {
  switch (action.type) {
    case REHYDRATE:
      return getInitialNavState(action.payload);

    case ACCOUNT_SWITCH:
      return getStateForRoute('loading');

    case LOGIN_SUCCESS:
      return getStateForRoute('main');

    case INITIAL_FETCH_COMPLETE:
      return getFirstIfDeepEqual(
        state,
        getStateForRoute('main'),
        (a, b) => state.routes[0].routeName === 'main',
      );

    case LOGOUT:
      return getStateForRoute('account');

    case SWITCH_NARROW:
      return AppNavigator.router.getStateForAction(navigateToChat(action.narrow), state);

    default:
      return AppNavigator.router.getStateForAction(action, state);
  }
};
<commit_msg>navReducers: Simplify out vacuous use of "getFirstIfDeepEqual".<commit_after>/* @flow */
import { REHYDRATE } from 'redux-persist/constants';

import type { NavigationState, Action } from '../types';
import { navigateToChat } from './navActions';
import { getStateForRoute, getInitialNavState } from './navSelectors';
import AppNavigator from './AppNavigator';
import {
  INITIAL_FETCH_COMPLETE,
  ACCOUNT_SWITCH,
  LOGIN_SUCCESS,
  LOGOUT,
  SWITCH_NARROW,
} from '../actionConstants';

export default (
  state: NavigationState = getStateForRoute('loading'),
  action: Action,
): NavigationState => {
  switch (action.type) {
    case REHYDRATE:
      return getInitialNavState(action.payload);

    case ACCOUNT_SWITCH:
      return getStateForRoute('loading');

    case LOGIN_SUCCESS:
      return getStateForRoute('main');

    case INITIAL_FETCH_COMPLETE:
      return state.routes[0].routeName === 'main' ?
        state : getStateForRoute('main');

    case LOGOUT:
      return getStateForRoute('account');

    case SWITCH_NARROW:
      return AppNavigator.router.getStateForAction(navigateToChat(action.narrow), state);

    default:
      return AppNavigator.router.getStateForAction(action, state);
  }
};
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc provider
 * @name $anchorScrollProvider
 *
 * @description
 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
 * {@link ng.$location#hash $location.hash()} changes.
 */
function $AnchorScrollProvider() {

  var autoScrollingEnabled = true;

  /**
   * @ngdoc method
   * @name $anchorScrollProvider#disableAutoScrolling
   *
   * @description
   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.

   * Use this method to disable automatic scrolling.
   *
   * If automatic scrolling is disabled, one must explicitly call
   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
   * current hash.
   */
  this.disableAutoScrolling = function() {
    autoScrollingEnabled = false;
  };

  /**
   * @ngdoc service
   * @name $anchorScroll
   * @kind function
   * @requires $window
   * @requires $location
   * @requires $rootScope
   *
   * @description
   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
   * in the
   * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
   *
   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
   * match any anchor whenever it changes. This can be disabled by calling
   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
   *
   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
   * vertical scroll-offset (either fixed or dynamic).
   *
   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
   *                       {@link ng.$location#hash $location.hash()} will be used.
   *
   * @property {(number|function|jqLite)} yOffset
   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
   * positioned elements at the top of the page, such as navbars, headers etc.
   *
   * `yOffset` can be specified in various ways:
   * - **number**: A fixed number of pixels to be used as offset.

   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
   *   a number representing the offset (in pixels).

   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
   *   the top of the page to the element's bottom will be used as offset.

   *   **Note**: The element will be taken into account only as long as its `position` is set to
   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
   *   their height and/or positioning according to the viewport's size.
   *
   *

   *

   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
   * not some child element.
   *

   *
   * @example
     <example module="anchorScrollExample">
       <file name="index.html">

           Go to bottom
            You're at the bottom!

       </file>
       <file name="script.js">
         angular.module('anchorScrollExample', [])
           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
             function ($scope, $location, $anchorScroll) {
               $scope.gotoBottom = function() {
                 // set the location.hash to the id of
                 // the element you wish to scroll to.
                 $location.hash('bottom');

                 // call $anchorScroll()
                 $anchorScroll();
               };
             }]);
       </file>
       <file name="style.css">
         #scrollArea {
           height: 280px;
           overflow: auto;
         }

         #bottom {
           display: block;
           margin-top: 2000px;
         }
       </file>
     </example>
   *
   *

   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
   *
   * @example
     <example module="anchorScrollOffsetExample">
       <file name="index.html">

             Go to anchor {{x}}

           Anchor {{x}} of 5

       </file>
       <file name="script.js">
         angular.module('anchorScrollOffsetExample', [])
           .run(['$anchorScroll', function($anchorScroll) {
             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
           }])
           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
             function ($anchorScroll, $location, $scope) {
               $scope.gotoAnchor = function(x) {
                 var newHash = 'anchor' + x;
                 if ($location.hash() !== newHash) {
                   // set the $location.hash to `newHash` and
                   // $anchorScroll will automatically scroll to it
                   $location.hash('anchor' + x);
                 } else {
                   // call $anchorScroll() explicitly,
                   // since $location.hash hasn't changed
                   $anchorScroll();
                 }
               };
             }
           ]);
       </file>
       <file name="style.css">
         body {
           padding-top: 50px;
         }

         .anchor {
           border: 2px dashed DarkOrchid;
           padding: 10px 10px 200px 10px;
         }

         .fixed-header {
           background-color: rgba(0, 0, 0, 0.2);
           height: 50px;
           position: fixed;
           top: 0; left: 0; right: 0;
         }

         .fixed-header > a {
           display: inline-block;
           margin: 5px 15px;
         }
       </file>
     </example>
   */
  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
    var document = $window.document;

    // Helper function to get first anchor from a NodeList
    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
    //  and working in all supported browsers.)
    function getFirstAnchor(list) {
      var result = null;
      Array.prototype.some.call(list, function(element) {
        if (nodeName_(element) === 'a') {
          result = element;
          return true;
        }
      });
      return result;
    }

    function getYOffset() {

      var offset = scroll.yOffset;

      if (isFunction(offset)) {
        offset = offset();
      } else if (isElement(offset)) {
        var elem = offset[0];
        var style = $window.getComputedStyle(elem);
        if (style.position !== 'fixed') {
          offset = 0;
        } else {
          offset = elem.getBoundingClientRect().bottom;
        }
      } else if (!isNumber(offset)) {
        offset = 0;
      }

      return offset;
    }

    function scrollTo(elem) {
      if (elem) {
        elem.scrollIntoView();

        var offset = getYOffset();

        if (offset) {
          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
          // top of the viewport.
          //
          // IF the number of pixels from the top of `elem` to the end of the page's content is less
          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
          // way down the page.
          //
          // This is often the case for elements near the bottom of the page.
          //
          // In such cases we do not need to scroll the whole `offset` up, just the difference between
          // the top of the element and the offset, which is enough to align the top of `elem` at the
          // desired position.
          var elemTop = elem.getBoundingClientRect().top;
          $window.scrollBy(0, elemTop - offset);
        }
      } else {
        $window.scrollTo(0, 0);
      }
    }

    function scroll(hash) {
      hash = isString(hash) ? hash : $location.hash();
      var elm;

      // empty hash, scroll to the top of the page
      if (!hash) scrollTo(null);

      // element with given id
      else if ((elm = document.getElementById(hash))) scrollTo(elm);

      // first anchor with given name :-D
      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);

      // no element and hash == 'top', scroll to the top of the page
      else if (hash === 'top') scrollTo(null);
    }

    // does not scroll when user clicks on anchor link that is currently on
    // (no url change, no $location.hash() change), browser native does scroll
    if (autoScrollingEnabled) {
      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
        function autoScrollWatchAction(newVal, oldVal) {
          // skip the initial scroll if $location.hash is empty
          if (newVal === oldVal && newVal === '') return;

          jqLiteDocumentLoaded(function() {
            $rootScope.$evalAsync(scroll);
          });
        });
    }

    return scroll;
  }];
}
<commit_msg>docs($anchorScroll): fix link to HTML5 spec<commit_after>'use strict';

/**
 * @ngdoc provider
 * @name $anchorScrollProvider
 *
 * @description
 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
 * {@link ng.$location#hash $location.hash()} changes.
 */
function $AnchorScrollProvider() {

  var autoScrollingEnabled = true;

  /**
   * @ngdoc method
   * @name $anchorScrollProvider#disableAutoScrolling
   *
   * @description
   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.

   * Use this method to disable automatic scrolling.
   *
   * If automatic scrolling is disabled, one must explicitly call
   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
   * current hash.
   */
  this.disableAutoScrolling = function() {
    autoScrollingEnabled = false;
  };

  /**
   * @ngdoc service
   * @name $anchorScroll
   * @kind function
   * @requires $window
   * @requires $location
   * @requires $rootScope
   *
   * @description
   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
   * in the
   * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
   *
   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
   * match any anchor whenever it changes. This can be disabled by calling
   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
   *
   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
   * vertical scroll-offset (either fixed or dynamic).
   *
   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
   *                       {@link ng.$location#hash $location.hash()} will be used.
   *
   * @property {(number|function|jqLite)} yOffset
   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
   * positioned elements at the top of the page, such as navbars, headers etc.
   *
   * `yOffset` can be specified in various ways:
   * - **number**: A fixed number of pixels to be used as offset.

   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
   *   a number representing the offset (in pixels).

   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
   *   the top of the page to the element's bottom will be used as offset.

   *   **Note**: The element will be taken into account only as long as its `position` is set to
   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
   *   their height and/or positioning according to the viewport's size.
   *
   *

   *

   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
   * not some child element.
   *

   *
   * @example
     <example module="anchorScrollExample">
       <file name="index.html">

           Go to bottom
            You're at the bottom!

       </file>
       <file name="script.js">
         angular.module('anchorScrollExample', [])
           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
             function ($scope, $location, $anchorScroll) {
               $scope.gotoBottom = function() {
                 // set the location.hash to the id of
                 // the element you wish to scroll to.
                 $location.hash('bottom');

                 // call $anchorScroll()
                 $anchorScroll();
               };
             }]);
       </file>
       <file name="style.css">
         #scrollArea {
           height: 280px;
           overflow: auto;
         }

         #bottom {
           display: block;
           margin-top: 2000px;
         }
       </file>
     </example>
   *
   *

   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
   *
   * @example
     <example module="anchorScrollOffsetExample">
       <file name="index.html">

             Go to anchor {{x}}

           Anchor {{x}} of 5

       </file>
       <file name="script.js">
         angular.module('anchorScrollOffsetExample', [])
           .run(['$anchorScroll', function($anchorScroll) {
             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
           }])
           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
             function ($anchorScroll, $location, $scope) {
               $scope.gotoAnchor = function(x) {
                 var newHash = 'anchor' + x;
                 if ($location.hash() !== newHash) {
                   // set the $location.hash to `newHash` and
                   // $anchorScroll will automatically scroll to it
                   $location.hash('anchor' + x);
                 } else {
                   // call $anchorScroll() explicitly,
                   // since $location.hash hasn't changed
                   $anchorScroll();
                 }
               };
             }
           ]);
       </file>
       <file name="style.css">
         body {
           padding-top: 50px;
         }

         .anchor {
           border: 2px dashed DarkOrchid;
           padding: 10px 10px 200px 10px;
         }

         .fixed-header {
           background-color: rgba(0, 0, 0, 0.2);
           height: 50px;
           position: fixed;
           top: 0; left: 0; right: 0;
         }

         .fixed-header > a {
           display: inline-block;
           margin: 5px 15px;
         }
       </file>
     </example>
   */
  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
    var document = $window.document;

    // Helper function to get first anchor from a NodeList
    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
    //  and working in all supported browsers.)
    function getFirstAnchor(list) {
      var result = null;
      Array.prototype.some.call(list, function(element) {
        if (nodeName_(element) === 'a') {
          result = element;
          return true;
        }
      });
      return result;
    }

    function getYOffset() {

      var offset = scroll.yOffset;

      if (isFunction(offset)) {
        offset = offset();
      } else if (isElement(offset)) {
        var elem = offset[0];
        var style = $window.getComputedStyle(elem);
        if (style.position !== 'fixed') {
          offset = 0;
        } else {
          offset = elem.getBoundingClientRect().bottom;
        }
      } else if (!isNumber(offset)) {
        offset = 0;
      }

      return offset;
    }

    function scrollTo(elem) {
      if (elem) {
        elem.scrollIntoView();

        var offset = getYOffset();

        if (offset) {
          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
          // top of the viewport.
          //
          // IF the number of pixels from the top of `elem` to the end of the page's content is less
          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
          // way down the page.
          //
          // This is often the case for elements near the bottom of the page.
          //
          // In such cases we do not need to scroll the whole `offset` up, just the difference between
          // the top of the element and the offset, which is enough to align the top of `elem` at the
          // desired position.
          var elemTop = elem.getBoundingClientRect().top;
          $window.scrollBy(0, elemTop - offset);
        }
      } else {
        $window.scrollTo(0, 0);
      }
    }

    function scroll(hash) {
      hash = isString(hash) ? hash : $location.hash();
      var elm;

      // empty hash, scroll to the top of the page
      if (!hash) scrollTo(null);

      // element with given id
      else if ((elm = document.getElementById(hash))) scrollTo(elm);

      // first anchor with given name :-D
      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);

      // no element and hash == 'top', scroll to the top of the page
      else if (hash === 'top') scrollTo(null);
    }

    // does not scroll when user clicks on anchor link that is currently on
    // (no url change, no $location.hash() change), browser native does scroll
    if (autoScrollingEnabled) {
      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
        function autoScrollWatchAction(newVal, oldVal) {
          // skip the initial scroll if $location.hash is empty
          if (newVal === oldVal && newVal === '') return;

          jqLiteDocumentLoaded(function() {
            $rootScope.$evalAsync(scroll);
          });
        });
    }

    return scroll;
  }];
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 RedHog (Egil Möller) <egil.moller@freecode.no>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Basic usage:
 *
 * require("./index").require("./examples/foo.ejs")
 */

var ejs = require("ejs");
var fs = require("fs");
var path = require("path");
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
var resolve = require("resolve");

exports.info = {
  __output_stack: [],
  block_stack: [],
  file_stack: [],
  args: []
};

function getCurrentFile() {
  return exports.info.file_stack[exports.info.file_stack.length-1];
}

function createBlockId(name) {
  return getCurrentFile().path + '|' + name;
}

exports._init = function (b, recursive) {
  exports.info.__output_stack.push(exports.info.__output);
  exports.info.__output = b;
}

exports._exit = function (b, recursive) {
  getCurrentFile().inherit.forEach(function (item) {
    exports._require(item.name, item.args);
  });
  exports.info.__output = exports.info.__output_stack.pop();
}

exports.begin_capture = function() {
  exports.info.__output_stack.push(exports.info.__output.concat());
  exports.info.__output.splice(0, exports.info.__output.length);
}

exports.end_capture = function () {
  var res = exports.info.__output.join("");
  exports.info.__output.splice.apply(
    exports.info.__output,
    [0, exports.info.__output.length].concat(exports.info.__output_stack.pop()));
  return res;
}

exports.begin_define_block = function (name) {
  exports.info.block_stack.push(name);
  exports.begin_capture();
}

exports.end_define_block = function () {
  var content = exports.end_capture();
  return content;
}

exports.end_block = function () {
  var name = exports.info.block_stack.pop();
  var renderContext = exports.info.args[exports.info.args.length-1];
  var args = {content: exports.end_define_block(), renderContext: renderContext};
  hooks.callAll("eejsBlock_" + name, args);
  exports.info.__output.push(args.content);
}

exports.begin_block = exports.begin_define_block;

exports.inherit = function (name, args) {
    getCurrentFile().inherit.push({name:name, args:args});
}

exports.require = function (name, args, mod) {
  if (args == undefined) args = {};

  var basedir = __dirname;
  var paths = [];

  if (exports.info.file_stack.length) {
    basedir = path.dirname(getCurrentFile().path);
  }
  if (mod) {
    basedir = path.dirname(mod.filename);
    paths = mod.paths;
  }

  var ejspath = resolve.sync(
    name,
    {
      paths : paths,
      basedir : basedir,
      extensions : [ '.html', '.ejs' ],
    }
  )

  args.e = exports;
  args.require = require;
  var template = '<% e._init(__output); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>';

  exports.info.args.push(args);
  exports.info.file_stack.push({path: ejspath, inherit: []});

  var res = ejs.render(template, args);
  exports.info.file_stack.pop();
  exports.info.args.pop();

  return res;
}

exports._require = function (name, args) {
  exports.info.__output.push(exports.require(name, args));
}
<commit_msg>performance: add template cache for quicker page renders<commit_after>/*
 * Copyright (c) 2011 RedHog (Egil Möller) <egil.moller@freecode.no>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Basic usage:
 *
 * require("./index").require("./examples/foo.ejs")
 */

var ejs = require("ejs");
var fs = require("fs");
var path = require("path");
var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js");
var resolve = require("resolve");

const templateCache = new Map()

exports.info = {
  __output_stack: [],
  block_stack: [],
  file_stack: [],
  args: []
};

function getCurrentFile() {
  return exports.info.file_stack[exports.info.file_stack.length-1];
}

function createBlockId(name) {
  return getCurrentFile().path + '|' + name;
}

exports._init = function (b, recursive) {
  exports.info.__output_stack.push(exports.info.__output);
  exports.info.__output = b;
}

exports._exit = function (b, recursive) {
  getCurrentFile().inherit.forEach(function (item) {
    exports._require(item.name, item.args);
  });
  exports.info.__output = exports.info.__output_stack.pop();
}

exports.begin_capture = function() {
  exports.info.__output_stack.push(exports.info.__output.concat());
  exports.info.__output.splice(0, exports.info.__output.length);
}

exports.end_capture = function () {
  var res = exports.info.__output.join("");
  exports.info.__output.splice.apply(
    exports.info.__output,
    [0, exports.info.__output.length].concat(exports.info.__output_stack.pop()));
  return res;
}

exports.begin_define_block = function (name) {
  exports.info.block_stack.push(name);
  exports.begin_capture();
}

exports.end_define_block = function () {
  var content = exports.end_capture();
  return content;
}

exports.end_block = function () {
  var name = exports.info.block_stack.pop();
  var renderContext = exports.info.args[exports.info.args.length-1];
  var args = {content: exports.end_define_block(), renderContext: renderContext};
  hooks.callAll("eejsBlock_" + name, args);
  exports.info.__output.push(args.content);
}

exports.begin_block = exports.begin_define_block;

exports.inherit = function (name, args) {
    getCurrentFile().inherit.push({name:name, args:args});
}

exports.require = function (name, args, mod) {
  if (args == undefined) args = {};

  var basedir = __dirname;
  var paths = [];

  if (exports.info.file_stack.length) {
    basedir = path.dirname(getCurrentFile().path);
  }
  if (mod) {
    basedir = path.dirname(mod.filename);
    paths = mod.paths;
  }

  var ejspath = resolve.sync(
    name,
    {
      paths : paths,
      basedir : basedir,
      extensions : [ '.html', '.ejs' ],
    }
  )

  args.e = exports;
  args.require = require;

  let template
  if (!templateCache.has(ejspath)) {
    template = '<% e._init(__output); %>' + fs.readFileSync(ejspath).toString() + '<% e._exit(); %>';
    templateCache.set(ejspath, template)
  } else {
    template = templateCache.get(ejspath)
  }

  exports.info.args.push(args);
  exports.info.file_stack.push({path: ejspath, inherit: []});

  var res = ejs.render(template, args, { cache: true, filename: ejspath });
  exports.info.file_stack.pop();
  exports.info.args.pop();

  return res;
}

exports._require = function (name, args) {
  exports.info.__output.push(exports.require(name, args));
}
<|endoftext|>


<commit_before>/*global Parser:true*/
// extends src/nodes/image.js which extends src/node-box-native-view.js

$(function(){

  var template = '

';

  Iframework.NativeNodes["util-math"] = Iframework.NativeNodes["util"].extend({

    template: _.template(template),
    info: {
      title: "math",
      description: "math equation evaluator"
    },
    initializeModule: function(){
      var self = this;
      // WTF Firefox has an empty Parser object?
      if (Parser && Parser.parse) {
        // Ready
        if (this._equation) {
          this.inputequation(this._equation);
        }
      } else {
        yepnope({
          load: "libs/js-expression-eval/parser.js",
          complete: function () {
            self.initializeModule();
          }
        });
      }
    },
    _expression: null,
    inputequation: function(s) {
      if (Parser && Parser.parse) {
        try {
          this._expression = Parser.parse(s);
          // For some reason immediate evaluate() without values causes this to throw
          var self = this;
          window.setTimeout(function(){
            self.inputevaluate();
          }, 500);

          this.$(".equation")
            .attr("title", this._expression.toString())
            .text(s + " = ");
        } catch (e) {
          this._expression = null;
          this.$(".equation").text("error parsing equation");
          this.$(".solution").text("");
        }
      } else {
        // Wait for library to load
        this._equation = s;
      }
    },
    inputevaluate: function() {
      if (this._expression) {
        try {
          var solution = this._expression.evaluate({
            x: this._x,
            y: this._y,
            z: this._z
          });
          if (solution===solution) {
            // not NaN
            this.send("equals", solution);
          }
          this.$(".solution").text(solution);
        } catch (e) {
          this.$(".solution").text("error evaluating; got variables?");
        }
      }
    },
    redraw: function(timestamp){
      // Called from NodeBoxNativeView.renderAnimationFrame()
      this.inputevaluate();
    },
    inputs: {
      x: {
        type: "float",
        "default": 0
      },
      y: {
        type: "float"
      },
      z: {
        type: "float"
      },
      equation: {
        type: "string",
        description: "f(x,y,z) (function calls), ^ (exponentiation), *, /, and % (multiplication, division, and remainder), and finally +, -, and || (addition, subtraction, and string concatenation) ... sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), sqrt(x), log(x), abs(x), ceil(x), floor(x), round(x), exp(x) ... random(n), fac(n), min(a,b,…), max(a,b,…), pyt(a, b), pow(x, y), atan2(y, x)",
        "default": "x"
      },
      evaluate: {
        type: "bang",
        description: "evaluates the equation"
      }
    },
    outputs: {
      equals: {
        type: "float"
      }
    }

  });

});
<commit_msg>needed that window dot<commit_after>/*global Parser:true*/
// extends src/nodes/image.js which extends src/node-box-native-view.js

$(function(){

  var template = '

';

  Iframework.NativeNodes["util-math"] = Iframework.NativeNodes["util"].extend({

    template: _.template(template),
    info: {
      title: "math",
      description: "math equation evaluator"
    },
    initializeModule: function(){
      var self = this;
      // WTF Firefox has an empty Parser object?
      if (window.Parser && Parser.parse) {
        // Ready
        if (this._equation) {
          this.inputequation(this._equation);
        }
      } else {
        yepnope({
          load: "libs/js-expression-eval/parser.js",
          complete: function () {
            self.initializeModule();
          }
        });
      }
    },
    _expression: null,
    inputequation: function(s) {
      if (window.Parser && Parser.parse) {
        try {
          this._expression = Parser.parse(s);
          // For some reason immediate evaluate() without values causes this to throw
          var self = this;
          window.setTimeout(function(){
            self.inputevaluate();
          }, 500);

          this.$(".equation")
            .attr("title", this._expression.toString())
            .text(s + " = ");
        } catch (e) {
          this._expression = null;
          this.$(".equation").text("error parsing equation");
          this.$(".solution").text("");
        }
      } else {
        // Wait for library to load
        this._equation = s;
      }
    },
    inputevaluate: function() {
      if (this._expression) {
        try {
          var solution = this._expression.evaluate({
            x: this._x,
            y: this._y,
            z: this._z
          });
          if (solution===solution) {
            // not NaN
            this.send("equals", solution);
          }
          this.$(".solution").text(solution);
        } catch (e) {
          this.$(".solution").text("error evaluating; got variables?");
        }
      }
    },
    redraw: function(timestamp){
      // Called from NodeBoxNativeView.renderAnimationFrame()
      this.inputevaluate();
    },
    inputs: {
      x: {
        type: "float",
        "default": 0
      },
      y: {
        type: "float"
      },
      z: {
        type: "float"
      },
      equation: {
        type: "string",
        description: "f(x,y,z) (function calls), ^ (exponentiation), *, /, and % (multiplication, division, and remainder), and finally +, -, and || (addition, subtraction, and string concatenation) ... sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), sqrt(x), log(x), abs(x), ceil(x), floor(x), round(x), exp(x) ... random(n), fac(n), min(a,b,…), max(a,b,…), pyt(a, b), pow(x, y), atan2(y, x)",
        "default": "x"
      },
      evaluate: {
        type: "bang",
        description: "evaluates the equation"
      }
    },
    outputs: {
      equals: {
        type: "float"
      }
    }

  });

});
<|endoftext|>


<commit_before>/* @flow weak */

/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

 /* @flow weak */

 /**
  * OfflineMobile Android Index
  * Sustainable Solutions (NZ) Ltd. 2016
  */

import React, {
 Component,
 StyleSheet,
 Text,
 TextInput,
 View,
} from 'react-native';

import {
 Cell,
 DataTable,
 Expansion,
 Header,
 HeaderCell,
 Row,
} from '../widgets/DataTable';

import DropDown from 'react-native-dropdown';
const {
  Option,
  OptionList,
  Select,
  UpdatePosition,
} = DropDown;

import { getItemQuantity } from '../utilities';
import { ListView } from 'realm/react-native';
import globalStyles from '../globalStyles';

export default class StockPage extends Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource,
      query: 'item_name=@',
      items: props.database.objects('Item'),
      sortBy: 'name',
      reverseSort: false,
    };
    this.componentWillMount = this.componentWillMount.bind(this);
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderExpansion = this.renderExpansion.bind(this);
    this.renderRow = this.renderRow.bind(this);
  }

  componentWillMount() {
    const data = this.state.items.sorted(this.state.sortBy);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data),
      deleteTargetItem: this.state.items[0],
    });
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    const { items, sortBy, dataSource, reverseSort } = this.state;
    const data = items.filtered(`${sortBy} CONTAINS[c] $0`, term).sorted(sortBy, reverseSort);
    this.setState({
      dataSource: dataSource.cloneWithRows(data),
    });
  }

  onColumnSort() {
    this.setState({
      reverseSort: this.state.reverseSort !== true,
    });
    const data = this.state.items.sorted(this.state.sortBy, this.state.reverseSort);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data),
    });
  }

  renderHeader() {
    return (
      <Header style={globalStyles.dataTableHeader}>
        <HeaderCell
          style={[globalStyles.dataTableCell, globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          onPress={() => this.onColumnSort('code')}
          width={columnWidths[0]}
          text={'ITEM CODE'}
        />
        <HeaderCell
          style={[globalStyles.dataTableCell, globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[1]}
          onPress={() => this.onColumnSort('name')}
          text={'ITEM NAME'}
        />
        <HeaderCell
          style={[globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[2]}
          text={'STOCK ON HAND'}
        />
      </Header>
    );
  }

  renderExpansion(item) {
    return (
      <Expansion>
        <View style={{ flex: columnWidths[0] }} />
        <View style={{ flex: columnWidths[1], flexDirection: 'row' }}>
          <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-around' }}>
            <Text style={[globalStyles.text, styles.text]}>Category: {item.category.name}</Text>
            <Text style={[globalStyles.text, styles.text]}>Department: {item.department.name}</Text>
          </View>
          <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-around' }}>
            <Text style={[globalStyles.text, styles.text]}>
              Number of batches: {item.lines.length}
            </Text>
            <Text style={[globalStyles.text, styles.text]}>
              Nearest expiry: value
            </Text>
          </View>
        </View>
        <View style={{ flex: columnWidths[2] }} />
      </Expansion>
    );
  }

  renderRow(item) {
    return (
      <Row style={globalStyles.dataTableRow} renderExpansion={() => this.renderExpansion(item)}>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[0]}
        >
          {item.code}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[1]}
        >
          {item.name}
        </Cell>
        <Cell
          style={[globalStyles.dataTableCell, styles.cellLast]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[2]}
        >
          {getItemQuantity(item)}
        </Cell>
      </Row>
    );
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={styles.horizontalContainer}>
          <TextInput
            style={[globalStyles.dataTableSearchBar, { flex: 1 }]}
            onChange={(event) => this.onSearchChange(event)}
            placeholder="Search"
          />
          <Select
            style={[globalStyles.dataTableDropDown, { flex: 0.5 }]}
            defaultValue={'Category'}
          >
          </Select>
        </View>
        <DataTable
          style={globalStyles.dataTable}
          listViewStyle={globalStyles.container}
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          renderHeader={this.renderHeader}
        />
      </View>
    );
  }
}

StockPage.propTypes = {
  database: React.PropTypes.object,
  style: View.propTypes.style,
};
const columnWidths = [1.3, 7.2, 1.6];
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  horizontalContainer: {
    flexDirection: 'row',
  },
  text: {
    fontSize: 20,
    marginLeft: 20,
    textAlign: 'left',
  },
  cellLast: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<commit_msg>category state implemented<commit_after>/* @flow weak */

/**
 * OfflineMobile Android Index
 * Sustainable Solutions (NZ) Ltd. 2016
 */

 /* @flow weak */

 /**
  * OfflineMobile Android Index
  * Sustainable Solutions (NZ) Ltd. 2016
  */

import React, {
 Component,
 StyleSheet,
 Text,
 TextInput,
 View,
} from 'react-native';

import {
 Cell,
 DataTable,
 Expansion,
 Header,
 HeaderCell,
 Row,
} from '../widgets/DataTable';

import DropDown from 'react-native-dropdown';
const {
  Option,
  OptionList,
  Select,
  UpdatePosition,
} = DropDown;

import { getItemQuantity } from '../utilities';
import { ListView } from 'realm/react-native';
import globalStyles from '../globalStyles';

export default class StockPage extends Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource,
      query: 'item_name=@',
      items: props.database.objects('Item'),
      category: '',
      sortBy: 'name',
      reverseSort: false,
    };
    this.componentWillMount = this.componentWillMount.bind(this);
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderExpansion = this.renderExpansion.bind(this);
    this.renderRow = this.renderRow.bind(this);
  }

  componentWillMount() {
    const data = this.state.items.sorted(this.state.sortBy);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data),
      deleteTargetItem: this.state.items[0],
    });
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    const { items, sortBy, category, dataSource, reverseSort } = this.state;
    const data = items
      .filtered(`category.name CONTAINS[c] $0 && ${sortBy} CONTAINS[c] $1`, category, term)
      .sorted(sortBy, reverseSort);
    this.setState({
      dataSource: dataSource.cloneWithRows(data),
    });
  }

  onColumnSort() {
    this.setState({
      reverseSort: this.state.reverseSort !== true,
    });
    const data = this.state.items.sorted(this.state.sortBy, this.state.reverseSort);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(data),
    });
  }

  renderHeader() {
    return (
      <Header style={globalStyles.dataTableHeader}>
        <HeaderCell
          style={[globalStyles.dataTableCell, globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          onPress={() => this.onColumnSort('code')}
          width={columnWidths[0]}
          text={'ITEM CODE'}
        />
        <HeaderCell
          style={[globalStyles.dataTableCell, globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[1]}
          onPress={() => this.onColumnSort('name')}
          text={'ITEM NAME'}
        />
        <HeaderCell
          style={[globalStyles.dataTableHeaderCell]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[2]}
          text={'STOCK ON HAND'}
        />
      </Header>
    );
  }

  renderExpansion(item) {
    return (
      <Expansion>
        <View style={{ flex: columnWidths[0] }} />
        <View style={{ flex: columnWidths[1], flexDirection: 'row' }}>
          <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-around' }}>
            <Text style={[globalStyles.text, styles.text]}>Category: {item.category.name}</Text>
            <Text style={[globalStyles.text, styles.text]}>Department: {item.department.name}</Text>
          </View>
          <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-around' }}>
            <Text style={[globalStyles.text, styles.text]}>
              Number of batches: {item.lines.length}
            </Text>
            <Text style={[globalStyles.text, styles.text]}>
              Nearest expiry: value
            </Text>
          </View>
        </View>
        <View style={{ flex: columnWidths[2] }} />
      </Expansion>
    );
  }

  renderRow(item) {
    return (
      <Row style={globalStyles.dataTableRow} renderExpansion={() => this.renderExpansion(item)}>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[0]}
        >
          {item.code}
        </Cell>
        <Cell
          style={globalStyles.dataTableCell}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[1]}
        >
          {item.name}
        </Cell>
        <Cell
          style={[globalStyles.dataTableCell, styles.cellLast]}
          textStyle={[globalStyles.text, styles.text]}
          width={columnWidths[2]}
        >
          {getItemQuantity(item)}
        </Cell>
      </Row>
    );
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={styles.horizontalContainer}>
          <TextInput
            style={[globalStyles.dataTableSearchBar, { flex: 1 }]}
            onChange={(event) => this.onSearchChange(event)}
            placeholder="Search"
          />
          <Select
            style={[globalStyles.dataTableDropDown, { flex: 0.5 }]}
            defaultValue={'Category'}
          >
            {

            }
          </Select>
        </View>
        <DataTable
          style={globalStyles.dataTable}
          listViewStyle={globalStyles.container}
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          renderHeader={this.renderHeader}
        />
      </View>
    );
  }
}

StockPage.propTypes = {
  database: React.PropTypes.object,
  style: View.propTypes.style,
};
const columnWidths = [1.3, 7.2, 1.6];
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  horizontalContainer: {
    flexDirection: 'row',
  },
  text: {
    fontSize: 20,
    marginLeft: 20,
    textAlign: 'left',
  },
  cellLast: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<|endoftext|>