<commit_before>import helpers from 'glView-helpers'
let annotations = helpers.annotations
import THREE from 'three'

//method to filter the source data by types : types are arrays of typeUids
export function obsByTypes(srcData, types){
  return srcData
    .flatMap(function(items){
      //console.log("items",items)
      return Rx.Observable.from(items)
    })
    .filter(x=> types.indexOf(x.type) > -1 )
}

//mesh insertion post process
export function meshInjectPostProcess( mesh ){
  //FIXME: not sure about these, they are used for selection levels
  mesh.selectable      = true
  mesh.selectTrickleUp = false
  mesh.transformable   = true
  //FIXME: not sure, these are very specific for visuals
  mesh.castShadow      = true
  //mesh.receiveShadow = true
  return mesh
}

export function applyEntityPropsToMesh( inputs ){
  let {entity, mesh} = inputs
  mesh.userData.entity = entity//FIXME : should we have this sort of backlink ?
  //FIXME/ make a list of all operations needed to be applied on part meshes
  mesh.position.fromArray( entity.pos )
  mesh.rotation.fromArray( entity.rot )
  mesh.scale.fromArray(  entity.sca )
  mesh.material.color.set( entity.color )
  return mesh
}

function meshesFromDeps(deps, getVisual, entities$){
  /*let observables = deps
    .map(getEntityByIuid)
    .map(getVisual)
    .map(s=>s.take(1))//only need one, also, otherwise, forkjoin will not fire
  //return Rx.Observable.forkJoin( observables )*/

  return Rx.Observable.just(null)//how can I not use this one ?
    .combineLatest(entities$.pluck("byId"),function(x,byId){
      //console.log("byId",byId,deps)
      return deps
        .map(d=>byId[d])
        .filter(x=>x!==undefined)
        .map(getVisual)
        .map(s=>s.take(1))
    })
    .do(e=>console.log("got some data",e))
    .flatMap(Rx.Observable.forkJoin)
    .do(e=>console.log("got some data2",e))
    //.subscribe(x=>console.log("deps",x))

}

/*provides visual mapping to "arbitrary" 3d meshes*/
function remoteMeshVisualProvider(entity, subJ, getVisual, types$){
  //console.log("return mesh")
  types$.subscribe(
    function(types){
      //console.log("TYPES",types)
      let originalMesh = types.typeUidToTemplateMesh[entity.typeUid]
      if(originalMesh){
        let mesh = originalMesh.clone()
        let material = originalMesh.material.clone()

        mesh.boundingBox = originalMesh.boundingBox //because clone() does not clone custom attributes
        mesh.boundingSphere = originalMesh.boundingSphere
        mesh.material = material

        mesh = meshInjectPostProcess(mesh)
        mesh = applyEntityPropsToMesh({entity,mesh})
        subJ.onNext(mesh)
      }
    },e=>console.error(e))
  //subJ.onCompleted("mesh completed")
}

/*this one is used for "static"/pre determined visuals, like for annotations*/
function staticVisualProvider(entity, subJ, getVisual){
  console.log("staticVisualProvider",entity,subJ)

  meshesFromDeps(entity.deps, getVisual)
    .subscribe(function(vO){
      subJ.onNext("in progress static for "+entity.name+" based on "+entity.deps)
    },e=>console.log("error",e),e=>console.log("DONE with observables"))
}

//wrapper function
export function createVisualMapper(types$, entities$){

  //ugh !! this is needed to be OUTSIDE the scope of "getVisual" otherwise, each call returns a new instance
  //of the cache : ie NO CACHE
  let iuidToMesh = {}
  let typeUidToTemplateMesh = {}

  function getVisual(entity){
    console.log("getting visual")

    //now each resolver  needs to fire "onNext on this subject"
    let subJ = new Rx.ReplaySubject()
    let {iuid, typeUid} = entity

    //what we want as "user" is a refined, updated result
    //note: is this always the case or only with geometry?
    function mod(mesh){
      console.log("oh great a mesh to change")
      return mesh
    }

    function cache(mesh){
      if(!iuidToMesh[iuid]){//needed ?
        console.log("caching mesh",mesh, "total cache",iuidToMesh)
        iuidToMesh[entity.iuid] = mesh
      }
    }

    if(!iuidToMesh[iuid]){
      let entities = [entity]

      //obsByTypes(entities,["A0"]).map()
      if(entity.typeUid === "A0") remoteMeshVisualProvider(entity, subJ, getVisual, types$)
      if(entity.typeUid === "A1") noteVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A2") thicknessVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A3") diameterVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A4") distanceVisualProvider(entity, subJ, getVisual, entities$)

    }else{
      console.log("reusing mesh from cache by iuid",iuid)
      let mesh = iuidToMesh[iuid]
      mesh = applyEntityPropsToMesh({entity,mesh})
      subJ.onNext(mesh)
    }

    return subJ.do(cache)//.map(mod)
  }

  function addVisualProvider(types,provider,postProcesses){

  }

  return {getVisual,addVisualProvider}
}

//annotations require 1...n preloaded meshes

  let annotStyle = {
    crossColor:"#000",
    textColor:"#000",
    lineColor:"#000",
    arrowColor:"#000",
    lineWidth:2.2,
    highlightColor: "#60C4F8",//"#00F",
    fontFace:"Open Sans"
  }

function noteVisualProvider(entity, subJ, getVisual, entities$){
  console.log("note annot",entity)
  let point = entity.target.point
  let deps = [entity.target.iuid]

  function visual(mesh){
    //mesh.updateMatrix()
    //mesh.updateMatrixWorld()
    let pt = new THREE.Vector3().fromArray(point)//.add(mesh.position)
    pt = mesh.localToWorld(pt)

    let params = {
      point:pt,
      object:mesh}
    params = Object.assign(params,annotStyle)

    return new annotations.NoteVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

function thicknessVisualProvider(entity, subJ, getVisual, entities$){
  let deps = [entity.target.iuid]
  let entryPoint = entity.target.entryPoint
  let exitPoint  = entity.target.exitPoint

  function visual(mesh){

    entryPoint= new THREE.Vector3().fromArray(entryPoint)
    exitPoint = new THREE.Vector3().fromArray(exitPoint)
    entryPoint = mesh.localToWorld(entryPoint)
    exitPoint = mesh.localToWorld(exitPoint)

    let params = {
      entryPoint,
      exitPoint,
      object:mesh
    }
    params = Object.assign(params,annotStyle)
    return new annotations.ThicknessVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

function distanceVisualProvider(entity, subJ, getVisual, entities$){
  let start = entity.target.start
  let end = entity.target.end

  let deps = [start.iuid, end.iuid]

  function visual(startMesh, endMesh){
    let startPt = new THREE.Vector3().fromArray(start.point)
    let endPt   = new THREE.Vector3().fromArray(end.point)
    startMesh.localToWorld(startPt)
    endMesh.localToWorld(endPt)
    //startMesh.worldToLocal(startPt)
    //endMesh.worldToLocal(endPt)

    let params = {
      start:startPt,
      startObject:startMesh,
      end: endPt,
      endObject: endMesh
    }
    params = Object.assign(params, annotStyle)

    return new annotations.DistanceVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0],data[1]))
    })
}

