text:
#include "ViewMenu2d.h"

edk::gui2d::MenuObj::MenuObj(){
    //
    this->active = false;
    this->id = 0u;
    this->status = edk::gui2d::gui2dTextureNormal;
    this->saveStatus = edk::gui2d::gui2dTextureSize;
    edk::uint8 size = edk::gui2d::gui2dTextureSize;
    for(edk::uint8 i=0u;i<size;i++){
        this->color[i] = edk::color4f32(1,1,1,1);
    }
    //load the mesh
    this->obj.addMesh(&this->meshObj);
    {
        edk::shape::Rectangle2D rect;
        rect.setPivoToCenter();
        rect.setPolygonColor(0,0,0,1);
        this->meshObj.addPolygon(rect);
        this->meshObj.material.setEmission(1,1,1,1);
    }
}
edk::gui2d::MenuObj::~MenuObj(){
    //
    this->str1.cleanName();
    this->str2.cleanName();
}

//get the type
edk::gui2d::gui2dTypes edk::gui2d::MenuObj::getType(){
    return edk::gui2d::gui2dTypeMenu;
}

void edk::gui2d::MenuObj::updatePositions(){
    //set the objects position based on the full size of the button

    this->sprite.position.x = this->obj.position.x - (this->obj.size.width * 0.5f) + this->sprite.size.width*0.5f + edkGui2dInsideBorder
            ;
    this->sprite.position.y = this->obj.position.y;

    //set the object in the position
    this->text1.setPosition(this->obj.position.x - (this->obj.size.width * 0.5f) + (this->text1.getMapScaleWidth() * 0.5f) + this->positionText1
                            ,this->obj.position.y
                            );
    //set the object in the position
    this->text2.setPosition(this->obj.position.x - (this->obj.size.width * 0.5f) + (this->text2.getMapScaleWidth() * 0.5f) + this->positionText2
                            ,this->obj.position.y
                            );
}

//load the object
bool edk::gui2d::MenuObj::loadObject(edk::uint32 id,edk::char8* spriteName,edk::char8* text1,edk::char8* text2){
    this->id = id;
    this->sprite.cleanMeshes();
    this->text1.deleteMap();
    this->str1.cleanName();
    this->text2.deleteMap();
    this->str2.cleanName();
    bool ret = true;
    this->positionText1 = this->positionText2 = 0.f;
    //set the object size
    this->obj.size = edk::size2f32(0,1);
    this->sprite.size = edkGui2dInsideHeight;

    this->obj.size.width += edkGui2dInsideBorder;
    if(spriteName){
        //
        edk::shape::Mesh2D* mesh = this->sprite.newMesh();
        if(mesh){
            edk::shape::Rectangle2D rect;
            rect.setPivoToCenter();
            mesh->addPolygon(rect);
            //load the texture
            if(mesh->material.loadTexture(spriteName,0u)){
                edk::size2ui32 texSize = mesh->material.getTextureSize(0u);
                if(texSize.width && texSize.height){
                    //test the size to set the sprite new size
                    if(texSize.width > texSize.height){
                        //
                        this->sprite.size.height = this->sprite.size.width * ((edk::float32)texSize.height / (edk::float32)texSize.width);
                    }
                    else{
                        this->sprite.size.width = this->sprite.size.height * ((edk::float32)texSize.width / (edk::float32)texSize.height);
                    }
                    this->obj.size.width += this->sprite.size.width;
                    this->obj.size.width += edkGui2dInsideBorder;
                }
                else{
                    this->sprite.removeAllMesh();
                    ret = false;
                }
            }
            else{
                this->sprite.removeAllMesh();
                ret = false;
            }
        }
    }
    else{
        this->sprite.size.width = 0.f;
    }
    if(text1){
        //test the size of the text
        if(edk::String::strSize(text1)){
            //create the fontMap
            this->text1.deleteMap();
            this->text1.setScale(edkGui2dInsideHeight*0.5f,edkGui2dInsideHeight);
            this->positionText1 = this->obj.size.width;
            if(this->text1.createStringMap(text1)){
                this->str1.setName(text1);
                this->obj.size.width += this->text1.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
            }
            else{
                ret = false;
            }
        }
        this->obj.size.width += edkGui2dInsideBorder;
    }
    if(text2){

        //test the size of the text
        if(edk::String::strSize(text2)){
            //
            //create the fontMap
            this->text2.deleteMap();
            this->text2.setScale(edkGui2dInsideHeight*0.5f,edkGui2dInsideHeight);
            this->positionText2 = this->obj.size.width;
            if(this->text2.createStringMap(text2)){
                this->str2.setName(text2);
                this->obj.size.width += this->text2.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
            }
            else{
                ret = false;
            }
        }
        this->obj.size.width += edkGui2dInsideBorder;

    }

    this->updatePositions();

    return ret;
}
bool edk::gui2d::MenuObj::loadObject(edk::uint32 id,const edk::char8* spriteName,edk::char8* text1,edk::char8* text2){
    return this->loadObject(id,(edk::char8*) spriteName,text1,text2);
}
bool edk::gui2d::MenuObj::loadObjectFromPack(edk::pack::FilePackage* pack,edk::uint32 id,edk::char8* spriteName,edk::char8* text1,edk::char8* text2){
    this->id = id;
    this->sprite.cleanMeshes();
    this->text1.deleteMap();
    this->str1.cleanName();
    this->text2.deleteMap();
    this->str2.cleanName();
    bool ret = true;
    this->positionText1 = this->positionText2 = 0.f;
    //set the object size
    this->obj.size = edk::size2f32(0,1);
    this->sprite.size = edkGui2dInsideHeight;

    this->obj.size.width += edkGui2dInsideBorder;
    if(spriteName && pack){
        //
        edk::shape::Mesh2D* mesh = this->sprite.newMesh();
        if(mesh){
            edk::shape::Rectangle2D rect;
            rect.setPivoToCenter();
            mesh->addPolygon(rect);
            //load the texture
            if(mesh->material.loadTextureFromPack(pack,spriteName,0u)){
                edk::size2ui32 texSize = mesh->material.getTextureSize(0u);
                if(texSize.width && texSize.height){
                    //test the size to set the sprite new size
                    if(texSize.width > texSize.height){
                        //
                        this->sprite.size.height = this->sprite.size.width * ((edk::float32)texSize.height / (edk::float32)texSize.width);
                    }
                    else{
                        this->sprite.size.width = this->sprite.size.height * ((edk::float32)texSize.width / (edk::float32)texSize.height);
                    }
                    this->obj.size.width += this->sprite.size.width;
                    this->obj.size.width += edkGui2dInsideBorder;
                }
                else{
                    this->sprite.removeAllMesh();
                    ret = false;
                }
            }
            else{
                this->sprite.removeAllMesh();
                ret = false;
            }
        }
    }
    else{
        this->sprite.size.width = 0.f;
    }
    if(text1){
        //test the size of the text
        if(edk::String::strSize(text1)){
            //create the fontMap
            this->text1.deleteMap();
            this->text1.setScale(edkGui2dInsideHeight*0.5f,edkGui2dInsideHeight);
            this->positionText1 = this->obj.size.width;
            if(this->text1.createStringMap(text1)){
                this->str1.setName(text1);
                this->obj.size.width += this->text1.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
            }
            else{
                ret = false;
            }
        }
        this->obj.size.width += edkGui2dInsideBorder;
    }
    if(text2){

        //test the size of the text
        if(edk::String::strSize(text2)){
            //
            //create the fontMap
            this->text2.deleteMap();
            this->text2.setScale(edkGui2dInsideHeight*0.5f,edkGui2dInsideHeight);
            this->positionText2 = this->obj.size.width;
            if(this->text2.createStringMap(text2)){
                this->str2.setName(text2);
                this->obj.size.width += this->text2.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
            }
            else{
                ret = false;
            }
        }
        this->obj.size.width += edkGui2dInsideBorder;

    }

    this->updatePositions();

    return ret;
}
bool edk::gui2d::MenuObj::loadObjectFromPack(edk::pack::FilePackage* pack,edk::uint32 id,const edk::char8* spriteName,edk::char8* text1,edk::char8* text2){
    return this->loadObjectFromPack(pack,id,(edk::char8*) spriteName,text1,text2);
}
//set the button position
void edk::gui2d::MenuObj::setPosition(edk::float32 x,edk::float32 y){
    this->setPosition(edk::vec2f32 (x,y));
}
void edk::gui2d::MenuObj::setPosition(edk::vec2f32 position){
    //
    this->obj.position = position;
    //update the positions
    this->updatePositions();
}
//set size
bool edk::gui2d::MenuObj::setSize(edk::size2f32 size){
    //
    if(size.width>0.f && size.height>0.f){
        //
        this->obj.size = size;
        this->updatePositions();
        return true;
    }
    return false;
}
bool edk::gui2d::MenuObj::setSize(edk::float32 width,edk::float32 height){
    //
    return this->setSize(edk::size2f32(width,height));
}

//get the size
edk::size2f32 edk::gui2d::MenuObj::getSize(){
    return this->obj.size;
}
//return the object strings pointer
edk::char8* edk::gui2d::MenuObj::getStr1(){
    if(this->str1.getSize())
        return this->str1.getName();
    return NULL;
}
edk::char8* edk::gui2d::MenuObj::getStr2(){
    if(this->str2.getSize())
        return this->str2.getName();
    return NULL;
}
void edk::gui2d::MenuObj::updateSizes(){
    this->positionText1 = this->positionText2 = 0.f;
    //set the object size
    this->obj.size = edk::size2f32(0,1);
    this->obj.size.width += edkGui2dInsideBorder;

    if(this->sprite.size.width>0.f){
        this->sprite.size = edkGui2dInsideHeight;
        this->obj.size.width += this->sprite.size.width;
        this->obj.size.width += edkGui2dInsideBorder;
    }
    else{
        this->sprite.size.height = edkGui2dInsideHeight;
    }

    if(this->text1.haveText()){
        this->positionText1 = this->obj.size.width;
        this->obj.size.width += this->text1.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
        this->obj.size.width += edkGui2dInsideBorder;
    }
    if(this->text2.haveText()){
        this->positionText2 = this->obj.size.width;
        this->obj.size.width += this->text2.getMapSizeWidth() * edkGui2dInsideHeight * 0.5f;
        this->obj.size.width += edkGui2dInsideBorder;

    }
}
//set the status of the object to be draw
bool edk::gui2d::MenuObj::setStatus(edk::gui2d::gui2dTexture status){
    //
    if(status < edk::gui2d::gui2dTextureSize){
        //set the status
        this->status = status;
        return true;
    }
    return false;
}