function diameterVisualProvider(entity, subJ, getVisual, entities$){
  let point = entity.target.point
  let normal = entity.target.normal
  let diameter = entity.value
  let deps = [entity.target.iuid]

  function visual(mesh){
    point    = new THREE.Vector3().fromArray(point)
    normal   = new THREE.Vector3().fromArray(normal)
    //mesh.updateMatrix()
    //mesh.updateMatrixWorld()
    point = mesh.localToWorld(point)

    let params = {
         center:point,
         diameter,
         orientation:normal
      }
    params = Object.assign(params,annotStyle)

    return new annotations.DiameterVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

/*for testing
let fakeEntities$= Rx.Observable.just({
    byId:{
        5:{typeUid:"A0",iuid:5,name:"PART1",pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
        2: {typeUid:"A0",iuid:2,name:"PART2",pos:[0,0,40],rot:[0,45,0],sca:[1,1,1]},
        7: {typeUid:"A0",iuid:7,name:"PART3",pos:[10,-20,0],rot:[0,0,0],sca:[1,1,1]},

        10:{typeUid:"A1",iuid:10,name:"ANNOT3",deps:[5,2,7],                     pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
        11:{typeUid:"A2",iuid:11,name:"Note ANNOT",value:"some text", deps:[2],  pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
          target:{point:[10,5,0],iuid:2}
        },
      }
    })
    .shareReplay(1)

  let {getVisual,addVisualProvider } = createVisualMapper(partTypes$, fakeEntities$)

  Rx.Observable.from([
    {typeUid:"A0",iuid:5,name:"PART1",pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
    {typeUid:"A0",iuid:2,name:"PART2",pos:[0,0,40],rot:[0,45,0],sca:[1,1,1]},
    {typeUid:"A0",iuid:7,name:"PART3",pos:[10,-20,0],rot:[0,0,0],sca:[1,1,1]},
    //{typeUid:"A1",iuid:10,name:"ANNOT3",deps:[5,2,7],pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
    {typeUid:"A1",iuid:11,name:"Note ANNOT",value:"some text",
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{point:[10,5,0],iuid:2}
    },
    {typeUid:"A2",iuid:12,name:"thickness ANNOT",value:150.45,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{entryPoint:[10,5,0], exitPoint:[0,-7.2,19],iuid:5}
    },
    {typeUid:"A3",iuid:13,name:"Diameter ANNOT",value:34.09,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{point:[10,5,0],normal:[1,0,0],iuid:7}
    },
    {typeUid:"A4",iuid:14,name:"distance ANNOT",value:56.22,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{
        start:{point:[10,5,0],iuid:2},
        end:{point:[0,-7.2,19],iuid:5}
      }
    }
  ])
    .map(getVisual)
    .subscribe(function(vO){
      vO.subscribe(v2=>console.log("visuals",v2),e=>e,v3=>console.log("visuals done",v3))
    })
 */<commit_msg>- updated mapping system :  * visualProviders can now be set dynamically  * modified parameters for visual providers to allow for variable params<commit_after>import helpers from 'glView-helpers'
let annotations = helpers.annotations
import THREE from 'three'

//method to filter the source data by types : types are arrays of typeUids
export function obsByTypes(srcData, types){
  return srcData
    .flatMap(function(items){
      //console.log("items",items)
      return Rx.Observable.from(items)
    })
    .filter(x=> types.indexOf(x.type) > -1 )
}

//mesh insertion post process
export function meshInjectPostProcess( mesh ){
  //FIXME: not sure about these, they are used for selection levels
  mesh.selectable      = true
  mesh.selectTrickleUp = false
  mesh.transformable   = true
  //FIXME: not sure, these are very specific for visuals
  mesh.castShadow      = true
  //mesh.receiveShadow = true
  return mesh
}

export function applyEntityPropsToMesh( inputs ){
  let {entity, mesh} = inputs
  mesh.userData.entity = entity//FIXME : should we have this sort of backlink ?
  //FIXME/ make a list of all operations needed to be applied on part meshes
  mesh.position.fromArray( entity.pos )
  mesh.rotation.fromArray( entity.rot )
  mesh.scale.fromArray(  entity.sca )
  mesh.material.color.set( entity.color )
  return mesh
}

function meshesFromDeps(deps, getVisual, entities$){
  /*let observables = deps
    .map(getEntityByIuid)
    .map(getVisual)
    .map(s=>s.take(1))//only need one, also, otherwise, forkjoin will not fire
  //return Rx.Observable.forkJoin( observables )*/

  return Rx.Observable.just(null)//how can I not use this one ?
    .combineLatest(entities$.pluck("byId"),function(x,byId){
      //console.log("byId",byId,deps)
      return deps
        .map(d=>byId[d])
        .filter(x=>x!==undefined)
        .map(getVisual)
        .map(s=>s.take(1))
    })
    .do(e=>console.log("got some data",e))
    .flatMap(Rx.Observable.forkJoin)
    .do(e=>console.log("got some data2",e))
    //.subscribe(x=>console.log("deps",x))

}

/*provides visual mapping to "arbitrary" 3d meshes*/
function remoteMeshVisualProvider(entity, subJ, params){
  let {types$} = params
  //console.log("return mesh")
  types$.subscribe(
    function(types){
      //console.log("TYPES",types)
      let originalMesh = types.typeUidToTemplateMesh[entity.typeUid]
      if(originalMesh){
        let mesh = originalMesh.clone()
        let material = originalMesh.material.clone()

        mesh.boundingBox = originalMesh.boundingBox //because clone() does not clone custom attributes
        mesh.boundingSphere = originalMesh.boundingSphere
        mesh.material = material

        mesh = meshInjectPostProcess(mesh)
        mesh = applyEntityPropsToMesh({entity,mesh})
        subJ.onNext(mesh)
      }
    },e=>console.error(e))
  //subJ.onCompleted("mesh completed")
}

/*this one is used for "static"/pre determined visuals, like for annotations*/
function staticVisualProvider(entity, subJ, getVisual){
  console.log("staticVisualProvider",entity,subJ)

  meshesFromDeps(entity.deps, getVisual)
    .subscribe(function(vO){
      subJ.onNext("in progress static for "+entity.name+" based on "+entity.deps)
    },e=>console.log("error",e),e=>console.log("DONE with observables"))
}

//wrapper function
export function createVisualMapper(types$, entities$){

  //ugh !! this is needed to be OUTSIDE the scope of "getVisual" otherwise, each call returns a new instance
  //of the cache : ie NO CACHE
  let iuidToMesh = {}
  let typeUidToTemplateMesh = {}
  let visualProviders = {}

  visualProviders["A0"] = remoteMeshVisualProvider //(entity, subJ, getVisual, types$)
  visualProviders["A1"] = noteVisualProvider //(entity, subJ, getVisual, entities$)
  visualProviders["A2"] = thicknessVisualProvider //(entity, subJ, getVisual, entities$)
  visualProviders["A3"] = diameterVisualProvider //(entity, subJ, getVisual, entities$)
  visualProviders["A4"] = distanceVisualProvider //(entity, subJ, getVisual, entities$)

  function getVisual(entity){
    console.log("getting visual")

    //now each resolver  needs to fire "onNext on this subject"
    let subJ = new Rx.ReplaySubject()
    let {iuid, typeUid} = entity

    //what we want as "user" is a refined, updated result
    //note: is this always the case or only with geometry?
    function mod(mesh){
      console.log("oh great a mesh to change")
      return mesh
    }

    function cache(mesh){
      if(!iuidToMesh[iuid]){//needed ?
        console.log("caching mesh",mesh, "total cache",iuidToMesh)
        iuidToMesh[entity.iuid] = mesh
      }
    }

    if(!iuidToMesh[iuid]){
      let entities = [entity]
      let typeUid = entity.typeUid
      let visualProvider = visualProviders[typeUid]

      let params = {getVisual,types$,entities$}
      //TODO: some providers need the "types$" , others need entities$, needs to be specifiable
      if(visualProvider){
        visualProvider(entity, subJ, params)
      }
      //obsByTypes(entities,["A0"]).map()
      /*if(entity.typeUid === "A0") remoteMeshVisualProvider(entity, subJ, getVisual, types$)
      if(entity.typeUid === "A1") noteVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A2") thicknessVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A3") diameterVisualProvider(entity, subJ, getVisual, entities$)
      if(entity.typeUid === "A4") distanceVisualProvider(entity, subJ, getVisual, entities$) */

    }else{
      console.log("reusing mesh from cache by iuid",iuid)
      let mesh = iuidToMesh[iuid]
      mesh = applyEntityPropsToMesh({entity,mesh})
      subJ.onNext(mesh)
    }

    return subJ.do(cache)//.map(mod)
  }

  function addVisualProvider(type,provider,extraParam){
    visualProviders[type]=provider
  }

  return {getVisual,addVisualProvider}
}

//annotations require 1...n preloaded meshes

  let annotStyle = {
    crossColor:"#000",
    textColor:"#000",
    lineColor:"#000",
    arrowColor:"#000",
    lineWidth:2.2,
    highlightColor: "#60C4F8",//"#00F",
    fontFace:"Open Sans"
  }

function noteVisualProvider(entity, subJ, params){
  console.log("note annot",entity)
  let {getVisual,entities$} = params
  let point = entity.target.point
  let deps = [entity.target.iuid]

  function visual(mesh){
    //mesh.updateMatrix()
    //mesh.updateMatrixWorld()
    let pt = new THREE.Vector3().fromArray(point)//.add(mesh.position)
    pt = mesh.localToWorld(pt)

    let params = {
      point:pt,
      object:mesh}
    params = Object.assign(params,annotStyle)

    return new annotations.NoteVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

function thicknessVisualProvider(entity, subJ, params){
  let {getVisual,entities$} = params
  let deps = [entity.target.iuid]
  let entryPoint = entity.target.entryPoint
  let exitPoint  = entity.target.exitPoint

  function visual(mesh){

    entryPoint= new THREE.Vector3().fromArray(entryPoint)
    exitPoint = new THREE.Vector3().fromArray(exitPoint)
    entryPoint = mesh.localToWorld(entryPoint)
    exitPoint = mesh.localToWorld(exitPoint)

    let params = {
      entryPoint,
      exitPoint,
      object:mesh
    }
    params = Object.assign(params,annotStyle)
    return new annotations.ThicknessVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

function distanceVisualProvider(entity, subJ, params){
  let {getVisual,entities$} = params
  let start = entity.target.start
  let end = entity.target.end

  let deps = [start.iuid, end.iuid]

  function visual(startMesh, endMesh){
    let startPt = new THREE.Vector3().fromArray(start.point)
    let endPt   = new THREE.Vector3().fromArray(end.point)
    startMesh.localToWorld(startPt)
    endMesh.localToWorld(endPt)
    //startMesh.worldToLocal(startPt)
    //endMesh.worldToLocal(endPt)

    let params = {
      start:startPt,
      startObject:startMesh,
      end: endPt,
      endObject: endMesh
    }
    params = Object.assign(params, annotStyle)

    return new annotations.DistanceVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0],data[1]))
    })
}

function diameterVisualProvider(entity, subJ, params){
  let {getVisual,entities$} = params
  let point = entity.target.point
  let normal = entity.target.normal
  let diameter = entity.value
  let deps = [entity.target.iuid]

  function visual(mesh){
    point    = new THREE.Vector3().fromArray(point)
    normal   = new THREE.Vector3().fromArray(normal)
    //mesh.updateMatrix()
    //mesh.updateMatrixWorld()
    point = mesh.localToWorld(point)

    let params = {
         center:point,
         diameter,
         orientation:normal
      }
    params = Object.assign(params,annotStyle)

    return new annotations.DiameterVisual(params)
  }

  meshesFromDeps(deps, getVisual, entities$)
    .subscribe(function(data){
      subJ.onNext(visual(data[0]))
    })
}

/*for testing
let fakeEntities$= Rx.Observable.just({
    byId:{
        5:{typeUid:"A0",iuid:5,name:"PART1",pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
        2: {typeUid:"A0",iuid:2,name:"PART2",pos:[0,0,40],rot:[0,45,0],sca:[1,1,1]},
        7: {typeUid:"A0",iuid:7,name:"PART3",pos:[10,-20,0],rot:[0,0,0],sca:[1,1,1]},

        10:{typeUid:"A1",iuid:10,name:"ANNOT3",deps:[5,2,7],                     pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
        11:{typeUid:"A2",iuid:11,name:"Note ANNOT",value:"some text", deps:[2],  pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
          target:{point:[10,5,0],iuid:2}
        },
      }
    })
    .shareReplay(1)

  let {getVisual,addVisualProvider } = createVisualMapper(partTypes$, fakeEntities$)

  Rx.Observable.from([
    {typeUid:"A0",iuid:5,name:"PART1",pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
    {typeUid:"A0",iuid:2,name:"PART2",pos:[0,0,40],rot:[0,45,0],sca:[1,1,1]},
    {typeUid:"A0",iuid:7,name:"PART3",pos:[10,-20,0],rot:[0,0,0],sca:[1,1,1]},
    //{typeUid:"A1",iuid:10,name:"ANNOT3",deps:[5,2,7],pos:[0,0,0],rot:[0,0,0],sca:[1,1,1]},
    {typeUid:"A1",iuid:11,name:"Note ANNOT",value:"some text",
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{point:[10,5,0],iuid:2}
    },
    {typeUid:"A2",iuid:12,name:"thickness ANNOT",value:150.45,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{entryPoint:[10,5,0], exitPoint:[0,-7.2,19],iuid:5}
    },
    {typeUid:"A3",iuid:13,name:"Diameter ANNOT",value:34.09,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{point:[10,5,0],normal:[1,0,0],iuid:7}
    },
    {typeUid:"A4",iuid:14,name:"distance ANNOT",value:56.22,
      pos:[0,0,0],rot:[0,0,0],sca:[1,1,1],
      target:{
        start:{point:[10,5,0],iuid:2},
        end:{point:[0,-7.2,19],iuid:5}
      }
    }
  ])
    .map(getVisual)
    .subscribe(function(vO){
      vO.subscribe(v2=>console.log("visuals",v2),e=>e,v3=>console.log("visuals done",v3))
    })
 */<|endoftext|>


<commit_before>import {guid} from '../services';

export default function download(data, textMimeType) {
	const csvFile = new Blob([data], {type: 'text/' + textMimeType});
	const downloadLink = document.createElement('a');
	const body = document.body;

	downloadLink.download = guid() ? `${guid()}.${textMimeType}` : `exportTable.${textMimeType}`;
	downloadLink.href = window.URL.createObjectURL(csvFile);
	downloadLink.style.display = 'none';
	body.appendChild(downloadLink);
	downloadLink.click();
	body.removeChild(downloadLink);
}<commit_msg>download different text mime types support<commit_after>import {guid} from '../services';

export default function download(data, textMimeType) {
	const csvFile = new Blob([data], {type: `text/${textMimeType}`});
	const downloadLink = document.createElement('a');
	const body = document.body;

	downloadLink.download = guid() ? `${guid()}.${textMimeType}` : `exportTable.${textMimeType}`;
	downloadLink.href = window.URL.createObjectURL(csvFile);
	downloadLink.style.display = 'none';
	body.appendChild(downloadLink);
	downloadLink.click();
	body.removeChild(downloadLink);
}<|endoftext|>


<commit_before>import {
  debounce,
  throttle
} from 'uni-shared'
import emitter from './emitter'
import keyboard from './keyboard'
import interact from './interact'

UniViewJSBridge.subscribe('getSelectedTextRange', function ({ pageId, callbackId }) {
  const activeElement = document.activeElement
  const tagName = activeElement.tagName.toLowerCase()
  const tagNames = ['input', 'textarea']
  const data = {}
  if (tagNames.includes(tagName)) {
    data.errMsg = 'getSelectedTextRange:ok'
    data.start = activeElement.selectionStart
    data.end = activeElement.selectionEnd
  } else {
    data.errMsg = 'getSelectedTextRange:fail no focused'
  }
  UniViewJSBridge.publishHandler('onGetSelectedTextRange', {
    callbackId,
    data
  }, pageId)
})

// App 延迟获取焦点
const FOCUS_DELAY = 200
let startTime

export default {
  name: 'Field',
  mixins: [emitter, keyboard, interact],
  model: {
    prop: 'value',
    event: 'update:value'
  },
  props: {
    value: {
      type: [String, Number],
      default: ''
    },
    /**
     * 已废弃属性，用于历史兼容
     */
    autoFocus: {
      type: [Boolean, String],
      default: false
    },
    focus: {
      type: [Boolean, String],
      default: false
    },
    cursor: {
      type: [Number, String],
      default: -1
    },
    selectionStart: {
      type: [Number, String],
      default: -1
    },
    selectionEnd: {
      type: [Number, String],
      default: -1
    }
  },
  data () {
    return {
      composing: false,
      valueSync: this._getValueString(this.value),
      focusSync: this.focus,
      // Safari 14 以上修正禁用状态颜色
      fixColor: String(navigator.vendor).indexOf('Apple') === 0 && CSS.supports('image-orientation:from-image')
    }
  },
  watch: {
    focus (val) {
      if (val) {
        this._focus()
      } else {
        this._blur()
      }
    },
    focusSync (val) {
      this.$emit('update:focus', val)
    },
    cursorNumber () {
      this._checkCursor()
    },
    selectionStartNumber () {
      this._checkSelection()
    },
    selectionEndNumber () {
      this._checkSelection()
    }
  },
  computed: {
    needFocus () {
      return this.autoFocus || this.focus
    },
    cursorNumber () {
      var cursor = Number(this.cursor)
      return isNaN(cursor) ? -1 : cursor
    },
    selectionStartNumber () {
      var selectionStart = Number(this.selectionStart)
      return isNaN(selectionStart) ? -1 : selectionStart
    },
    selectionEndNumber () {
      var selectionEnd = Number(this.selectionEnd)
      return isNaN(selectionEnd) ? -1 : selectionEnd
    }
  },
  created () {
    const valueChange = this.__valueChange = debounce((val) => {
      this.valueSync = this._getValueString(val)
    }, 100)
    this.$watch('value', valueChange)
    this.__triggerInput = throttle(($event, detail) => {
      this.__valueChange.cancel()
      this.$emit('update:value', detail.value)
      this.$trigger('input', $event, detail)
    }, 100)
    this.$triggerInput = ($event, detail, force) => {
      this.__valueChange.cancel()
      this.__triggerInput($event, detail)
      if (force) {
        this.__triggerInput.flush()
      }
    }
  },
  beforeDestroy () {
    this.__valueChange.cancel()
    this.__triggerInput.cancel()
  },
  directives: {
    field: {
      inserted (el, binding, vnode) {
        vnode.context._initField(el)
      }
    }
  },
  methods: {
    _getValueString (value) {
      return value === null ? '' : String(value)
    },
    _initField (el) {
      this._field = el
      startTime = startTime || Date.now()
      if (this.needFocus) {
        this._focus()
      }
    },
    _focus () {
      if (!this.needFocus) {
        return
      }
      const field = this._field
      if (!field || (__PLATFORM__ === 'app-plus' && !window.plus)) {
        setTimeout(this._focus.bind(this), 100)
        return
      }
      if (__PLATFORM__ === 'h5') {
        field.focus()
      } else {
        const timeout = FOCUS_DELAY - (Date.now() - startTime)
        if (timeout > 0) {
          setTimeout(this._focus.bind(this), timeout)
          return
        }
        field.focus()
        // 无用户交互的 webview 需主动显示键盘（安卓）
        if (!this.userInteract) {
          plus.key.showSoftKeybord()
        }
      }
    },
    _blur () {
      const field = this._field
      field && field.blur()
    },
    _onFocus ($event) {
      this.focusSync = true
      this.$trigger('focus', $event, {
        value: this.valueSync
      })
      // 从 watch:focusSync 中移出到这里。在watcher中如果focus初始值为ture，则不会执行以下逻辑
      this._checkSelection()
      this._checkCursor()
    },
    _onBlur ($event) {
      // iOS 输入法 compositionend 事件可能晚于 blur
      if (this.composing) {
        this.composing = false
        this._onInput($event, true)
      }
      this.focusSync = false
      const field = $event.target
      let cursor
      if (field.type === 'number') {
        field.type = 'text'
        cursor = field.selectionEnd
        field.type = 'number'
      } else {
        cursor = field.selectionEnd
      }
      this.$trigger('blur', $event, {
        value: this.valueSync,
        cursor
      })
    },
    _checkSelection () {
      const field = this._field
      if (this.focusSync && this.selectionStartNumber > -1 && this.selectionEndNumber > -1 && field.type !== 'number') {
        field.selectionStart = this.selectionStartNumber
        field.selectionEnd = this.selectionEndNumber
      }
    },
    _checkCursor () {
      const field = this._field
      if (this.focusSync && this.selectionStartNumber < 0 && this.selectionEndNumber < 0 && this.cursorNumber > -1 && field.type !== 'number') {
        field.selectionEnd = field.selectionStart = this.cursorNumber
      }
    }
  }
}
<commit_msg>fix: 修复  input设置confirm-type为search时，自动获取焦点问题<commit_after>import {
  debounce,
  throttle
} from 'uni-shared'
import emitter from './emitter'
import keyboard from './keyboard'
import interact from './interact'

UniViewJSBridge.subscribe('getSelectedTextRange', function ({ pageId, callbackId }) {
  const activeElement = document.activeElement
  const tagName = activeElement.tagName.toLowerCase()
  const tagNames = ['input', 'textarea']
  const data = {}
  if (tagNames.includes(tagName)) {
    data.errMsg = 'getSelectedTextRange:ok'
    data.start = activeElement.selectionStart
    data.end = activeElement.selectionEnd
  } else {
    data.errMsg = 'getSelectedTextRange:fail no focused'
  }
  UniViewJSBridge.publishHandler('onGetSelectedTextRange', {
    callbackId,
    data
  }, pageId)
})

// App 延迟获取焦点
const FOCUS_DELAY = 200
let startTime

export default {
  name: 'Field',
  mixins: [emitter, keyboard, interact],
  model: {
    prop: 'value',
    event: 'update:value'
  },
  props: {
    value: {
      type: [String, Number],
      default: ''
    },
    /**
     * 已废弃属性，用于历史兼容
     */
    autoFocus: {
      type: [Boolean, String],
      default: false
    },
    focus: {
      type: [Boolean, String],
      default: false
    },
    cursor: {
      type: [Number, String],
      default: -1
    },
    selectionStart: {
      type: [Number, String],
      default: -1
    },
    selectionEnd: {
      type: [Number, String],
      default: -1
    }
  },
  data () {
    return {
      composing: false,
      valueSync: this._getValueString(this.value),
      focusSync: this.focus,
      // Safari 14 以上修正禁用状态颜色
      fixColor: String(navigator.vendor).indexOf('Apple') === 0 && CSS.supports('image-orientation:from-image')
    }
  },
  watch: {
    focus (val) {
      if (val) {
        this._focus()
      } else {
        this._blur()
      }
    },
    focusSync (val) {
      this.$emit('update:focus', val)
    },
    cursorNumber () {
      this._checkCursor()
    },
    selectionStartNumber () {
      this._checkSelection()
    },
    selectionEndNumber () {
      this._checkSelection()
    }
  },
  computed: {
    needFocus () {
      return this.autoFocus || this.focus
    },
    cursorNumber () {
      var cursor = Number(this.cursor)
      return isNaN(cursor) ? -1 : cursor
    },
    selectionStartNumber () {
      var selectionStart = Number(this.selectionStart)
      return isNaN(selectionStart) ? -1 : selectionStart
    },
    selectionEndNumber () {
      var selectionEnd = Number(this.selectionEnd)
      return isNaN(selectionEnd) ? -1 : selectionEnd
    }
  },
  created () {
    const valueChange = this.__valueChange = debounce((val) => {
      this.valueSync = this._getValueString(val)
    }, 100)
    this.$watch('value', valueChange)
    this.__triggerInput = throttle(($event, detail) => {
      this.__valueChange.cancel()
      this.$emit('update:value', detail.value)
      this.$trigger('input', $event, detail)
    }, 100)
    this.$triggerInput = ($event, detail, force) => {
      this.__valueChange.cancel()
      this.__triggerInput($event, detail)
      if (force) {
        this.__triggerInput.flush()
      }
    }
  },
  beforeDestroy () {
    this.__valueChange.cancel()
    this.__triggerInput.cancel()
  },
  directives: {
    field: {
      inserted (el, binding, vnode) {
        vnode.context._initField(el)
      }
    }
  },
  methods: {
    _getValueString (value) {
      return value === null ? '' : String(value)
    },
    _initField (el) {
      this._field = el
      startTime = startTime || Date.now()
      if (this.needFocus) {
        setTimeout(() => {
          this._focus()
        })
      }
    },
    _focus () {
      if (!this.needFocus) {
        return
      }
      const field = this._field
      if (!field || (__PLATFORM__ === 'app-plus' && !window.plus)) {
        setTimeout(this._focus.bind(this), 100)
        return
      }
      if (__PLATFORM__ === 'h5') {
        field.focus()
      } else {
        const timeout = FOCUS_DELAY - (Date.now() - startTime)
        if (timeout > 0) {
          setTimeout(this._focus.bind(this), timeout)
          return
        }
        field.focus()
        // 无用户交互的 webview 需主动显示键盘（安卓）
        if (!this.userInteract) {
          plus.key.showSoftKeybord()
        }
      }
    },
    _blur () {
      const field = this._field
      field && field.blur()
    },
    _onFocus ($event) {
      this.focusSync = true
      this.$trigger('focus', $event, {
        value: this.valueSync
      })
      // 从 watch:focusSync 中移出到这里。在watcher中如果focus初始值为ture，则不会执行以下逻辑
      this._checkSelection()
      this._checkCursor()
    },
    _onBlur ($event) {
      // iOS 输入法 compositionend 事件可能晚于 blur
      if (this.composing) {
        this.composing = false
        this._onInput($event, true)
      }
      this.focusSync = false
      const field = $event.target
      let cursor
      if (field.type === 'number') {
        field.type = 'text'
        cursor = field.selectionEnd
        field.type = 'number'
      } else {
        cursor = field.selectionEnd
      }
      this.$trigger('blur', $event, {
        value: this.valueSync,
        cursor
      })
    },
    _checkSelection () {
      const field = this._field
      if (this.focusSync && this.selectionStartNumber > -1 && this.selectionEndNumber > -1 && field.type !== 'number') {
        field.selectionStart = this.selectionStartNumber
        field.selectionEnd = this.selectionEndNumber
      }
    },
    _checkCursor () {
      const field = this._field
      if (this.focusSync && this.selectionStartNumber < 0 && this.selectionEndNumber < 0 && this.cursorNumber > -1 && field.type !== 'number') {
        field.selectionEnd = field.selectionStart = this.cursorNumber
      }
    }
  }
}
<|endoftext|>


<commit_before>/* global saveAs */
'use strict';

angular.module('metadatamanagementApp').service('DataSetReportService',
  function(Upload, FileResource, JobLoggingService) {
    var uploadTexTemplate = function(file, dataAcquisitionProjectId) {
      if (file !== null) {
        JobLoggingService.start('dataSetReport');
        Upload.upload({
          url: 'api/data-sets/report',
          fields: {
            'id': dataAcquisitionProjectId
          },
          file: file
        }).success(function(gridFsFileName) {
          //Upload and document could filled with data successfully
          //Download automaticly data filled tex template
          FileResource.download(gridFsFileName).then(function(response) {
            JobLoggingService.success({
              message: 'data-set-management.log-messages.tex.upload-terminated'
            });
            saveAs(response.data.blob, file.name);
            console.log('Error Download');
            JobLoggingService.finish(
              'data-set-management.log-messages.tex.saved', {});
          }).catch(function(error) {
            JobLoggingService.error({
              message: error
            });
            JobLoggingService.cancel(
              'data-set-management.log-messages.tex.cancelled', {}
            );
          });
          //Server hat issues with the tex file, send error to error output
        }).error(function(error) {
          console.log('Error Upload');
          console.log(error);
          console.log(error.errors[0].message);
          //TODO DKatzberg
          //var endErrorIndex = error.message.indexOf('----');
          //if no ---- symbol is in the error message
          //if (endErrorIndex <= 0) {
          //  endErrorIndex = error.message.length;
          //}
          //var messageShort = error.message.substr(0, endErrorIndex).trim();
          JobLoggingService.error({
            message: error.errors[0].message,
            messageParams: error.errors[0].invalidValue
          });
          JobLoggingService.cancel(
            'data-set-management.log-messages.tex.cancelled', {});
        });
      } else {
        JobLoggingService.cancel(
          'data-set-management.log-messages.tex.cancelled', {});
      }
    };
    return {
      uploadTexTemplate: uploadTexTemplate
    };
  });
<commit_msg>#821 extended handling of error messages in the client. handles many errors and not just the first.<commit_after>/* global saveAs */
'use strict';

angular.module('metadatamanagementApp').service('DataSetReportService',
  function(Upload, FileResource, JobLoggingService) {
    var uploadTexTemplate = function(file, dataAcquisitionProjectId) {
      if (file !== null) {
        JobLoggingService.start('dataSetReport');
        Upload.upload({
          url: 'api/data-sets/report',
          fields: {
            'id': dataAcquisitionProjectId
          },
          file: file
        }).success(function(gridFsFileName) {
          //Upload and document could filled with data successfully
          //Download automaticly data filled tex template
          FileResource.download(gridFsFileName).then(function(response) {
            JobLoggingService.success({
              message: 'data-set-management.log-messages.tex.upload-terminated'
            });
            saveAs(response.data.blob, file.name);
            console.log('Error Download');
            JobLoggingService.finish(
              'data-set-management.log-messages.tex.saved', {});
          }).catch(function(error) {
            JobLoggingService.error({
              message: error
            });
            JobLoggingService.cancel(
              'data-set-management.log-messages.tex.cancelled', {}
            );
          });
          //Server hat issues with the tex file, send error to error output
        }).error(function(error) {
          //TODO DKatzberg Delete this code later, after the extension for
          //TemplateException
          //var endErrorIndex = error.message.indexOf('----');
          //if no ---- symbol is in the error message
          //if (endErrorIndex <= 0) {
          //  endErrorIndex = error.message.length;
          //}
          //var messageShort = error.message.substr(0, endErrorIndex).trim();
          error.errors.forEach(function(error) {
            JobLoggingService.error({
              message: error.message,
              messageParams: error.invalidValue
            });
          });
          JobLoggingService.cancel(
            'data-set-management.log-messages.tex.cancelled', {});
        });
      } else {
        JobLoggingService.cancel(
          'data-set-management.log-messages.tex.cancelled', {});
      }
    };
    return {
      uploadTexTemplate: uploadTexTemplate
    };
  });
<|endoftext|>


<commit_before>macro setupContracts {
    case ($loc) => {
	var C = $loc;
    }
}

// by abstracting this out we actually get stuck with *more* parens

// macro vbl {
//     case ($p_type -> $ret_type) => {
// 	C.fun(vbl $p_type, vbl $ret_type)
//     }
// // case (($p_type -> $ret_type) $[|] $opts) => {
// //     	C.fun([vbl $p_type], vbl $ret_type, opt_obj $opts)
// //     }
//     // this is kinda nice, but we're still stuck with parenthesized predicates...
//     case (($p_type -> $ret_type) $[|] $($key $[:] $opt) (,) ...) => {
//     	C.fun([vbl $p_type], vbl $ret_type, {$($key: (vbl $opt),) ...})
//     }
//     // TODO: find an elegant way to allow a method to have
//     //  pre/post (not both), if possible
//     // case (($p_type -> $ret_type) $[|]
//     //          pre:  $pre_cond
//     //          post: $post_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    pre: vbl $pre_cond,
//     // 	    post: vbl $post_cond})
//     // }
//     // case (($p_type -> $ret_type) $[|]
//     //          pre:  $pre_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    pre: vbl $pre_cond})
//     // }
//     // case (($p_type -> $ret_type) $[|]
//     //          post: $post_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    post: vbl $post_cond})
//     // }
//     case ($($key $[:] $type) (,) ...) => {
// 	C.object({$($key: (vbl $type),) ...})
//     }
//     case (($($key $[:] $type) (,) ...) $[|] $pred) => {
// 	C.object({$($key: (vbl $type),) ...},
// 		 {invariant: vbl $pred})
//     }
//     case [$type (,) ...] => {
// 	C.arr([(vbl $type) (,) ...])
//     }
//     case (? $comb) => {
// 	C.opt(C.$comb)
//     }
//     case ($comb1 or $comb2) => {
// 	C.or(vbl $comb1, vbl $comb2)
//     }
//     case ($comb1 and $comb2) => {
// 	C.and(vbl $comb1, vbl $comb2)
//     }
//     case (! ($arg:ident) -> { $check ... }) => {
// 	function ($arg) {
// 	    $check ...
// 	}
//     }
//     case (! ($args:ident, $result:ident) -> { $check ... }) => {
// 	function ($args) {
//     	    return C.check(
// 		function ($result) { $check ... }, 'foobar')
// 	}
//     }
//     case $comb => {
// 	C.$comb
//     }
//     case [$type $[...]] => {
//     	C.arr([C.___(vbl $type)])
//     }
// }

// TODO: we're stuck with 'some arbitrary name' here. This will be
//       fixed when sweet.js gets some new features

// TODO: this needs to change. see notes
macro bang {
    case ($param:ident) -> $body:expr => {
	C.check(function ($param) $body, 'some arbitrary name');
    }
    case $name:ident ($param:ident) -> $body:expr => {
    	C.$name = C.check(function ($param) $body, 'some arbitrary name')
    }
}

macro def {
    case $handle:ident ($($param:ident : $type) (,) ...) : $ret_type $body => {
        var $handle = C.guard(
	    C.fun([(vbl $type) (,) ...], vbl $ret_type),
	    function ($($param,) ...) $body);
    }
}

macro vbl {
    case ($[?] $comb1, $rest ...) => {
	vbl (? $comb1), vbl ($rest ...)
    }
    case ($p_type -> $ret_type $[|] $rest ...) => {
	C.fun(vbl ($p_type), vbl ($ret_type), {
	    vbl ($rest ...)
	})
    }
    case ($key:ident $[:] ! ($arg) -> { $check ... }, $rest ...) => {
	$key: function($arg) { $check ... }, vbl ($rest ...)
    }
    case ($key:ident $[:] ! ($arg) -> { $check ... }) => {
	$key: function($arg) { $check ... }
    }
    // the 'this' contract (for objects) goes here
    case ($p_type -> $ret_type, $rest ...) => {
    	vbl ($p_type -> $ret_type), vbl ($rest ...)
    }
    case {$key $[:] $rest ...} => {
	C.object({$key: vbl ($rest ...)})
    }
    case ($key $[:] $rest ...) => {
	$key: vbl ($rest ...)
    }
    case ([$comb, $arr ...], $rest ...) => {
    	C.arr([vbl ($comb), vbl ($arr ...)]), vbl ($rest ...)
    }
    // this contract must come at the end of param list
    case ($[#] $this) => {
	vbl ($this)
    }
    // can't figure our how to get arbitrarily many and/or withouut parens...
    case ($comb1 or $comb2, $rest ...) => {
    	C.or(vbl ($comb1), vbl ($comb2)), vbl ($rest ...)
    }
    case ($comb1 and $comb2, $rest ...) => {
    	C.and(vbl ($comb1), vbl ($comb2)), vbl ($rest ...)
    }
    case ($comb:ident, $rest ...) => {
	C.$comb, vbl ($rest ...)
    }
    // base cases
    case $p_type -> $ret_type $[|] $opts => {
    	C.fun ([vbl $p_type], vbl $ret_type, {
    	    opts $opts
    	})
    }
    case ($p_type -> $ret_type) => {
	C.fun ([vbl $p_type], vbl $ret_type)
    }
    case (! ($args:ident, $result:ident) -> { $check ... }) => {
	function ($args) {
	    return C.check(
		function ($result) { $check ... }, 'dependent')
	}
    }
    case [$comb, $rest ...] => {
	C.arr([vbl ($comb), vbl ($rest ...)])
    }
    case ($comb1 and $comb2) => {
	c.and(vbl ($comb1), vbl ($comb2))
    }
    case ($comb1 or $comb2) => {
	C.or(vbl ($comb1), vbl ($comb2))
    }
    case ($key: ! ($arg) -> { $check ...} ) => {
	function ($arg) {
	    $check ...
	}
    }
    // this is colliding with the regular ellipses
    case ($comb $[...]) => {
	C.___(vbl ($comb))
    }
    case [$comb] => {
    	C.arr([vbl ($comb)])
    }
    case [$comb $[...]] => {
    	C.arr([vbl ($comb)])
    }
    case ($[?] $comb) => {
	C.opt(vbl ($comb))
    }
    case ($comb) => {
    	vbl $comb
    }
    case $comb:ident => {
	C.$comb
    }
}

// so many cases...makes the syntax pretty, though
macro fun {
    case ($parms ...) -> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...)),
    	    function $args $body);
    }
    case ($parms ...) -> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...})),
    	    function $args $body);
    }
    case ($parms ...) -> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...)),
    	    $def);
    }
    case ($parms ...) -> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...})),
    	    $def);
    }

    // no new
    case ($parms ...) --> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {callOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) --> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {callOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) --> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {callOnly: true}),
    	    $def);
    }
    case ($parms ...) --> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {callOnly: true}),
    	    $def);
    }

    // only new
    case ($parms ...) ==> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {newOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) ==> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {newOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) ==> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {newOnly: true}),
    	    $def);
    }
    case ($parms ...) ==> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {newOnly: true}),
    	    $def);
    }
}

// double duty objects and arrays
macro obj {
    case $contract var $handle:ident = $obj => {
	var $handle = C.guard(vbl $contract, $obj)
    }
}

var contracts = window['contracts-js'];
setupContracts(contracts)

vbl [Num, Str]

vbl (Str or Str, ? Num, (Bool, Num) -> (Str or Bool), [Num, Str], Num)

fun (Num, {x: (Num) -> Str |
    pre: !(x) -> {return x.a > 10;},
    post: !(x) -> {return x.a > 20}
     }) ==> (Str or Num)
function baz (x, y) { return y; }

fun (Num, #{name: Str}) -> (Str)
function batch (x) { return this.name; }

obj {x: (Num) -> Str |
       pre: !(x) -> {return x.a > 10;},
       post: !(x) -> {return x.a > 20} }
var see = {x: function(y) {return y > 3;}};

// fun (Num, Str or Bool, (Str)->(Str or Num)) -> (Num)
// function baz (x, y, z) { return x; }

// vbl {x: Num, y: (Str) -> ! (args, result) -> { return args[0]} }

fun (Num or Bool, Str) -> ! (args, result) -> { return args[0] > result; }
function foo (x, y) { return x - 1; }

// var q = opts (pre: ! (x) -> { return x.a > 10; })

// fun (Num or Str, Str or Bool) -> (Str and Str or Num)
// function bar(x, y) {
//     return y + x;
// }

// obj (a: Num,
//      b: ((Num -> Num) |
//           pre: (!(o) -> { return o.a > 10; }),
//           post: (!(o) -> { return o.a > 20; }),
// 	  this: (a: Num, b: (Num->Num))))
// var ppo = {a: 3, b: function (x) {return this.a = this.a + x;} };
// ppo.b(3);

// obj (a: Num,
//      b: ((Num -> Num) |
//         (pre: (!(o) -> o.a > 10),
//         post: (!(o) -> o.a > 20))))
// var ppo = {a: 12, b: function (x) {return this.a = this.a + x;} };
<commit_msg>got the arbitrarily long strings of and/or   -- still need to parenthesize a more complicated contract if its further down the alternation chain...<commit_after>macro setupContracts {
    case ($loc) => {
	var C = $loc;
    }
}

// by abstracting this out we actually get stuck with *more* parens

// macro vbl {
//     case ($p_type -> $ret_type) => {
// 	C.fun(vbl $p_type, vbl $ret_type)
//     }
// // case (($p_type -> $ret_type) $[|] $opts) => {
// //     	C.fun([vbl $p_type], vbl $ret_type, opt_obj $opts)
// //     }
//     // this is kinda nice, but we're still stuck with parenthesized predicates...
//     case (($p_type -> $ret_type) $[|] $($key $[:] $opt) (,) ...) => {
//     	C.fun([vbl $p_type], vbl $ret_type, {$($key: (vbl $opt),) ...})
//     }
//     // TODO: find an elegant way to allow a method to have
//     //  pre/post (not both), if possible
//     // case (($p_type -> $ret_type) $[|]
//     //          pre:  $pre_cond
//     //          post: $post_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    pre: vbl $pre_cond,
//     // 	    post: vbl $post_cond})
//     // }
//     // case (($p_type -> $ret_type) $[|]
//     //          pre:  $pre_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    pre: vbl $pre_cond})
//     // }
//     // case (($p_type -> $ret_type) $[|]
//     //          post: $post_cond) => {
//     // 	C.fun([vbl $p_type], vbl $ret_type, {
//     // 	    post: vbl $post_cond})
//     // }
//     case ($($key $[:] $type) (,) ...) => {
// 	C.object({$($key: (vbl $type),) ...})
//     }
//     case (($($key $[:] $type) (,) ...) $[|] $pred) => {
// 	C.object({$($key: (vbl $type),) ...},
// 		 {invariant: vbl $pred})
//     }
//     case [$type (,) ...] => {
// 	C.arr([(vbl $type) (,) ...])
//     }
//     case (? $comb) => {
// 	C.opt(C.$comb)
//     }
//     case ($comb1 or $comb2) => {
// 	C.or(vbl $comb1, vbl $comb2)
//     }
//     case ($comb1 and $comb2) => {
// 	C.and(vbl $comb1, vbl $comb2)
//     }
//     case (! ($arg:ident) -> { $check ... }) => {
// 	function ($arg) {
// 	    $check ...
// 	}
//     }
//     case (! ($args:ident, $result:ident) -> { $check ... }) => {
// 	function ($args) {
//     	    return C.check(
// 		function ($result) { $check ... }, 'foobar')
// 	}
//     }
//     case $comb => {
// 	C.$comb
//     }
//     case [$type $[...]] => {
//     	C.arr([C.___(vbl $type)])
//     }
// }

// TODO: we're stuck with 'some arbitrary name' here. This will be
//       fixed when sweet.js gets some new features

// TODO: this needs to change. see notes
macro bang {
    case ($param:ident) -> $body:expr => {
	C.check(function ($param) $body, 'some arbitrary name');
    }
    case $name:ident ($param:ident) -> $body:expr => {
    	C.$name = C.check(function ($param) $body, 'some arbitrary name')
    }
}

macro def {
    case $handle:ident ($($param:ident : $type) (,) ...) : $ret_type $body => {
        var $handle = C.guard(
	    C.fun([(vbl $type) (,) ...], vbl $ret_type),
	    function ($($param,) ...) $body);
    }
}

macro vbl {
    case ($[?] $comb1, $rest ...) => {
	vbl (? $comb1), vbl ($rest ...)
    }
    case ($p_type -> $ret_type $[|] $rest ...) => {
	C.fun(vbl ($p_type), vbl ($ret_type), {
	    vbl ($rest ...)
	})
    }
    case ($key:ident $[:] ! ($arg) -> { $check ... }, $rest ...) => {
	$key: function($arg) { $check ... }, vbl ($rest ...)
    }
    case ($key:ident $[:] ! ($arg) -> { $check ... }) => {
	$key: function($arg) { $check ... }
    }
    // the 'this' contract (for objects) goes here
    case ($p_type -> $ret_type, $rest ...) => {
    	vbl ($p_type -> $ret_type), vbl ($rest ...)
    }
    case {$key $[:] $rest ...} => {
	C.object({$key: vbl ($rest ...)})
    }
    case ($key $[:] $rest ...) => {
	$key: vbl ($rest ...)
    }
    case ([$comb, $arr ...], $rest ...) => {
    	C.arr([vbl ($comb), vbl ($arr ...)]), vbl ($rest ...)
    }
    // this contract must come at the end of param list
    case ($[#] $this) => {
	vbl ($this)
    }
    // can't figure our how to get arbitrarily many and/or withouut parens...
    case ($comb1 or $comb2, $rest ...) => {
    	C.or(vbl ($comb1), vbl ($comb2)), vbl ($rest ...)
    }
    case ($comb1 and $comb2, $rest ...) => {
    	C.and(vbl ($comb1), vbl ($comb2)), vbl ($rest ...)
    }
    case ($comb1 or $comb2 $rest ...) => {
	vbl (($comb1 or $comb2) $rest ...)
    }
    case ($comb1 and $com2 $rest ...) => {
	vbl (($comb2 and $comb2) $rest ...)
    }
    case ($comb:ident, $rest ...) => {
	C.$comb, vbl ($rest ...)
    }
    // base cases
    case $p_type -> $ret_type $[|] $opts => {
    	C.fun ([vbl $p_type], vbl $ret_type, {
    	    opts $opts
    	})
    }
    case ($p_type -> $ret_type) => {
	C.fun ([vbl $p_type], vbl $ret_type)
    }
    case (! ($args:ident, $result:ident) -> { $check ... }) => {
	function ($args) {
	    return C.check(
		function ($result) { $check ... }, 'dependent')
	}
    }
    case [$comb, $rest ...] => {
	C.arr([vbl ($comb), vbl ($rest ...)])
    }
    case ($comb1 and $comb2) => {
	C.and(vbl ($comb1), vbl ($comb2))
    }
    case ($comb1 or $comb2) => {
	C.or(vbl ($comb1), vbl ($comb2))
    }
    case ($key: ! ($arg) -> { $check ...} ) => {
	function ($arg) {
	    $check ...
	}
    }
    // this is colliding with the regular ellipses
    // case ($comb $[...]) => {
    // 	C.___(vbl ($comb))
    // }
    case [$comb] => {
    	C.arr([vbl ($comb)])
    }
    case [$comb $[...]] => {
    	C.arr([vbl ($comb)])
    }
    case ($[?] $comb) => {
	C.opt(vbl ($comb))
    }
    case ($comb) => {
    	vbl $comb
    }
    case $comb:ident => {
	C.$comb
    }
}

// so many cases...makes the syntax pretty, though
macro fun {
    case ($parms ...) -> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...)),
    	    function $args $body);
    }
    case ($parms ...) -> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...})),
    	    function $args $body);
    }
    case ($parms ...) -> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...)),
    	    $def);
    }
    case ($parms ...) -> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...})),
    	    $def);
    }

    // no new
    case ($parms ...) --> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {callOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) --> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {callOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) --> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {callOnly: true}),
    	    $def);
    }
    case ($parms ...) --> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {callOnly: true}),
    	    $def);
    }

    // only new
    case ($parms ...) ==> ($ret ...) function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {newOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) ==> ! ($argl:ident, $result:ident) -> {$check ...} function $handle $args $body => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {newOnly: true}),
    	    function $args $body);
    }
    case ($parms ...) ==> ($ret ...) var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl ($ret ...), {newOnly: true}),
    	    $def);
    }
    case ($parms ...) ==> ! ($argl:ident, $result:ident) -> {$check ...} var $handle = $def:expr => {
    	var $handle = C.guard(
    	    C.fun([vbl ($parms ...)], vbl (! ($argl, $result) -> {$check ...}), {newOnly: true}),
    	    $def);
    }
}