void edk::gui2d::MenuObj::draw(){
    //test if the status is different
    if(this->saveStatus!=this->status){
        //set the color
        this->saveStatus=this->status;
        this->meshObj.material.setEmission(this->color[this->status]);
    }
    //
    this->obj.draw();
    this->obj.drawWire();

    this->sprite.draw();
    this->text1.draw();
    this->text2.draw();
    /*
    this->obj.drawPivo(1.f,edk::color3f32(1,0,0));
    this->sprite.drawPivo(this->sprite.size.width,edk::color3f32(1,0,0));
    this->text1.drawPivo(this->text1.getMapScaleWidth(),edk::color3f32(1,0,0));
    this->text2.drawPivo(this->text1.getMapScaleWidth(),edk::color3f32(1,0,0));
*/
}
void edk::gui2d::MenuObj::drawSelection(){
    //
    this->obj.draw();
}

edk::gui2d::ViewMenu2d::ViewMenu2d(){
    //
    this->xOrder = true;
    this->positions = edk::vec2f32(0,0);
    this->mouseActivate = this->mouseOn = false;

    this->selectTree = &this->tree1;
    this->selectTreeS = &this->tree2;

    //set the obj paramerets
    this->color[edk::gui2d::gui2dTextureNormal] = edk::color4f32(0.5f,0.5f,0.5f,1.f);
    this->color[edk::gui2d::gui2dTextureUp] = edk::color4f32(0.75f,0.75f,0.75f,1.f);
    this->color[edk::gui2d::gui2dTexturePressed] = edk::color4f32(0.3f,0.3f,0.3f,1.f);
    this->color[edk::gui2d::gui2dTexturePressedUp] = edk::color4f32(0.4f,0.4f,0.4f,1.f);
}
edk::gui2d::ViewMenu2d::~ViewMenu2d(){
    //
}

//return true if have the element on the callback list
bool edk::gui2d::ViewMenu2d::haveCallback(edk::gui2d::Menu2dCallback* callback){
    //test the callback
    if(callback){
        //get the size and the pointer
        edk::uint32 size = this->listCallback.getSize();
        for(edk::uint32 i=0u;i<size;i++){
            if(this->listCallback.get(i) == callback){
                return true;
            }
        }
    }
    return false;
}
//process the callbacks
void edk::gui2d::ViewMenu2d::processEnable(edk::gui2d::MenuObj* obj){
    //get the list size
    edk::uint32 size = this->listCallback.size();
    edk::gui2d::Menu2dCallback* pointer = NULL;
    for(edk::uint32 i=0u;i<size;i++){
        pointer = this->listCallback.get(i);
        if(pointer){
            //process the function
            pointer->enable(this,obj);
        }
    }
}
void edk::gui2d::ViewMenu2d::processExec(edk::gui2d::MenuObj* obj){
    //get the list size
    edk::uint32 size = this->listCallback.size();
    edk::gui2d::Menu2dCallback* pointer = NULL;
    for(edk::uint32 i=0u;i<size;i++){
        pointer = this->listCallback.get(i);
        if(pointer){
            //process the function
            pointer->exec(this,obj);
        }
    }
}
void edk::gui2d::ViewMenu2d::processDisable(edk::gui2d::MenuObj* obj){
    //get the list size
    edk::uint32 size = this->listCallback.size();
    edk::gui2d::Menu2dCallback* pointer = NULL;
    for(edk::uint32 i=0u;i<size;i++){
        pointer = this->listCallback.get(i);
        if(pointer){
            //process the function
            pointer->disable(this,obj);
        }
    }
}
//update camera size and position to have all the objects
void edk::gui2d::ViewMenu2d::updateCamera(){
    //
    if(this->positions.x!= 0.f && this->positions.y!=0.f){
        if(this->xOrder){
            //
            //this->frame.size;
            this->camera.setRect(0,0
                                 ,this->positions.y * (edk::float32)this->frame.size.width / (edk::float32)this->frame.size.height
                                 ,this->positions.y
                                 );
        }
        else{
            //
            this->camera.setRect(0
                                 ,0
                                 ,this->positions.x
                                 ,(this->positions.x * (edk::float32)this->frame.size.height / (edk::float32)this->frame.size.width)
                                 );
            //this->camera.setRect(0,0,this->positions.x,this->positions.y);
            //update the objects be on top
            this->camera.position.y = 0.f + this->positions.y - this->camera.getSize().height*0.5f;
        }
    }
}
//update the objects positions
void edk::gui2d::ViewMenu2d::updatePositions(){
    //update the positions of the objects
    this->positions = 0.f;
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    if(this->xOrder){
        for(edk::uint32 i=0u;i<size;i++){
            //
            obj = this->objs[i];
            if(obj){
                //
                obj->updateSizes();
                //set the object position
                obj->setPosition(this->positions.x + obj->getSize().width*0.5f,obj->getSize().height*0.5f);
                //increment the positions
                this->positions.x += obj->getSize().width;
                if(obj->getSize().height>this->positions.y)
                    this->positions.y = obj->getSize().height;

            }
        }
    }
    else{
        edk::float32 width = 0.f;
        for(edk::uint32 i=0u;i<size;i++){
            //
            obj = this->objs[i];
            if(obj){
                //
                //increment the positions
                if(obj->getSize().width>width)
                    width = obj->getSize().width;
            }
        }

        //calculate the max width
        for(edk::uint32 i=size;i>0u;i--){
            //
            obj = this->objs[i-1u];
            if(obj){
                //

                //set the object size
                obj->setSize(width,obj->getSize().height);
                //set the object position
                obj->setPosition(width*0.5f,this->positions.y + obj->getSize().height*0.5f);
                //increment the positions
                this->positions.y += obj->getSize().height;
                if(obj->getSize().width>this->positions.x)
                    this->positions.x = obj->getSize().width;
            }
        }
    }
}
//process the selection
void edk::gui2d::ViewMenu2d::selectObject(edk::uint32 ,edk::int32 ,edk::float32 ,edk::float32 ,edk::vector::Stack<edk::uint32>* names){
    //
    if(names->size()){
        //add to the select tree
        this->selectTree->add(names->get(0u));
        //remove from the treeS
        this->selectTreeS->remove(names->get(0u));
    }
}

//create a new object
edk::uint32 edk::gui2d::ViewMenu2d::newObject(edk::char8* spriteName,edk::char8* text1,edk::char8* text2,edk::uint32 id){
    //
    edk::gui2d::MenuObj* obj = new edk::gui2d::MenuObj;
    if(obj){
        if(obj->loadObject(id,spriteName,text1,text2)){
            edk::uint32 size = this->objs.size();
            //add the object to the stack
            edk::uint32 ret = this->objs.pushBack(obj);
            if(size != this->objs.size()){
                this->updatePositions();

                //set the obj paramerets
                obj->color[edk::gui2d::gui2dTextureNormal] = this->color[edk::gui2d::gui2dTextureNormal];
                obj->color[edk::gui2d::gui2dTextureUp] = this->color[edk::gui2d::gui2dTextureUp];
                obj->color[edk::gui2d::gui2dTexturePressed] = this->color[edk::gui2d::gui2dTexturePressed];
                obj->color[edk::gui2d::gui2dTexturePressedUp] = this->color[edk::gui2d::gui2dTexturePressedUp];

                return ret;
            }
        }
        delete obj;
    }
    return 0u;
}
edk::uint32 edk::gui2d::ViewMenu2d::newObject(const edk::char8* spriteName,const edk::char8* text1,const edk::char8* text2,edk::uint32 id){
    return newObject((edk::char8*) spriteName,(edk::char8*) text1,(edk::char8*) text2,id);
}
//return the object strings pointer
edk::char8* edk::gui2d::ViewMenu2d::getObjectStr1(edk::uint32 position){
    //test if have the object in position
    if(this->objs.havePos(position)){
        edk::gui2d::MenuObj* obj = this->objs[position];
        if(obj){
            return obj->getStr1();
        }
    }
    return NULL;
}
edk::char8* edk::gui2d::ViewMenu2d::getObjectStr2(edk::uint32 position){
    //test if have the object in position
    if(this->objs.havePos(position)){
        edk::gui2d::MenuObj* obj = this->objs[position];
        if(obj){
            return obj->getStr2();
        }
    }
    return NULL;
}

//return the size of objects inside the stack
edk::uint32 edk::gui2d::ViewMenu2d::getObjectsSize(){
    //
    return this->objs.size();
}
//set the order to X
void edk::gui2d::ViewMenu2d::setOrderX(){
    this->xOrder = true;
    //update the position and the camera
    this->updatePositions();
    this->updateCamera();
}
//set the order to Y
void edk::gui2d::ViewMenu2d::setOrderY(){
    this->xOrder = false;
    //update the position and the camera
    this->updatePositions();
    this->updateCamera();
}
//return if is orderX
bool edk::gui2d::ViewMenu2d::isOrderX(){
    return xOrder;
}
bool edk::gui2d::ViewMenu2d::iOrderY(){
    return xOrder;
}