// double duty objects and arrays
macro obj {
    case $contract var $handle:ident = $obj => {
	var $handle = C.guard(vbl $contract, $obj)
    }
}

var contracts = window['contracts-js'];
setupContracts(contracts)

// still need to parenthesize the function, which kind of makes sense...
//   can we really define associativity? can't really backtrack
vbl (Num or Bool or Str or ((Num)->Str))

// fun (Num or Bool or Str or (Num)->(Str)) -> (Str or Bool)
// function baz (x) { return x; }

// vbl [Num, Str]

// vbl (Str or Str, ? Num, (Bool, Num) -> (Str or Bool), [Num, Str], Num)

// fun (Num, {x: (Num) -> Str |
//     pre: !(x) -> {return x.a > 10;},
//     post: !(x) -> {return x.a > 20}
//      }) ==> (Str or Num)
// function baz (x, y) { return y; }

// fun (Num, #{name: Str}) -> (Str)
// function batch (x) { return this.name; }

// obj {x: (Num) -> Str |
//        pre: !(x) -> {return x.a > 10;},
//        post: !(x) -> {return x.a > 20} }
// var see = {x: function(y) {return y > 3;}};

// fun (Num, Str or Bool, (Str)->(Str or Num)) -> (Num)
// function baz (x, y, z) { return x; }