//set the menu colors
void edk::gui2d::ViewMenu2d::setColorNormal(edk::color3f32 color){
    this->setColorNormal(edk::color4f32(color.r,color.g,color.b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorNormal(edk::color4f32 color){
    this->color[edk::gui2d::gui2dTextureNormal] = color;

    //set objects color
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    for(edk::uint32 i=0u;i<size;i++){
        obj = this->objs[i];
        if(obj){
            obj->color[edk::gui2d::gui2dTextureNormal] = this->color[edk::gui2d::gui2dTextureNormal];
        }
    }
}
void edk::gui2d::ViewMenu2d::setColorNormal(edk::float32 r,edk::float32 g,edk::float32 b){
    this->setColorNormal(edk::color4f32(r,g,b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorNormal(edk::float32 r,edk::float32 g,edk::float32 b,edk::float32 a){
    this->setColorNormal(edk::color4f32(r,g,b,a));
}
void edk::gui2d::ViewMenu2d::setColorUp(edk::color3f32 color){
    //
    this->setColorUp(edk::color4f32(color.r,color.g,color.b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorUp(edk::color4f32 color){
    //
    this->color[edk::gui2d::gui2dTextureUp] = color;

    //set objects color
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    for(edk::uint32 i=0u;i<size;i++){
        obj = this->objs[i];
        if(obj){
            obj->color[edk::gui2d::gui2dTextureUp] = this->color[edk::gui2d::gui2dTextureUp];
        }
    }
}
void edk::gui2d::ViewMenu2d::setColorUp(edk::float32 r,edk::float32 g,edk::float32 b){
    this->setColorUp(edk::color4f32(r,g,b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorUp(edk::float32 r,edk::float32 g,edk::float32 b,edk::float32 a){
    this->setColorUp(edk::color4f32(r,g,b,a));
}
void edk::gui2d::ViewMenu2d::setColorPressed(edk::color3f32 color){
    //
    this->setColorPressed(edk::color4f32(color.r,color.g,color.b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorPressed(edk::color4f32 color){
    //
    this->color[edk::gui2d::gui2dTexturePressed] = color;

    //set objects color
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    for(edk::uint32 i=0u;i<size;i++){
        obj = this->objs[i];
        if(obj){
            obj->color[edk::gui2d::gui2dTexturePressed] = this->color[edk::gui2d::gui2dTexturePressed];
        }
    }
}
void edk::gui2d::ViewMenu2d::setColorPressed(edk::float32 r,edk::float32 g,edk::float32 b){
    this->setColorPressed(edk::color4f32(r,g,b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorPressed(edk::float32 r,edk::float32 g,edk::float32 b,edk::float32 a){
    //
    this->setColorPressed(edk::color4f32(r,g,b,a));
}
void edk::gui2d::ViewMenu2d::setColorPressedUp(edk::color3f32 color){
    //
    this->setColorPressedUp(edk::color4f32(color.r,color.g,color.b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorPressedUp(edk::color4f32 color){
    //
    this->color[edk::gui2d::gui2dTexturePressedUp] = color;

    //set objects color
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    for(edk::uint32 i=0u;i<size;i++){
        obj = this->objs[i];
        if(obj){
            obj->color[edk::gui2d::gui2dTexturePressedUp] = this->color[edk::gui2d::gui2dTexturePressedUp];
        }
    }
}
void edk::gui2d::ViewMenu2d::setColorPressedUp(edk::float32 r,edk::float32 g,edk::float32 b){
    this->setColorPressedUp(edk::color4f32(r,g,b,1.f));
}
void edk::gui2d::ViewMenu2d::setColorPressedUp(edk::float32 r,edk::float32 g,edk::float32 b,edk::float32 a){
    //
    this->setColorPressedUp(edk::color4f32(r,g,b,a));
}

void edk::gui2d::ViewMenu2d::load(edk::rectf32){
    this->objSelected = NULL;
    this->mouseStatus = edk::gui2d::gui2dMouseNothing;
    //
    this->saveFrame = edk::rectf32();
    this->camera.setSize(20,20);
    this->mouseOn = false;

    //clean the objects status
    edk::gui2d::MenuObj* obj = NULL;
    edk::uint32 size = this->objs.size();
    for(edk::uint32 i=0u;i<size;i++){
        obj = this->objs[i];
        if(obj)
            obj->setStatus(edk::gui2d::gui2dTextureNormal);
    }
}
void edk::gui2d::ViewMenu2d::unload(){
    //
}
void edk::gui2d::ViewMenu2d::update(edk::WindowEvents* events){
    //
    this->runSelection = false;
    edk::uint32 size = 0u;

    //change trees
    if(this->selectTree == &this->tree1){
        this->selectTree = &this->tree2;
        this->selectTreeS = &this->tree1;
    }
    else{
        this->selectTree = &this->tree1;
        this->selectTreeS = &this->tree2;
    }

    //save the mouse status
    this->mouseStatus = edk::gui2d::gui2dMouseNothing;

    size = events->mousePressed.size();
    //test the buttons
    for(edk::uint32 i=0u;i<size;i++){
        switch(events->mousePressed[i]){
        case edk::mouse::left:
            this->mouseStatus = edk::gui2d::gui2dMousePressed;
            this->runSelection = true;
            break;
        }
    }
    size = events->mouseHolded.size();
    //test the buttons
    for(edk::uint32 i=0u;i<size;i++){
        switch(events->mouseHolded[i]){
        case edk::mouse::left:
            if(this->mouseStatus != edk::gui2d::gui2dMousePressed)
                this->mouseStatus = edk::gui2d::gui2dMouseHolded;
            this->runSelection = true;
            break;
        }
    }
    size = events->mouseRelease.size();
    //test the buttons
    for(edk::uint32 i=0u;i<size;i++){
        switch(events->mouseRelease[i]){
        case edk::mouse::left:
            this->mouseStatus = edk::gui2d::gui2dMouseRelease;
            this->runSelection = true;
            if(!this->xOrder && this->objSelected){
                //then activate button
                this->processExec(this->objSelected);
            }
            break;
        }
    }

    if(events->mouseMoved || this->runSelection){
        //
        this->runSelection = true;
        this->testSelection(events->mousePos);
    }
}
void edk::gui2d::ViewMenu2d::drawSelectionScene(){
    //draw the objects
    edk::uint32 size = this->objs.size();
    edk::gui2d::MenuObj* obj=NULL;
    for(edk::uint32 i=0u;i<size;i++){
        //
        this->selectionPushName(i);
        obj = this->objs[i];
        obj->draw();
        this->selectionPopName();
    }
}
void edk::gui2d::ViewMenu2d::drawScene(edk::rectf32){
    if(this->saveFrame!=this->frame){
        this->saveFrame=this->frame;
        this->updateCamera();
    }

    edk::uint32 size = 0u;
    edk::uint32 sizeSelected = 0u;
    edk::gui2d::MenuObj* obj = NULL;
    if(this->runSelection){
        this->objSelected = NULL;
        sizeSelected = this->selectTree->size();
        if(sizeSelected){
            {
                //get the object
                this->objSelected = this->objs[this->selectTree->getElementInPosition(0u)];
                if(this->objSelected){
                    //test the mouse status
                    switch(this->mouseStatus){
                    case edk::gui2d::gui2dMousePressed:
                        //change the mouse on
                        this->mouseOn=!this->mouseOn;

                        //test if is xOrder
                        if(this->xOrder && this->mouseOn){
                            //then activate button
                            this->processExec(this->objSelected);
                        }
                        break;
                    case edk::gui2d::gui2dMouseRelease:
                        //test if is NOT xOrder
                        if(!this->xOrder){
                            //run button action
                            this->processExec(this->objSelected);
                        }
                        break;
                    case edk::gui2d::gui2dMouseHolded:
                    case edk::gui2d::gui2dMouseNothing:
                    case edk::gui2d::gui2dMouseStatusSize:
                        break;
                    }

                    //test if the mouse is on
                    if(this->mouseOn || !this->xOrder){
                        this->mouseOn = true;
                        //set the button status to pressedUp
                        this->objSelected->setStatus(edk::gui2d::gui2dTexturePressedUp);
                        if(!this->objSelected->active){
                            this->objSelected->active = true;
                            processEnable(this->objSelected);
                        }
                    }
                    else{
                        //set the button status to op
                        this->objSelected->setStatus(edk::gui2d::gui2dTextureUp);
                        if(this->objSelected->active){
                            this->objSelected->active = false;
                            processDisable(this->objSelected);
                        }
                    }
                }
            }

            edk::uint32 n;
            for(edk::uint32 i=1u;i<sizeSelected;i++){
                //remove this position
                n = this->selectTree->getElementInPosition(1u);
                this->selectTreeS->add(n);
                this->selectTree->remove(n);

            }
        }
        else{
            if(this->mouseStatus==edk::gui2d::gui2dMouseRelease){
                this->mouseOn=false;
            }
        }
        size = this->selectTreeS->size();

        if(size){
            for(edk::uint32 i=0u;i<size;i++){
                //get the object
                obj = this->objs[this->selectTreeS->getElementInPosition(i)];
                if(obj){
                    if(!this->mouseOn || sizeSelected){
                        obj->setStatus(edk::gui2d::gui2dTextureNormal);
                        if(obj->active){
                            //desactivate the button
                            obj->active = false;
                            processDisable(obj);
                        }
                    }
                    else{
                        if(obj->active){
                            obj->setStatus(edk::gui2d::gui2dTexturePressed);
                        }
                        else{
                            obj->setStatus(edk::gui2d::gui2dTextureNormal);
                        }
                    }
                }
            }
            this->selectTreeS->clean();
        }
        else{
            if(sizeSelected || this->mouseStatus==edk::gui2d::gui2dMouseRelease){
                //clean all objects on the list unless the selected
                size = this->objs.size();
                for(edk::uint32 i=0u;i<size;i++){
                    obj = this->objs[i];
                    if(obj && obj!=this->objSelected){
                        obj->setStatus(edk::gui2d::gui2dTextureNormal);
                        if(obj->active){
                            //desactivate the button
                            obj->active = false;
                            processDisable(obj);
                        }
                    }
                }
            }
        }
    }

    //draw the objects
    size = this->objs.size();
    obj=NULL;
    for(edk::uint32 i=0u;i<size;i++){
        //
        obj = this->objs[i];
        obj->draw();
    }
}

//callback
bool edk::gui2d::ViewMenu2d::addCallback(edk::gui2d::Menu2dCallback* callback){
    //test the callback
    if(callback){
        //test if have this callback inside the list
        if(!this->listCallback.haveElement(callback)){
            //add the callback to the list
            return this->listCallback.pushBack(callback);
        }
    }
    return false;
}
bool edk::gui2d::ViewMenu2d::removeCallback(edk::gui2d::Menu2dCallback* callback){
    //test the callback
    if(callback){
        //test if have this callback inside the list
        edk::uint32 pos = this->listCallback.find(callback);
        if(!pos){
            if(this->listCallback.get(pos) != callback){
                return false;
            }
        }
        return this->listCallback.remove(pos);
    }
    return false;
}
void edk::gui2d::ViewMenu2d::cleanCallbacks(){
    this->listCallback.clean();
}


text:
description('This layouttest was initially there to test that' +
            ' shorthand property value is correct even if' +
            ' background-repeat property is declared before it in the ' +
            ' style declaration. It used to test regression described in ' +
            ' this bug.' +
            ' Now that access to non author stylesheet is blocked, we should instead' +
            ' get null when accessing the css rules on that object.');

function iconMarginValue()
{
    var iconDiv = document.getElementById('icon');
    var rules = window.getMatchedCSSRules(iconDiv,'',false);
    return rules[1] ? rules[1].style.getPropertyValue('margin') : 'null';
}

shouldBe('iconMarginValue()', '"null"');

redpajama_set_name: RedPajamaGithub


text:
QuickSpecBegin(SDLSoftButtonStateSpec)

describe(@"soft button state", ^{
    __block SDLSoftButtonState *testState = nil;

    __block NSString *testStateName = @"Test State Name";
    __block NSString *testStateText = @"Test State Text";

    NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
    __block UIImage *testStateImage = [[UIImage alloc] initWithContentsOfFile:[testBundle pathForResource:@"testImageJPEG" ofType:@"jpeg"]];

    it(@"should properly create with an image with defaults", ^{
        testState = [[SDLSoftButtonState alloc] initWithStateName:testStateName text:testStateText image:testStateImage];

        expect(testState.name).to(equal(testStateName));
        expect(testState.text).to(equal(testStateText));
        expect(testState.highlighted).to(beFalsy());
        expect(testState.systemAction).to(equal(SDLSystemActionDefaultAction));
        expect(testState.artwork.persistent).to(beTruthy());
        expect(testState.artwork.fileType).to(equal(SDLFileTypePNG));
        // The rest should be tested in SDLArtwork
    });

    context(@"when created with an artwork", ^{
        __block SDLArtwork *testArtwork = nil;
        __block NSString *testArtworkName = @"Test Artwork Name";
        beforeEach(^{
            testArtwork = [[SDLArtwork alloc] initWithImage:testStateImage name:testArtworkName persistent:YES asImageFormat:SDLArtworkImageFormatPNG];

            testState = [[SDLSoftButtonState alloc] initWithStateName:testStateName text:testStateText artwork:testArtwork];
        });

        it(@"should properly create", ^{
            expect(testState.name).to(equal(testStateName));
            expect(testState.text).to(equal(testStateText));
            expect(testState.highlighted).to(beFalsy());
            expect(testState.systemAction).to(equal(SDLSystemActionDefaultAction));
            expect(testState.artwork.persistent).to(beTruthy());
            expect(testState.artwork.fileType).to(equal(SDLFileTypePNG));
        });

        it(@"should properly create a soft button", ^{
            SDLSoftButton *testSoftButton = testState.softButton;

            expect(testSoftButton.type).to(equal(SDLSoftButtonTypeBoth));
            expect((id)testSoftButton.handler).to(beNil());
            expect(testSoftButton.softButtonID).to(equal(0));
            expect(testSoftButton.isHighlighted).to(beFalsy());
            expect(testSoftButton.systemAction).to(equal(SDLSystemActionDefaultAction));
            expect(testSoftButton.text).to(equal(testStateText));
            expect(testSoftButton.image.imageType).to(equal(SDLImageTypeDynamic));
            expect(testSoftButton.image.value).to(equal(testArtworkName));
        });
    });

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value"
    context(@"when created invalid", ^{
        it(@"should assert", ^{
            expectAction(^{
                [[SDLSoftButtonState alloc] initWithStateName:testStateName text:nil image:nil];
            }).to(raiseException());
        });
    });
#pragma clang diagnostic pop
});

QuickSpecEnd


text:
package com.dsatab.fragment;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

import com.dsatab.DsaTabApplication;
import com.dsatab.R;
import com.dsatab.config.TabInfo;
import com.dsatab.data.Hero;
import com.dsatab.data.adapter.BaseRecyclerAdapter;
import com.dsatab.data.adapter.TabInfoDraggableItemAdapter;
import com.dsatab.util.Util;
import com.dsatab.util.ViewUtils;

import net.steamcrafted.materialiconlib.MaterialDrawableBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by Ganymedes on 04.10.2015.
 */
public class TabListFragment extends BaseRecyclerFragment implements View.OnClickListener {

    private TabInfo currentInfo = null;

    private TabInfoDraggableItemAdapter mAdapter;

    private TabListListener tabListListener;

    public interface TabListListener {
        void onTabInfoClicked(TabInfo tabInfo);

        void onTabInfoSelected(TabInfo tabInfo);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);

        List<TabInfo> tabs;
        if (DsaTabApplication.getInstance().getHero() != null
                && DsaTabApplication.getInstance().getHero().getHeroConfiguration() != null) {
            tabs = new ArrayList<TabInfo>(DsaTabApplication.getInstance().getHero().getHeroConfiguration().getTabs());
        } else {
            tabs = new ArrayList<TabInfo>();
        }
        mAdapter = new TabInfoDraggableItemAdapter(tabs);
        mAdapter.setEventListener(this);
    }

    /*
         * (non-Javadoc)
         *
         * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
         */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = configureContainerView(inflater.inflate(R.layout.tab_list_content, container, false));
        return root;
    }

    protected void initRecycler() {
        recyclerView = (RecyclerView) findViewById(android.R.id.list);

        initRecyclerView(recyclerView,mAdapter,new LinearLayoutManager(getActivity()),true,true,true);
    }

    public TabListListener getTabListListener() {
        return tabListListener;
    }

    public void setTabListListener(TabListListener tabListListener) {
        this.tabListListener = tabListListener;
    }

    private void createTabInfo() {
        TabInfo info = new TabInfo();
        info.setIconUri(Util.getUriForResourceId(R.drawable.vd_mailed_fist));
        mAdapter.add(info);

        if (tabListListener != null) {
            tabListListener.onTabInfoClicked(info);
        }
    }

    @Override
    public void onItemClicked(BaseRecyclerAdapter adapter, int position, View v) {
        if (tabListListener != null) {
            tabListListener.onTabInfoClicked(mAdapter.getItem(position));
        }
        mRecyclerViewSelectionManager.setSelected(position,true);
    }

    @Override
    public boolean onItemLongClicked(BaseRecyclerAdapter adapter, int position, View v) {
        return false;
    }

    @Override
    public void onItemRemoved(BaseRecyclerAdapter adapter, int position) {

    }
    public void selectTabInfo(int index) {
        mRecyclerViewSelectionManager.setSelected(index,true);
        recyclerView.scrollToPosition(index);

        if (tabListListener != null) {
            tabListListener.onTabInfoSelected(mAdapter.getItem(index));
        }
    }

    public void setTabInfo(int index, TabInfo tabInfo) {
        mAdapter.set(tabInfo, index);
    }
    public void addTabInfo(TabInfo tabInfo) {
        mAdapter.add(tabInfo);
    }

    public List<TabInfo> getTabInfos() {
        if (mAdapter != null)
            return mAdapter.getItems();
        else
            return Collections.emptyList();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menuitem_reset, menu);

        if (menu.findItem(R.id.option_reset) != null) {
            menu.findItem(R.id.option_reset).setIcon(ViewUtils.toolbarIcon(getToolbarThemedContext(), MaterialDrawableBuilder.IconValue.BACKUP_RESTORE));
        }

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.option_reset:
                mAdapter.clear();
                mAdapter.addAll(DsaTabApplication.getInstance().getHero().getHeroConfiguration().getDefaultTabs(null));
                selectTabInfo(0);
                break;
        }

        return false;
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initRecycler();

        FloatingActionButton fab =(FloatingActionButton) view.findViewById(R.id.fab);
        fab.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.fab:
                createTabInfo();
        }
        super.onClick(v);
    }

    @Override
    public void onDestroy() {
        mAdapter = null;
        super.onDestroy();
    }

    @Override
    public void onHeroLoaded(Hero hero) {

    }
}


text:
package jj.document;

import java.nio.file.Path;
import java.util.List;

import org.jsoup.parser.ParseError;
import org.slf4j.Logger;

import jj.ServerLogger;
import jj.logging.LoggedEvent;

/**
 * @author jason
 *
 */
@ServerLogger
public class HtmlParseError extends LoggedEvent {

	private final Path path;
	private final List<ParseError> errors;

	public HtmlParseError(final Path path, final List<ParseError> errors) {
		this.path = path;
		this.errors = errors;
	}

	@Override
	public void describeTo(Logger logger) {
		logger.warn("errors while parsing {}, your document may not behave as expected", path);

		for (ParseError pe : errors) {
			logger.warn("{}", pe);
		}
		errors.clear();
	}

}


text:
/*eslint-env node */

// Note that Q and Fs are only require()d when they are required, so this
// module can be used in a browser.
const Utils = require("./Utils");

const TAG = "DataModel";

/**
 * This module provides a way to deserialise a datastructure from
 * JSON data which contains only initialisation data for the
 * objects within the data structure, under control of a data model
 * (specification).
 *
 * The data is read from in a JSON file. On load this data is
 * post-processed under the guidance of the spec, to validate the
 * structure and instantiate class members.
 *
 * The data model is a recursive description of the data. The data can
 * contain simple Javascript types (number, string etc), objects, and
 * arrays. Function and SYmbol objects are not supported.
 *
 * Keywords in the model, starting with $, are used to control the
 * checking and expansion, while other simple names are used for fields
 * in the modelled data.
 *
 * For example, we might want to load a simple data structure which
 * can have a single field, "id:", which must have a string value.
 * ```
 * {
 *   id: "28-0316027f81ff",
 * }
 * ```
 * this is modelled as follows:
 * ```
 * {
 *   id: { $class: String, $doc: "unique id" }
 * }
 * ```
 * Names that don't start with $ (such as `id` in the example) are
 * keys that are expected to be found in the data. The model maps these
 * to the epected type of the data.
 *
 * $keywords in the example define the type of the datum ($class)
 * and a block of documentation ($doc).
 *
 * Keywords include:
 *   $class - type of the datum (as returned by typeof, defaults to "object")
 *   $doc - a documentation string for the datum
 *   $optional - this datum is optional
 *   $default: default value, if not defined and not $optional
 *   $skip - skip deeper checking of this item
 *   $map_of - object is an associative array of elements, each of
 *   which has this model.
 *   $array_of - true if object is an integer-index list of elements, each
 *   of which has this model.
 *
 * for example,
 * ```
 *   ids: {
 *     $doc: "set of ids",
 *     $map_of: { id: { $class: String } }
 *   }
 * ```
 * specifies an array an array of simple objects, e.g.
 * ```
 * ids: { a: { id: "sam" }, b: { id: "joe" } }
 * ```
 * or using json shorthand for an integer key,
 * ```
 * ids: [ { id: "sam" }, { id: "joe" } ]
 * ```
 * you can also instantiate classes. For example,
 * ```
 * {
 *    location: {
 *       $class: Location,
 *       $doc: "Location of the event"
 *    }
 * }
 * ```
 * specifies data that might look like this:
 * { location: { latitude: 53.2856, longitude: -2.5678 } }
 * when this is loaded, the Location constructor is called, thus
 * ```
 * Location({string} key, {object} data, {object} spec)
 * ```
 * and the key value in the final structure is replaced with the created
 * object.
 *
 * Note that currently $class must be undefined if $array_of is defined.
 * A future extension may be to use $class to template objects that subclass
 * array - this is left open.
 *
 * Classes may want to decorate the spec with other $keys.
 * for example, the DataModel.File class uses the $mode key to specify
 * the modes that will be used with a file (see DataModel.File below)
 *
 * Models could be declared flat, but the convention is adopted
 * to break them down so that the model associated with a given
 * object is given alongside in class, using the key "spec". for example,
 * ```
 * function Thing(key, model, spec) { ... }
 *
 * Thing.Model = {
 *    $class: Thing,
 *    ...
 * };
 * ```
 * this can then be referred to in another model e.g
 * ```
 * things: {
 *    $array_of: Thing.Model,
 *    $doc: "list of things"
 * }
 * ```
 * Note that "undefined" is not regarded as a useful value. If the
 * value of a field is undefined, the key for that field will be dropped.
 * @namespace
 */
var DataModel = {
    private_key: {
        $map_of: true,
        $array_of: true,
        $checked: true,
        $class: true,
        $default: true,
        $doc: true,
        $optional: true,
        $skip: true
    },
    builtin_types: [
        Boolean,
        Number,
        Date,
        String,
        RegExp
    ],
};

/**
 * Check the model for correct construction
 * @param model the model to check
 * @param context undefined or an array
 * @private
 */
DataModel.check = function (model, context) {
    if (model.$checked)
        return;

    if (typeof context === "undefined")
        context = [];

    if (typeof model !== "object")
        throw Utils.report(TAG, ".check: Illegal model type ", model,
            " at ", context.join('.'));

    //Utils.LOG("check <",context.join('.'),"> {");

    var is_base = false;
    if (typeof model.$class === "function") {
        if (DataModel.builtin_types.indexOf(model.$class) >= 0) {
            // Internal fields not supported
            is_base = true;
        }
        // Currently cannot have both $array_of and $class
        if (typeof model.$array_of !== "undefined")
            throw Utils.report(TAG, ".check: cannot have $array_of and $class");
    } else if (typeof model.$class !== "undefined") {
        throw Utils.report(TAG, "/check: $class is ",
            typeof model.$class, " at ", context.join('.'));
    }

    model.$checked = true;

    if (!model.$skip) {
        if (typeof model.$array_of !== "undefined") {
            if (typeof model.$map_of !== "undefined")
                throw Utils.report(
                    TAG, ".check: cannot have $array_of and $map_of");
            DataModel.check(model.$array_of, context.concat("[]"));
        } else if (typeof model.$map_of !== "undefined")
            DataModel.check(model.$map_of, context.concat("{}"));
        else {
            for (var i in model) {
                if (i.charAt(0) !== '$') {
                    if (is_base)
                        throw Utils.report(
                            TAG, ".check: $class ",
                            model.$class.name, " has field ", i, " at ",
                            context.join('.'));
                    DataModel.check(model[i], context.concat(i));
                }
            }
        }
    }
    //Utils.LOG("} <",context.join('.'),">");
};

/**
 * Test the given model against the given data structure, checking data
 * against the model and constructing objects as required.
 * @param {object} data The data being loaded
 * @param {index} The index of the structure in the parent object. This
 * will be a number for an array entry, or a key for a hash, and is used
 * to pass to constructors for named objects.
 * @param {object} model the model
 * @param {string} context The context of the check, used in messages only
 * @return the data (or the default, if one was applied)
 */
DataModel.remodel = function (index, data, model, context) {
    var i;

    DataModel.check(model);

    if (index === "_readFrom")
        return data;

    if (typeof context === "undefined")
        context = [];

    //Utils.LOG("Remodel ", context.join('.'));

    // Got a data definition
    if (typeof data === "undefined") {
        if (model.$optional)
            return data;
        if (typeof model.$default === "undefined")
            throw Utils.report(TAG, ".remodel: not optional and no default at ",
                context.join('.'));
        else
            data = model.$default;
    }

    if (model.$skip)
        return data;

    if (typeof model.$map_of !== "undefined") {
        // Object with keys that don't have to match the model,
        // and values that can be undefined
        var rebuilt = {};
        for (var i in data) {
            rebuilt[i] = DataModel.remodel(
                i, data[i], model.$map_of, context.concat(i));
        }
        return rebuilt;
    } else if (typeof model.$array_of !== "undefined") {
        var rebuilt = [];

        for (var i in data) {
            // undefined is allowed in array data
            rebuilt[i] = DataModel.remodel(
                i, data[i], model.$array_of, context.concat(i));
        }
        return rebuilt;
    }

    if (DataModel.builtin_types.indexOf(model.$class) >= 0)
        return data;

    // Not a base type, so the model describes what has to be passed
    // to the constructor (if there is one)
    var rebuilt = data;

    if (typeof data === "object") {
        // Rebuild the sub-structure for this object. The rebuilt object
        // will either be placed in the result or passed to a constructor.

        // Note that fieds not listed in the model are not copied
        // into rebuilt.
        rebuilt = {};
        for (var i in model) {
            if (!DataModel.private_key[i]) {
                var datum = DataModel.remodel(
                    i, data[i], model[i],
                    context.concat(i));
                if (typeof datum !== "undefined")
                    // undefined is skipped in objects
                    rebuilt[i] = datum;
                //Utils.LOG("Rebuilt ",data[i]," to ",rebuilt[i]);
            }
        }
        // Make sure the data doesn't carry any hidden payload
        for (var i in data) {
            if (!model[i])
                throw Utils.report(
                    TAG, ".remodel: Hidden payload ", i, " in ", data,
                    " at ", context.join('.'));
        }
    }

    if (typeof model.$class === "function") {
        //Utils.LOG("Instantiate ", model.$class.name, " ", index," on ", rebuilt);
        // Have to pass index first for building native types such
        // as String, Number, Date
        // Could call remodel, this is fractionally quicker
        rebuilt = new model.$class(rebuilt, index, model);
        //Utils.LOG("Gives ",res);
    }

    return rebuilt;
};

/**
 * Promise to extract a serialisable version of a data structure
 * under guidance of the model as text, such that the data can be
 * reloaded using DataModel.load
 * @param {object} data data to save
 * @param {object{ model data model to follow
 */
DataModel.getSerialisable = function (data, model, context) {
    DataModel.check(model);

    var Q = require("q");

    // context is an internal parameter used for generating
    // meaningful errors
    if (typeof context === "undefined")
        context = [];

    if (typeof data === "undefined") {
        if (typeof model !== "undefined" && model.$optional) {
            return Q();
        }
        throw Utils.report(TAG, ".getSerialisable: non-optional at ",
            context.join('.'));
    }

    //Utils.LOG("Serialise ", data, " using ",model);

    if (model.$skip) {
        if (typeof data === "object" &&
            typeof data.getSerialisable === "function") {
            // objects can override getSerialisable
            // Could also use model.prototype.getSerialisable
            return data.getSerialisable(context);
        }
        return Q(data);
    }

    if (typeof data !== "object")
        return Q(data);

    var promises = Q();
    var serialisable;

    if (typeof model.$array_of !== "undefined") {
        // Serialise all entries in the object, it's an array
        if (typeof data !== "object")
            throw Utils.report(TAG, ".getSerialisable: array expected at ",
                context.join('.'), data);
        serialisable = [];
        Utils.forEach(data, function (entry, index) {
            promises = promises.then(function () {
                    return DataModel.getSerialisable(
                        entry, model.$array_of,
                        context.concat(index));
                })
                .then(function (c) {
                    serialisable[index] = c;
                });
        });
        return promises.then(function () {
            return serialisable;
        });

    } else if (typeof model.$map_of !== "undefined") {
        if (typeof data !== "object")
            throw Utils.report(TAG, ".getSerialisable: map expected at ",
                context.join('.'), data);
        serialisable = {};
        Utils.forEach(data, function (entry, index) {
            promises = promises.then(function () {
                    return DataModel.getSerialisable(
                        entry, model.$map_of,
                        context.concat(index));
                })
                .then(function (c) {
                    serialisable[index] = c;
                });
        });
        return promises.then(function () {
            return serialisable;
        });

    } else if (typeof model.$class === "function" &&
        typeof data.getSerialisable === "function") {
        // objects can override getSerialisable
        // Could also use model.prototype.getSerialisable
        return data.getSerialisable(model);
    } else if (DataModel.builtin_types.indexOf(model.$class) >= 0) {
        return Q(data);
    }

    serialisable = {};
    // Only serialise fields described in the model. All other fields
    // in the object are ignored.
    Utils.forEach(model, function (fieldmodel, key) {
        if (DataModel.private_key[key])
            return;
        promises = promises
            .then(function () {
                var promise = DataModel.getSerialisable(
                    data[key], fieldmodel,
                    context.concat(key));
                return promise
            })
            .then(function (c) {
                if (typeof c !== "undefined") {
                    serialisable[key] = c;
                }
            });
    });

    return promises.then(function () {
        return serialisable;
    });
};

/**
 * Follow the path from the root to the node at the end of the path,
 * and call a function on the the root of the subtree there and the
 * model that describes it. Will throw if the path does not describe a
 * node with a corresponding model.
 * @param {object} root the root of the tree
 * @param {object} model the root of the model that describes the tree
 * @param path either a path expresses as a /-separated
 * string or an already-split array of path components.
 * @param {function} fn (node, subtreemodel, parentnode, key)
 * where node is the root of the subtree, nodemodel is the model for the
 * subtree, parentnode is the node that contains the subtree and key is
 * the key for the subtree in the parent. parentnode and key will be
 * undefined if the path is empty.
 * @return the result of the call to fn
 */
DataModel.at = function (root, model, path, fn) {
    if (typeof path === "string") {
        // Convert string path to array of path components
        path = path.split(/\/+/);
        while (path.length > 0 && path[0].length == 0)
            path.shift();
    }

    DataModel.check(model);

    var node = root;
    var node_model = model;
    var parent;
    var key;

    // Walk down the node and model trees
    var i = 0;
    while (i < path.length) {
        if (typeof node === "undefined")
            throw Utils.report(
                TAG, ".at: no node at ", path, "[", i, "]");
        if (typeof node_model === "undefined")
            throw Utils.report(
                TAG, ".at: no model at ", path, "[", i, "]");
        parent = node;
        key = path[i++];
        node = node[key];
        if (typeof node_model.$array_of !== "undefined")
            node_model = node_model.$array_of;
        else if (typeof node_model.$map_of !== "undefined")
            node_model = node_model.$map_of;
        else
            node_model = node_model[key];
    }
    if (i < path.length)
        throw Utils.report(TAG, ".at: could not find ", path);
    return fn(node, node_model, parent, key);
};

/**
 * Promise to load data that observes the given data model
 * from a file.
 * @param file the file to load from
 * @param {object} model the data model to check against
 * @return {promise} promise that returns the loaded data
 * @public
 */
DataModel.loadData = function (file, model) {
    "use strict";

    DataModel.check(model);

    var Q = require("q");
    var Fs = require("fs");
    var readFilePromise = Q.denodeify(Fs.readFile);

    return readFilePromise(file)
        .then(function (code) {
            var data = Utils.eval(code, file);
            data = DataModel.remodel("", data, model);
            data._readFrom = file;
            return data;
        });
};

/**
 * Promise to save that part of the data as is specified by the model
 * to a file
 * @param {object} data the data to save
 * @param {object} model the data model
 * @param file file to write to, or undefined to rewrite the
 * file the data was read from.
 * @return {promise} promise that returns after saving
 */
DataModel.saveData = function (data, model, file) {
    "use strict";

    DataModel.check(model);

    if (typeof file === "undefined")
        file = this._readFrom;

    var Q = require("q");
    var Fs = require("fs");
    var writeFilePromise = Q.denodeify(Fs.writeFile);

    return DataModel.getSerialisable(data, model).then(function (remod) {
        return writeFilePromise(
            Utils.expandEnvVars(file),
            JSON.stringify(remod, null, 2), "utf8");
    });
};

/**
 * Generate the help string for the given model
 * @param {object} model the data model to generate help for
 */
DataModel.help = function (model, index) {
    DataModel.check(model);

    // index is used for formatting and is not visible to callers
    function shift_right(s) {
        return s.replace(/\n/g, "\n ");
    }

    var docstring = [];

    if (index)
        docstring.push(index + ":");

    if (typeof model === "function")
        return "<" + model.name + ">";

    if (model.$optional)
        docstring.push("(optional)");
    if (typeof model.$class !== "undefined") {
        if (typeof model.$class === "string")
            docstring.push('<' + model.$class + '>');
        else if (typeof model.$class === "function")
            docstring.push('<' + model.$class.name + '>');
        else
            docstring.push(model.$class); // wtf?
    }

    if (typeof model.$doc === "string")
        docstring.push(model.$doc);

    if (typeof model.$class === "undefined" || model.$class === "object") {
        if (typeof model.$array_of !== "undefined") {
            docstring.push('[\n');
            docstring.push(shift_right(DataModel.help(model.$array_of)));
            docstring.push("\n]");
        } else if (typeof model.$map_of !== "undefined") {
            docstring.push('{*\n');
            docstring.push(shift_right(DataModel.help(model.$map_of)));
            docstring.push("\n*}");
        } else {
            docstring.push("{\n");
            var sub = [];
            for (var i in model) {
                if (i.charAt(0) !== '$')
                    sub.push(DataModel.help(model[i], i));
            }
            docstring.push(shift_right(sub.join('\n')));
            docstring.push("\n}");
        }
    }
    return docstring.join(' ').replace(/ +\n/g, "\n");
}

/* Inner classes, helpers for file operations */

/**
 * DataModel inner class for handling filenames specified in serialisable data.
 * The constructor uses the $mode (default "r") specified in the
 * model to verify the status of the target file. This supports the
 * following modes:
 * e: the file must exist
 * r: the file must be readable
 * w: the file must be writable
 * x: the file must be executable
 * @param filename the file name
 * @param index the name passed to the constructor by DataModel
 * @param {object} model the model for this file
 * @class
 */
function File(filename, index, model) {
    this.data = filename;
    if (typeof model !== "undefined") {
        // Got a model to check against. This should always be the case
        // except in tests.
        var fnm = Utils.expandEnvVars(filename);
        var $mode = model.$mode;
        if (typeof $mode === "undefined")
            $mode = "r";
        var Fs = require("fs");
        var mode = Fs.constants.F_OK;

        if ($mode.indexOf("r") >= 0)
            mode = mode | Fs.constants.R_OK;

        if ($mode.indexOf("x") >= 0)
            mode = mode | Fs.constants.X_OK;

        if ($mode.indexOf("e") >= 0 && !Fs.existsSync(fnm)) {
            throw "Bad " + index + ": " + filename + " does not exist";
        }

        if ($mode.indexOf("w") >= 0) {
            mode = mode | Fs.constants.W_OK;

            if (Fs.existsSync(fnm)) {
                Fs.access(fnm, mode,
                    function (err) {
                        if (err)
                            throw "Bad " + index + ": " + filename + " " +
                                +$mode + " mode check failed: " +
                                err;
                    });
            }
        } else if ($mode.indexOf("w") >= 0) {
            // Just make sure we can write, and clear down the file
            Fs.writeFileSync(fnm, "", {
                mode: mode
            });
        }
    }
};

DataModel.File = File;

File.Model = {
    $class: DataModel.File,
    $doc: "Filename"
};

/**
 * Generate a simple string representation of this object suitable
 * for use in debugging and in serialisation
 */
File.prototype.toString = function () {
    return this.data;
};

/**
 * Promise to write a new value to the file
 * @param value new data to write to the file
 */
File.prototype.write = function (value) {
    var Q = require("q");
    var Fs = require("fs");
    var writeFilePromise = Q.denodeify(Fs.writeFile);
    return writeFilePromise(Utils.expandEnvVars(this.data), value, "utf8");
};

/**
 * Promise to read the file
 */
File.prototype.read = function () {
    var Q = require("q");
    var Fs = require("fs");
    var readFilePromise = Q.denodeify(Fs.readFile);
    return readFilePromise(Utils.expandEnvVars(this.data));
};

File.prototype.getSerialisable = function () {
    var Q = require("q");
    return Q(this.data);
};

/**
 * Subclass of DataModel.File representing a datum that can either
 * be a simple text string, or a file name.
 * When the object is constructed, if the string in the data is a
 * valid existing filename, then the object is assumed to refer to a file.
 * Otherwise it is assumed to be raw text data.
 * The $mode of the model is assumed to be at least "er"
 * @param data either a file name or raw data
 * @param index the name passed to the constructor by DataModel
 * @param {object} model the model for the datum
 * @class
 */
function TextOrFile(data, index, model) {
    DataModel.File.call(this, data, index, model);
    var Fs = require("fs");
    this.is_file = Fs.existsSync(Utils.expandEnvVars(data));
};
TextOrFile.prototype = new DataModel.File();
DataModel.TextOrFile = TextOrFile;

TextOrFile.Model = {
    $class: DataModel.TextOrFile,
    $doc: "Filename or plain text"
};

/**
 * Promise to read the datum
 */
TextOrFile.prototype.read = function () {
    if (this.is_file)
        return DataModel.File.prototype.read.call(this);
    else {
        var data = this.data;
        var Q = require("q");
        return Q.fcall(function () {
            return data;
        });
    }
};

/**
 * Promise to update the datum
 * @param value new value to store in the datum
 */
TextOrFile.prototype.write = function (value) {
    if (this.is_file)
        return DataModel.File.prototype.write.call(this, value);
    else {
        this.data = value;
        var Q = require("q");
        return Q(true);
    }
};

module.exports = DataModel;


text:
var MetaDataBuilder = function(threads, fileSize, url, method, port, headers, options) {

	this.data = {
		threads: threads,
		url: url,
		method: method,
		port: port,
		downloadSize: fileSize,
		headers: headers
	};
	options = options || {};
	this.block = options.block || 1024 * 10;
};

MetaDataBuilder.prototype.execute = function(callback) {

	this.callback = callback;
	var buffer = new Buffer(this.block);
	buffer.fill(' ');

	var dataString = JSON.stringify(this.data);
	buffer.write(dataString);

	var result = {
		data: buffer,
		position: this.data.downloadSize
	};

	this.callback(null, result);
};

module.exports = MetaDataBuilder;


text:
package syntaxtree;

import treedisplay.TreeDisplayable;
import treedisplay.TreeDrawException;
import visitor.Visitor;

/**
 * a unary expression (abstract)
 */
public abstract class UnExp extends Exp {

	// instance variables filled in by constructor
	public Exp exp; // the operand

	/**
	 * constructor
	 * @param pos file position
	 * @param aexp the operand
	 */
	public UnExp(int pos, Exp aexp) {
		super(pos);
		exp=aexp;
	}

	/*************** remaining methods are visitor- and display-related ****************/

	public Object accept(Visitor v) {
		return v.visitUnExp(this);
	}

	public TreeDisplayable getDrawTreeSubobj(int n) throws TreeDrawException {
		switch (n) {
		case 0: return exp;
		}
		throw new TreeDrawException();
	}
}


text:
class CreateWiStores < ActiveRecord::Migration

  def self.up
    create_table :wi_stores do |t|
      t.column :name, :string
      t.column :regex, :string
    end
  end

  def self.down
    drop_table :wi_stores
  end
end


text:
$(window).load(init);

function init() {
    switch (location.pathname) {
        case "/embarc-utils/inventory/stock_in.php":
            stock_in.initialize();
            break;

        case "/embarc-utils/inventory/stock_out.php":
            stock_out.initialize();
            break;

        case "/embarc-utils/inventory/preferences.php":
            preferences.initialize();
            break;

        case "/embarc-utils/inventory/stock_finder.php":
            stock_finder.initialize();
            break;

        case "/embarc-utils/inventory/clients.php":
            clients.initialize();
            break;

        case "/embarc-utils/inventory/trackers.php":
            trackers.initialize();
            break;
    }
}

var stock_in = {
    initialize: function () {
        var self = this;

        //initialize datepicker
        $('.datepicker').datepicker({
            'autoclose': true,
            'format': "dd/mm/yyyy"
        });

        //add change listener on model drop down
        $("#model").on('change', function (ev) {
            self.fillModelDetails($(this).val());
        });

        //validate and submit form when done
        $("#stockInForm").validate({
            highlight: function (element, errorClass, validClass) {
                $(element).parent().addClass("has-error");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).parent().removeClass("has-error");
            },
            submitHandler: function (form) {
                //set current state of submit button to loading
                $("#saveStockButton").button('loading');

                window.setTimeout(function () {
                    //call method to submit this form
                    self.saveStock();

                    //reset state of submit button
                    $("#saveStockButton").button('reset');

                    //focus on serial number to start again quickly
                    $("#serial").focus();
                }, (self.defaults.waitTime * 1000));
                return false;
            }
        });

        var $in_invoice = $("#in_invoice"),
            $serial = $("#serial")

        //set focus to invoice number by default
        $in_invoice.focus();

        //prevent form submit on enter press on invoice number (useful when scanned via barcode scanner) also set focus to serial number if required
        $($in_invoice).keypress(function (ev) {
            if (ev.which === 13) {
                ev.preventDefault();
                $serial.focus();
            }
        });

        //prevent form submit on enter press on serial number (useful when scanned via barcode scanner) also set focus to IMEI if required
        $($serial).keypress(function (ev) {
            if (ev.which === 13) {
                ev.preventDefault();
                $("#imei").focus();
            }
        });

        //fetch list of trackers from server
        this.getTrackersList();

        //fetch preferences and update defaults
        preferences.get.apply(this, [this.updateDefaults]);
    },

    getTrackersList: function () {
        var self = this;

        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getTrackers",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                //save list of trackers
                self.trackers = data;

                //fill trackers in model select drop down
                fillDropDown("#model", self.trackers, "model", "model");

                //set default values
                if (self.setDefaults) self.setDefaults();
            }
        });
    },

    fillModelDetails: function (model) {
        for (var i = 0; i < this.trackers.length; i++) {
            if (model == this.trackers[i].model) {
                $("#vendor").val(this.trackers[i].vendorName);
                $("#warranty").val(this.trackers[i].warranty);
            }
        }
    },

    defaults: {
        'serial': "",
        'imei': "",
        'model': "VT-62",
        'dateOfPurchase': "today",
        'invoice_no': ""
    },

    trackers: [],

    updateDefaults: function (preferences) {
        if (!preferences) preferences = {};

        this.defaults['autoSave'] = + preferences.autoSaveIN || 0;
        this.defaults['model'] = preferences.model || this.defaults.model;
        this.defaults['waitTime'] = + preferences.waitTimeIN || 1;

        //disable auto save if required
        if (!this.defaults.autoSave) {
            //prevent form submit on enter press on IMEI number (useful when scanned via barcode scanner)
            $("#imei").keypress(function (ev) {
                if (ev.which === 13) {
                    ev.preventDefault();
                }
            });
        }
    },

    //set default values for each form field
    setDefaults: function () {
        var self = this;

        $.each(this.defaults, function (key, value) {
            //set today's date
            if (key === "dateOfPurchase" && value === "today") {
                $('.datepicker').datepicker('setDate', new Date());
            } else {
                if (key == "model") self.fillModelDetails(value);

                //set all other values
                $("#" + key).val(value);
            }
        });
    },

    saveStock: function () {
        var jsn = createObject(["stockInForm"]),
            self = this;
        dropElements(jsn, ["warranty", "vendor"]);
        jsn.dateOfPurchase = getFormattedDate(jsn.dateOfPurchase);

        $.ajax({
            type: "POST",
            url: "/embarc-utils/php/main.php?util=inventory&fx=saveStockItem",
            async: true,
            data: jsn,
            success: function (result) {
                var $errorMsg = $("#errorMessage-1"),
                    $successMsg = $("#successMessage-1"),
                    $imeiExistsMsg = $("#errorMessage-2");

                if (strncmp(result, "SUCCESS", 7)) {
                    //hide error message and show success message
                    $errorMsg.hide();
                    $imeiExistsMsg.hide();
                    $successMsg.show();

                    //clear quick fill fields
                    self.clearToQuickFill();

                    //hide success message after 5 seconds
                    window.setTimeout(function () {
                        $successMsg.hide();
                    }, 5000);
                } else if (strncmp(result, "IMEI_EXISTS", 11)) {
                    $successMsg.hide();
                    $errorMsg.hide();
                    $imeiExistsMsg.show();
                } else {
                    //show error message and hide success message
                    $successMsg.hide();
                    $imeiExistsMsg.hide();
                    $errorMsg.show();
                }
            }
        });
    },

    clearToQuickFill: function () {
        $("#imei,#serial").val("");
    }
};

var stock_out = {
    initialize: function () {
        var self = this;

        //initialize datepicker
        $('.datepicker').datepicker({
            'autoclose': true,
            'format': "dd/mm/yyyy"
        });

        var $out_invoice = $("#out_invoice"),
            $imei = $("#imei");

        //initially, set focus to invoice number
        $out_invoice.focus();

        $imei.on("blur", function () {
            self.checkForItemInStock($(this).val());
        });

        $imei.keypress(function (ev) {
            if (ev.which === 13) {
                ev.preventDefault();
                $imei.blur();
            }
        });

        //validate and submit form when done
        $("#stockOutForm").validate({
            highlight: function (element, errorClass, validClass) {
                $(element).parent().addClass("has-error");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).parent().removeClass("has-error");
            },
            submitHandler: function (form) {
                //set current state of submit button to loading
                $("#saveStockButton").button('loading');

                //call method to submit this form after a few wait seconds
                window.setTimeout(function () {
                    self.saveStockOut();

                    //reset button loading state
                    $("#saveStockButton").button('reset');

                    //focus on serial number to start again quickly
                    $imei.focus();
                }, (self.defaults.waitTime * 1000));

                return false;
            }
        });

        //get list of clients
        this.getClients();

        //fetch preferences and update defaults
        preferences.get.apply(this, [this.updateDefaults]);
    },

    checkForItemInStock: function (value) {
        var self = this,
            $errorMsg = $("#errorMessage-1"),
                    $imei_no_exist = $("#errorMessage-2"),
                    $successMsg = $("#successMessage-1");

        $.ajax({
            type: "GET",
            async: true,
            url: "/embarc-utils/php/main.php?util=inventory&fx=getItemInStock&prop=imei&val=" + value,
            success: function (data) {
                if (strncmp(data, "ERROR", 5)) {
                    //show an error
                    $errorMsg.hide();
                    $imei_no_exist.show();
                    $successMsg.hide();

                    //clean up
                    $("#serial").val("");
                    $("#model").val("");
                    $("#id").val("");
                } else {
                    //hide the error message, if visible, or anyways
                    $errorMsg.hide();
                    $imei_no_exist.hide();
                    $successMsg.hide();

                    //fill it up
                    data = getJSONFromString(data);
                    $("#serial").val(data["serial"]);
                    $("#model").val(data["model"]);
                    $("#id").val(data["id"]);

                    //submit form if preference allows
                    if(self.defaults.autoSave) $("#stockOutForm").submit();
                }
            }
        });
    },

    getClients: function () {
        var self = this;

        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getClients",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                //fill clients list in client select drop down
                fillDropDown("#clientID", data, "name", "id");
            }
        });
    },

    defaults: {
        'serial': "",
        'imei': "",
        'out_warranty': "12",
        'dateOfSale': "today",
        'out_invoice_no': ""
    },

    updateDefaults: function (preferences) {
        if (!preferences) preferences = {};

        this.defaults['autoSave'] = +preferences.autoSaveOUT || 0;
        this.defaults['waitTime'] = +preferences.waitTimeOUT || 1;
        this.defaults['out_warranty'] = +preferences.out_warranty || this.defaults.out_warranty;

        //fill in default values
        this.setDefaults();
    },

    //set default values for each form field
    setDefaults: function () {
        $.each(this.defaults, function (key, value) {
            //set today's date
            if (key === "dateOfSale" && value === "today")
                $('.datepicker').datepicker('setDate', new Date());
            else
                //set all other values
                $("#" + key).val(value);
        });
    },

    saveStockOut: function () {
        var jsn = createObject(["stockOutForm"]),
            self = this;
        dropElements(jsn, ["model", "serial"]);
        jsn.dateOfSale = getFormattedDate(jsn.dateOfSale);

        $.ajax({
            type: "POST",
            async: true,
            url: "/embarc-utils/php/main.php?util=inventory&fx=updateStockItem",
            data: jsn,
            success: function (result) {
                var $errorMsg = $("#errorMessage-1"),
                    $imei_no_exist = $("#errorMessage-2"),
                    $successMsg = $("#successMessage-1");

                if (strncmp(result, "SUCCESS", 7)) {
                    //hide error message and show success message
                    $errorMsg.hide();
                    $imei_no_exist.hide();
                    $successMsg.show();

                    //hide success message after 5 seconds
                    window.setTimeout(function () {
                        $successMsg.hide();
                    }, 5000);

                    //clear quick fill fields
                    self.clearToQuickFill();
                } else if (strncmp(result, "IMEI_NOT_STOCK", 14)) {
                    //show message that IMEI number does not exist
                    $errorMsg.hide();
                    $imei_no_exist.show();
                    $successMsg.hide();
                } else {
                    //an error occured - dunno what?
                    $errorMsg.show();
                    $imei_no_exist.hide();
                    $successMsg.hide();
                }
            }
        });
    },

    clearToQuickFill: function () {
        $("#imei,#serial,#model").val("");
    }
};

var preferences = {
    initialize: function () {
        var self = this;

        stock_in.getTrackersList.apply(this);

        //remove independent 'required' messages
        jQuery.validator.messages.required = "";

        $("#preferencesForm").validate({
            highlight: function (element, errorClass, validClass) {
                $(element).parent().addClass("has-error");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).parent().removeClass("has-error");
            },
            submitHandler: function (form) {
                self.savePreferences();

                return false;
            },
            invalidHandler: function (ev, validator) {
                if (validator.numberOfInvalids()) {
                    $("#errorMessage-1").show();
                } else {
                    $("#errorMessage-1").hide();
                }
            }
        });
    },

    setDefaults: function () {
        //fill default preferences
        this.get(this.fill);
    },

    get: function (callback) {
        var self = this;

        $.ajax({
            type: "GET",
            async: true,
            url: "/embarc-utils/php/main.php?util=misc&fx=getPreferences&module=2",
            success: function (data) {
                try {
                    data = getJSONFromString(data);
                } catch (ex) {
                    console("Inventory: preferences not found");
                    return;
                }

                if (callback) callback.apply(self, [data]);
            }
        });
    },

    savePreferences: function () {
        $("#errorMessage-1").hide();
        $("#successMessage-1").hide();

        var jsdata = createObject(["preferencesForm"]);

        $.ajax({
            type: "POST",
            async: true,
            data: jsdata,
            url: "/embarc-utils/php/main.php?util=misc&fx=savePreferences&module=2",
            success: function (result) {
                if (strncmp(result, "success", 7)) {
                    $("#successMessage-1").show();

                    window.setTimeout(function () {
                        $("#successMessage-1").hide();
                    }, 5000);
                } else {
                    $("#errorMessage-1").show();
                }
            }
        });
    },

    trackers: [],

    fill: function (prefs) {
        var formFiller = new FormFiller(document.getElementById("preferencesForm", ""));

        formFiller.fillData(prefs);
    }
};

var stock_finder = {
    initialize: function () {
        var self = this;

        //attach change event on search criteria
        $("#stockSearchForm input[type=radio]").on("change", function () {
            self.changeCriteria(this.value);
        });

        //start with search textbox focussed
        $("#searchTextbox").focus();

        //get list of trackers
        this.getTrackersList(function (trackersList) {
            //save list of trackers
            self.trackers = trackersList;

            //if trackers list is scheduled to be filled on load
            if (self.doFillOnLoad.models) {
                self.fillTrackersList();
                self.doFillOnLoad.models = false;
            }
        });

        //get list of clients
        this.getClientsList(function (clientsList) {
            //save list of clients
            self.clients = clientsList;

            //fill clients mapper
            for (var i = 0, l = self.clients.length; i < l; i++) {
                self.clientsMap[self.clients[i].id] = self.clients[i].name;
            }

            //if clients list is scheduled to be filled on load
            if (self.doFillOnLoad.clients) {
                self.fillClientsList();
                self.doFillOnLoad.clients = false;
            }
        });

        //find something in stock
        $("#searchStockButton").on("click", function () {
            self.find(self.fillResultsInTable);
        });
    },

    changeCriteria: function (criteria) {
        var $searchDropdown = $("#searchDropdown"),
            $searchTextbox = $("#searchTextbox");

        $searchDropdown.val("");
        $searchTextbox.val("");
        $searchTextbox.datepicker("remove");

        switch (criteria) {
            case "model":
                //show dropdown and hide textbox
                $searchDropdown.show();
                $searchTextbox.hide();

                //populate list of trackers if available otherwise schedule for later
                if (this.trackers.length > 0) {
                    this.fillTrackersList();
                } else {
                    this.doFillOnLoad.models = true;
                    this.doFillOnLoad.clients = false;
                }
                break;

            case "clientID":
                //show dropdown and hide textbox
                $searchDropdown.show();
                $searchTextbox.hide();

                //populate list of clients if available otherwise schedule for later
                if (this.clients.length > 0) {
                    this.fillClientsList();
                } else {
                    this.doFillOnLoad.models = false;
                    this.doFillOnLoad.clients = true;
                }
                break;

            case "dateOfSale":
                //hide dropdown and show textbox
                $searchDropdown.hide();
                $searchTextbox.show();

                //initialize datepicker
                $("#searchTextbox").datepicker({
                    'autoclose': true,
                    'format': "yyyy-mm-dd"
                });
                //show datepicker and set todays date to current date
                $searchTextbox.datepicker("show");
                $searchTextbox.datepicker("setDate", new Date());
                break;

            default:
                //hide dropdown and show textbox
                $searchDropdown.hide();
                $searchTextbox.show();
                break;
        }
    },

    doFillOnLoad: {
        models: false,
        clients: false
    },

    trackers: [],

    getTrackersList: function (callback) {
        var self = this;

        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getTrackers",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                if (callback) callback(data);
            }
        });
    },

    fillTrackersList: function () {
        $("#searchDropdown").html("");

        //fill trackers in model select drop down
        fillDropDown("#searchDropdown", this.trackers, "model", "model");
    },

    clients: [],

    clientsMap: {},

    getClientsList: function (callback) {
        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getClients",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                if (callback) callback(data);
            }
        });
    },

    fillClientsList: function () {
        $("#searchDropdown").html("");

        //fill clients list in client select drop down
        fillDropDown("#searchDropdown", this.clients, "name", "id");
    },

    find: function (callback) {
        var self = this,
            jsdata = createObject(["stockSearchForm"]);
        if (jsdata.query1 === "" && jsdata.query2 === "") return;

        jsdata.query = jsdata.query1 || jsdata.query2;
        delete jsdata.query1;
        delete jsdata.query2;

        $.ajax({
            type: "POST",
            async: true,
            data: jsdata,
            url: "/embarc-utils/php/main.php?util=inventory&fx=search",
            success: function (data) {
                data = getJSONFromString(data);

                if (callback) callback.apply(self, [data, +jsdata.count]);
            }
        });
    },

    fillResultsInTable: function (results, isCount) {
        var $tableResult = $("#tableResult"),
            $countResult = $("#countResult");

        //clear all previous results
        $tableResult.html("");
        $countResult.html("");

        //hide all containers
        $("#tableResultContainer").hide();
        $("#countResultContainer").hide();
        $("#noResults").hide();

        //populate new results
        if (isCount) {
            $("#countResultContainer").show();

            $countResult.html(results);
        } else {

            var l = results.length;
            if (l > 0) {
                $("#tableResultContainer").show();
            }
            else {
                $("#noResults").show();
            }

            for (var i = 0; i < l; i++) {
                $tableResult.append(this.createResultsRow(results[i]));
            }
        }
    },

    createResultsRow: function (rowData) {
        var remainingWarranty = "";
        if (rowData.dateOfSale) {
            remainingWarranty = this.getRemainingWarranty(rowData.dateOfSale, +rowData.out_warranty);
            if (remainingWarranty < 0) remainingWarranty = "Expired " + Math.abs(remainingWarranty) + " days ago";
            else remainingWarranty += " days";
        }

        return '<tr>'+
                    (+rowData.inStock ? "<td class=\"stock\">In Stock" : "<td class=\"sold\">Sold") + '</td>\
                    <td>' + rowData.imei + '</td>\
                    <td>' + rowData.serial + '</td>\
                    <td>' + rowData.model + '</td>\
                    <td>' + dateStampToString(rowData.dateOfPurchase) + '</td>\
                    <td>' + rowData.in_invoice + '</td>\
                    <td>' + rowData.in_username + '</td>\
                    <td>' + (rowData.dateOfSale ? dateStampToString(rowData.dateOfSale) : "") + '</td>\
                    <td>' + (rowData.clientID ? this.clientsMap[rowData.clientID] : "") + '</td>\
                    <td>' + rowData.out_invoice + '</td>\
                    <td>' + remainingWarranty + '</td>\
                    <td>' + rowData.out_username + '</td>\
                </tr>';
    },

    //get remaining warranty, using sales date and warranty provided
    getRemainingWarranty: function (salesDate, warrantyProvided) {
        var today = new Date(),
            salesDate = new Date(salesDate),
            difference = dayDiff(salesDate, today),
            warrantyEnd = new Date(salesDate);
        warrantyEnd.setMonth(warrantyEnd.getMonth() + warrantyProvided);

        return Math.floor(dayDiff(salesDate, warrantyEnd) - difference);
    }
};

var clients = {
    initialize: function () {
        var self = this;

        //focus on company name - default
        $("#name").focus();

        //validate and submit form when done
        $("#addClientForm").validate({
            highlight: function (element, errorClass, validClass) {
                $(element).parent().addClass("has-error");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).parent().removeClass("has-error");
            },
            submitHandler: function (form) {
                //set current state of submit button to loading
                $("#saveClientButton").button('loading');

                //call method to submit this form
                self.save();

                //reset state of submit button
                $("#saveClientButton").button('reset');

                //focus on company name to start again quickly
                $("#name").focus();

                //clear form when done
                form.reset();

                return false;
            }
        });

        //populate list of clients
        this.get(function (clientsList) {
            self.checkNfill(clientsList);
        });
    },

    checkNfill: function (clientsList) {
        var self = this,
            l = clientsList.length,
            i = 0;

        if (l > 0) {
            //hide no clients message
            $("#noClients").hide();

            //show clients list
            $("#clientsListContainer").show();

            for (; i < l; i++) self.addClient2List(clientsList[i]);
        } else {
            //show no clients message
            $("#noClients").show();

            //hide clients list
            $("#clientsListContainer").hide();
        }
    },

    //get list of clients
    get: function (callback) {
        var self = this;

        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getClients",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                if (callback) callback(data);
            }
        });
    },

    //save a client
    save: function () {
        var self = this,
            jsdata = createObject(["addClientForm"]);

        $.ajax({
            type: "POST",
            async: true,
            url: "/embarc-utils/php/main.php?util=inventory&fx=saveClient",
            data: jsdata,
            success: function (result) {
                if (strncmp(result, "SUCCESS", 7)) {
                    self.checkNfill([jsdata]);
                }
            }
        });
    },

    //add a client to list of clients
    addClient2List: function (clientDetails) {
        $("#clientsList").append(this.createClientRow(clientDetails));
    },

    //create a row for list of clients
    createClientRow: function (rowData) {
        return '<tr>\
                    <td>' + rowData.name + '</td>\
                    <td>' + rowData.person + '</td>\
                    <td>' + rowData.email + '</td>\
                </tr>';
    }
};