// fun (Num or Bool, Str) -> ! (args, result) -> { return args[0] > result; }
// function foo (x, y) { return x - 1; }

// fun (Num or Str, Str or Bool) -> (Str and Str or Num)
// function bar(x, y) {
//     return y + x;
// }
<|endoftext|>


<commit_before>enyo.kind({
	name: "InfoDb",
	kind: "Component",
	create: function() {
		this.modules = [];
		this.packages = [];
		this.objects = [];
		this.inherited(arguments);
	},
	dbify: function(inReaderModules) {
		// mine data from module list
		var data = this.modulesToObjects(inReaderModules);
		// combine with existing db
		this.modules = this.modules.concat(data.modules);
		this.packages = this.packages.concat(data.packages);
		this.objects = this.objects.concat(data.objects);
		// add all property records to the objects array
		this.indexAllProperties(data.objects);
		// sort the object list by name
		this.objects.sort(this.nameCompare);
	},
	modulesToObjects: function(inReaderModules) {
		// extra module information into an (unmapped array)
		var modules = this.buildModuleList(inReaderModules);
		// iterate over modules to infer package information (unmapped array)
		var packages = this.buildPackageList(modules);
		// combine all module objects (ths module itself, and each of it's objects that has a name and type) into 'this.raw' array
		this.indexModuleObjects(modules);
		// process objects in 'this.raw' to make them easier to consume
		var objects = this.cookObjects();
		// to each cooked 'kind' record, add a list of superkinds and inherited properties
		this.indexInheritance(objects);
		return {
			modules: modules,
			packages: packages,
			objects: objects
		};
	},
	nameCompare: function(inA, inB) {
		if (inA.name < inB.name) {
			return -1;
		}
		if (inA.name > inB.name) {
			return 1;
		}
		return 0;
	},
	// return the subset of objects that are inType
	listByType: function(inType) {
		var result = [];
		for (var i=0, o; o=this.objects[i]; i++) {
			if (o.type == inType) {
				result.push(o);
			}
		}
		return result;
	},
	filter: function(inFilterFn) {
		return enyo.forEach(this.objects, inFilterFn);
	},
	findByProperty: function(inList, inProperty, inValue) {
		for (var i=0, k; k=inList[i]; i++) {
			if (k[inProperty] == inValue) {
				return k;
			}
		}
	},
	findByName: function(inName) {
		return this.findByProperty(this.objects, "name", inName);
	},
	findByTopic: function(inName) {
		return this.findByProperty(this.objects, "topic", inName);
	},
	//
	unmap: function(inMap, inFlag) {
		var results = [];
		for (var key in inMap) {
			var elt = inMap[key];
			elt.key = key;
			if (inFlag) {
				elt[inFlag] = true;
			}
			results.push(elt);
		}
		return results;
	},
	buildModuleList: function(inModuleHash) {
		// FIXME: should lower level code produce an array directly?
		return this.unmap(inModuleHash);
	},
	buildPackageList: function(inModules) {
		var pkgs = {};
		// for each module
		for (var i=0, m, n, lc, p; m=inModules[i]; i++) {
			// discover package name
			n = (m.packageName || "unknown");
			// bin by package name
			lc = n.toLowerCase();
			// create a package record, if needed
			if (!pkgs[lc]) {
				pkgs[lc] = {
					packageName: n,
					modules: []
				}
			}
			// the package record for this module
			p = pkgs[lc];
			// store a reference to this module
			p.modules.push(m);
		}
		// convert package map into array of named objects
		return this.unmap(pkgs);
	},
	//
	// combine all module objects into master array of raw objects
	indexModuleObjects: function(inModules) {
		this.raw = [];
		for (var i=0, m; m=inModules[i]; i++) {
			this.indexModule(m);
		}
	},
	// add this module and each of it's objects (that has a name and type) to the master array
	// refer each indexed object back to it's source module
	indexModule: function(inModule) {
		// the module itself is an object
		inModule.type = 'module';
		this.raw.push(inModule);
		// all named and typed objects go into raw array
		for (var i=0, o$=inModule.objects, o; o=o$[i]; i++) {
			if (o.name && o.type) {
				o.module = inModule;
				this.raw.push(o);
			}
		}
		// make an array to hold 'cooked' objects (overwrite old objects array)
		inModule.objects = [];
	},
	// process raw data in each object into a form that's easier to consume
	cookObjects: function() {
		var objects = [];
		for (var i=0, raw, cooked; raw=this.raw[i]; i++) {
			// cook the raw data
			cooked = this.cookObject(raw);
			// convert group id to flag
			if (raw.group) {
				cooked[raw.group] = true;
			}
			// track the source module
			cooked.module = raw.module;
			// make sure there is an index topic
			if (!cooked.topic) {
				cooked.topic = cooked.name;
			}
			// store the cooked object in the master array
			objects[i] = cooked;
			// store the cooked object in it's module
			if (cooked.module) {
				cooked.module.objects.push(cooked);
			}
		}
		return objects;
	},
	// process a raw object based on it's type
	cookObject: function(inObject) {
		var fn = "cook_" + inObject.type;
		if (this[fn]) {
			return this[fn](inObject);
		}
		return inObject;
	},
	cook_module: function(inModule) {
		inModule.topic = inModule.rawPath;
		inModule.name = inModule.rawPath;
		return inModule;
	},
	cook_function: function(inFunction) {
		return inFunction;
	},
	cook_object: function(o) {
		// cook raw data
		var info = {
			name: o.name,
			comment: o.comment,
			type: o.type,
			object: true
		};
		// list properties with references
		info.properties = this.listKindProperties(o, info);
		return info;
	},
	cook_kind: function(k) {
		// FIXME: the default kind is only enyo.Control if 'dom' package is loaded, otherwise it's enyo.Component
		var defaultKind = "enyo.Control";
		// cook raw data
		var info = {
			name: k.name.value,
			comment: k.comment,
			type: k.type,
			kind: true,
			// FIXME: the default constructor is only enyo.Control if 'dom' package is loaded, otherwise it's enyo.Component
			superKind: k.kind ? (k.kind.value != "null" && k.kind.value) : defaultKind
		};
		// list properties with references
		info.properties = this.listKindProperties(k, info);
		return info;
	},
	listKindProperties: function(inKind, inInfo) {
		/*
		// copy methods
		var props = this.unmap(inKind.methods.map, "method");
		// copy non-method properties
		props = props.concat(this.unmap(inKind.properties.map, "property"));
		*/
		var props = inKind.properties;
		// append published properties
		if (inKind.published && inKind.published.value) {
			for (var i=0, p; p=inKind.published.value[i]; i++) {
				p.published = true;
				props.push(p);
			}
			//props = props.concat(this.unmap(inKind.published.value.properties.map, "published"));
		}
		for (var i=0, p; p=props[i]; i++) {
			// convert group id to flag
			p[p.group] = true;
			// refer each property record back to the kind info it came from (for tracking overrides)
			p.kind = inInfo;
			// topic includes the source kind
			p.topic = p.kind.name + "::" + p.name;
			// object type
			p.type = p.method ? "method" : "property";
		}
		// sort by name property
		props.sort(this.nameCompare);
		return props;
	},
	//
	indexInheritance: function(inObjects) {
		for (var i=0, o; o=inObjects[i]; i++) {
			if (o.type == "kind") {
				o.superkinds = this.listSuperkinds(o);
				o.allProperties = this.listInheritedProperties(o);
			}
		}
	},
	listSuperkinds: function(inKind) {
		var supers = [], kind = inKind;
		while (kind && kind.superKind) {
			var sk = kind.superKind;
			kind = this.findByName(sk);
			if (!kind) {
				kind = this.findByName("enyo." + sk);
				if (kind) {
					sk = "enyo." + sk;
				}
			}
			supers.push(sk);
		}
		return supers;
	},
	listInheritedProperties: function(inKind) {
		var all = [], map = {};
		// utility function
		mergeProperties = function(inProperties) {
			for (var j=0, p; p=inProperties[j]; j++) {
				// look for overridden property
				var old = map.hasOwnProperty(p.name) && map[p.name];
				if (old) {
					// note the override, reference the previous instance
					p.overrides = old;
					// update array (only store latest property)
					all[enyo.indexOf(old, all)] = p;
				} else {
					// new property
					all.push(p);
				}
				// update temporary property map
				map[p.name] = p;
			}
		}
		// walk up the inheritance chain from the basest base
		for (var i=inKind.superkinds.length - 1, n; n=inKind.superkinds[i]; i--) {
			// find the superkind properties
			var sk = this.findByName(n);
			if (sk) {
				// merge the superkind properties
				mergeProperties(sk.properties);
			}
		}
		// merge the kind's own properties
		mergeProperties(inKind.properties);
		// default sort
		all.sort(this.nameCompare);
		// return the list
		return all;
	},
	indexAllProperties: function(inObjects) {
		for (var i=0, o; o=inObjects[i]; i++) {
			enyo.forEach(o.properties, function(p) {
				this.objects.push(p);
			}, this);
		}
	}
});
<commit_msg>use filter for b4<commit_after>enyo.kind({
	name: "InfoDb",
	kind: "Component",
	create: function() {
		this.modules = [];
		this.packages = [];
		this.objects = [];
		this.inherited(arguments);
	},
	dbify: function(inReaderModules) {
		// mine data from module list
		var data = this.modulesToObjects(inReaderModules);
		// combine with existing db
		this.modules = this.modules.concat(data.modules);
		this.packages = this.packages.concat(data.packages);
		this.objects = this.objects.concat(data.objects);
		// add all property records to the objects array
		this.indexAllProperties(data.objects);
		// sort the object list by name
		this.objects.sort(this.nameCompare);
	},
	modulesToObjects: function(inReaderModules) {
		// extra module information into an (unmapped array)
		var modules = this.buildModuleList(inReaderModules);
		// iterate over modules to infer package information (unmapped array)
		var packages = this.buildPackageList(modules);
		// combine all module objects (ths module itself, and each of it's objects that has a name and type) into 'this.raw' array
		this.indexModuleObjects(modules);
		// process objects in 'this.raw' to make them easier to consume
		var objects = this.cookObjects();
		// to each cooked 'kind' record, add a list of superkinds and inherited properties
		this.indexInheritance(objects);
		return {
			modules: modules,
			packages: packages,
			objects: objects
		};
	},
	nameCompare: function(inA, inB) {
		if (inA.name < inB.name) {
			return -1;
		}
		if (inA.name > inB.name) {
			return 1;
		}
		return 0;
	},
	// return the subset of objects that are inType
	listByType: function(inType) {
		var result = [];
		for (var i=0, o; o=this.objects[i]; i++) {
			if (o.type == inType) {
				result.push(o);
			}
		}
		return result;
	},
	filter: function(inFilterFn) {
		return enyo.filter(this.objects, inFilterFn);
	},
	findByProperty: function(inList, inProperty, inValue) {
		for (var i=0, k; k=inList[i]; i++) {
			if (k[inProperty] == inValue) {
				return k;
			}
		}
	},
	findByName: function(inName) {
		return this.findByProperty(this.objects, "name", inName);
	},
	findByTopic: function(inName) {
		return this.findByProperty(this.objects, "topic", inName);
	},
	//
	unmap: function(inMap, inFlag) {
		var results = [];
		for (var key in inMap) {
			var elt = inMap[key];
			elt.key = key;
			if (inFlag) {
				elt[inFlag] = true;
			}
			results.push(elt);
		}
		return results;
	},
	buildModuleList: function(inModuleHash) {
		// FIXME: should lower level code produce an array directly?
		return this.unmap(inModuleHash);
	},
	buildPackageList: function(inModules) {
		var pkgs = {};
		// for each module
		for (var i=0, m, n, lc, p; m=inModules[i]; i++) {
			// discover package name
			n = (m.packageName || "unknown");
			// bin by package name
			lc = n.toLowerCase();
			// create a package record, if needed
			if (!pkgs[lc]) {
				pkgs[lc] = {
					packageName: n,
					modules: []
				}
			}
			// the package record for this module
			p = pkgs[lc];
			// store a reference to this module
			p.modules.push(m);
		}
		// convert package map into array of named objects
		return this.unmap(pkgs);
	},
	//
	// combine all module objects into master array of raw objects
	indexModuleObjects: function(inModules) {
		this.raw = [];
		for (var i=0, m; m=inModules[i]; i++) {
			this.indexModule(m);
		}
	},
	// add this module and each of it's objects (that has a name and type) to the master array
	// refer each indexed object back to it's source module
	indexModule: function(inModule) {
		// the module itself is an object
		inModule.type = 'module';
		this.raw.push(inModule);
		// all named and typed objects go into raw array
		for (var i=0, o$=inModule.objects, o; o=o$[i]; i++) {
			if (o.name && o.type) {
				o.module = inModule;
				this.raw.push(o);
			}
		}
		// make an array to hold 'cooked' objects (overwrite old objects array)
		inModule.objects = [];
	},
	// process raw data in each object into a form that's easier to consume
	cookObjects: function() {
		var objects = [];
		for (var i=0, raw, cooked; raw=this.raw[i]; i++) {
			// cook the raw data
			cooked = this.cookObject(raw);
			// convert group id to flag
			if (raw.group) {
				cooked[raw.group] = true;
			}
			// track the source module
			cooked.module = raw.module;
			// make sure there is an index topic
			if (!cooked.topic) {
				cooked.topic = cooked.name;
			}
			// store the cooked object in the master array
			objects[i] = cooked;
			// store the cooked object in it's module
			if (cooked.module) {
				cooked.module.objects.push(cooked);
			}
		}
		return objects;
	},
	// process a raw object based on it's type
	cookObject: function(inObject) {
		var fn = "cook_" + inObject.type;
		if (this[fn]) {
			return this[fn](inObject);
		}
		return inObject;
	},
	cook_module: function(inModule) {
		inModule.topic = inModule.rawPath;
		inModule.name = inModule.rawPath;
		return inModule;
	},
	cook_function: function(inFunction) {
		return inFunction;
	},
	cook_object: function(o) {
		// cook raw data
		var info = {
			name: o.name,
			comment: o.comment,
			type: o.type,
			object: true
		};
		// list properties with references
		info.properties = this.listKindProperties(o, info);
		return info;
	},
	cook_kind: function(k) {
		// FIXME: the default kind is only enyo.Control if 'dom' package is loaded, otherwise it's enyo.Component
		var defaultKind = "enyo.Control";
		// cook raw data
		var info = {
			name: k.name.value,
			comment: k.comment,
			type: k.type,
			kind: true,
			// FIXME: the default constructor is only enyo.Control if 'dom' package is loaded, otherwise it's enyo.Component
			superKind: k.kind ? (k.kind.value != "null" && k.kind.value) : defaultKind
		};
		// list properties with references
		info.properties = this.listKindProperties(k, info);
		return info;
	},
	listKindProperties: function(inKind, inInfo) {
		/*
		// copy methods
		var props = this.unmap(inKind.methods.map, "method");
		// copy non-method properties
		props = props.concat(this.unmap(inKind.properties.map, "property"));
		*/
		var props = inKind.properties;
		// append published properties
		if (inKind.published && inKind.published.value) {
			for (var i=0, p; p=inKind.published.value[i]; i++) {
				p.published = true;
				props.push(p);
			}
			//props = props.concat(this.unmap(inKind.published.value.properties.map, "published"));
		}
		for (var i=0, p; p=props[i]; i++) {
			// convert group id to flag
			p[p.group] = true;
			// refer each property record back to the kind info it came from (for tracking overrides)
			p.kind = inInfo;
			// topic includes the source kind
			p.topic = p.kind.name + "::" + p.name;
			// object type
			p.type = p.method ? "method" : "property";
		}
		// sort by name property
		props.sort(this.nameCompare);
		return props;
	},
	//
	indexInheritance: function(inObjects) {
		for (var i=0, o; o=inObjects[i]; i++) {
			if (o.type == "kind") {
				o.superkinds = this.listSuperkinds(o);
				o.allProperties = this.listInheritedProperties(o);
			}
		}
	},
	listSuperkinds: function(inKind) {
		var supers = [], kind = inKind;
		while (kind && kind.superKind) {
			var sk = kind.superKind;
			kind = this.findByName(sk);
			if (!kind) {
				kind = this.findByName("enyo." + sk);
				if (kind) {
					sk = "enyo." + sk;
				}
			}
			supers.push(sk);
		}
		return supers;
	},
	listInheritedProperties: function(inKind) {
		var all = [], map = {};
		// utility function
		mergeProperties = function(inProperties) {
			for (var j=0, p; p=inProperties[j]; j++) {
				// look for overridden property
				var old = map.hasOwnProperty(p.name) && map[p.name];
				if (old) {
					// note the override, reference the previous instance
					p.overrides = old;
					// update array (only store latest property)
					all[enyo.indexOf(old, all)] = p;
				} else {
					// new property
					all.push(p);
				}
				// update temporary property map
				map[p.name] = p;
			}
		}
		// walk up the inheritance chain from the basest base
		for (var i=inKind.superkinds.length - 1, n; n=inKind.superkinds[i]; i--) {
			// find the superkind properties
			var sk = this.findByName(n);
			if (sk) {
				// merge the superkind properties
				mergeProperties(sk.properties);
			}
		}
		// merge the kind's own properties
		mergeProperties(inKind.properties);
		// default sort
		all.sort(this.nameCompare);
		// return the list
		return all;
	},
	indexAllProperties: function(inObjects) {
		for (var i=0, o; o=inObjects[i]; i++) {
			enyo.forEach(o.properties, function(p) {
				this.objects.push(p);
			}, this);
		}
	}
});
<|endoftext|>