var trackers = {
    initialize: function () {
        var self = this;

        //focus model
        $("#model").focus();

        //validate and submit form when done
        $("#addTrackerForm").validate({
            highlight: function (element, errorClass, validClass) {
                $(element).parent().addClass("has-error");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).parent().removeClass("has-error");
            },
            submitHandler: function (form) {
                //set current state of submit button to loading
                $("#saveTrackerButton").button('loading');

                //call method to submit this form
                self.save();

                //reset state of submit button
                $("#saveTrackerButton").button('reset');

                //focus on company name to start again quickly
                $("#model").focus();

                //clear form when done
                form.reset();

                return false;
            }
        });

        //populate list of trackers
        this.get(function (trackersList) {
            self.checkNfill(trackersList);
        });
    },

    checkNfill: function (trackersList) {
        var self = this,
            l = trackersList.length,
            i = 0;

        if (l > 0) {
            //hide no trackers message
            $("#noTrackers").hide();

            //show trackers list
            $("#trackersListContainer").show();

            for (; i < l; i++) self.addTracker2List(trackersList[i]);
        } else {
            //show no trackers message
            $("#noTrackers").show();

            //hide trackers list
            $("#trackersListContainer").hide();
        }
    },

    //get list of trackers
    get: function (callback) {
        var self = this;

        $.ajax({
            type: "GET",
            url: "/embarc-utils/php/main.php?util=inventory&fx=getTrackers",
            async: true,
            success: function (data) {
                data = getJSONFromString(data);

                if (callback) callback(data);
            }
        });
    },

    //save a tracker
    save: function () {
        var self = this,
            jsdata = createObject(["addTrackerForm"]);

        $.ajax({
            type: "POST",
            async: true,
            url: "/embarc-utils/php/main.php?util=inventory&fx=saveTracker",
            data: jsdata,
            success: function (result) {
                if (strncmp(result, "SUCCESS", 7)) {
                    self.checkNfill([jsdata]);
                }
            }
        });
    },

    //add a client to list of trackers
    addTracker2List: function (details) {
        $("#trackersList").append(this.createTrackerRow(details));
    },

    //create a row for list of trackers
    createTrackerRow: function (rowData) {
        return '<tr>\
                    <td>' + rowData.model + '</td>\
                    <td>' + rowData.vendorName + '</td>\
                    <td>' + rowData.vendorModel + '</td>\
                    <td>' + rowData.warranty + '</td>\
                </tr>';
    }
};


text:
This package is composer ready. This is PHP Remote file downloader. Create an array of files to download and run the downloader. It will
download the file in the folder specified. Especially helpful when we have images to download remotely programmatically.

# Usage
see example.php