<commit_before>var Node = function() {
    var value = undefined;
    var leftNode = undefined;
    var rightNode = undefined;
    var parent = undefined;
    this.getValue = function() {
        return value;
    }
    this.setValue = function(val) {
        value = val;
    }

    this.getLeftNode = function() {
        return leftNode;
    }
    this.setLeftNode = function(node) {
        leftNode = node;
    }

    this.getRightNode = function() {
        return rightNode;
    }
    this.setRightNode = function(node) {
        rightNode = node;
    }

    this.getParent = function() {
        return parent;
    }
    this.setParent = function(node) {
        parent = node;
    }
}
//補註解
var BiTree = function() {
    var depth = 0;
    var count = 0;
    var root = undefined;
    var generation = [];
    this.init = function(val) {//直接初始化
        root = new Node();
        root.setValue(val);
        generation[0] = [root];
        count = 1;
    }
    this.add = function(val) {
        if (!checkInit()) {
            console.error("Bi Tree is not init.");
            return false;
        }
        var getNodeFather = compare(val);

        var fatherNode = getNodeFather[0];
        var fatherValue = fatherNode.getValue();
        var height = getNodeFather[1] + 1; //length

        if (fatherValue === val) {
            console.log("Already has this value.");
            return false;
        }
        var tmpNode = new Node();
        tmpNode.setValue(val);
        tmpNode.setParent(fatherNode);

        if (generation[height] !== undefined) {
            generation[height].push(tmpNode);
        } else {
            generation[height] = [tmpNode];
        }
        if (val > fatherValue) {
            fatherNode.setRightNode(tmpNode);
        } else if (val < fatherValue) {
            fatherNode.setLeftNode(tmpNode);
        }
        count += 1;
        return true;
    }
    this.getDepth = function() {
        return generation;//改level
        // return generation.length;
    }
    this.getCount = function() {
        return count;
    }
    this.getRoot = function() {
        return root;
    }
    this.traverse = function(node) {//補前後traverse
        if (node === undefined) {
            return;
        }
        console.log(node.getValue());
        this.traverse(node.getLeftNode());
        this.traverse(node.getRightNode());
    }
    this.getBrother = function(node) {
        var tmpNodeValue = node.getValue();
        var tmpParentNode = node.getParent();
        var tmpNode = undefined;
        if (tmpParentNode.getLeftNode().getValue() === tmpNodeValue) {
            tmpNode = tmpParentNode.getRightNode();
        }
        tmpNode = tmpParentNode.getLeftNode();
        if (tmpNode !== undefined) {
            return tmpNode;
        }
        return;
    }
    this.delete = function(val) {//刪除整個tree方法
        var tmpNode = compare(val);
        var maxNode = undefined;

        var height = tmpNode[1]; //index to length
        tmpNode = tmpNode[0];
        if (tmpNode.getValue() === val) {
            if (tmpNode.getLeftNode() === undefined && tmpNode.getRightNode() === undefined) {
                var parentNode = tmpNode.getParent();

                if (parentNode.getLeftNode()!== undefined && parentNode.getLeftNode().getValue() === val) {
                    var index = generation[height].indexOf(tmpNode);
                    generation[height].splice(index, 1);
                    checkGeneration();
                    parentNode.setLeftNode(undefined);
                    tmpNode = undefined;
                } else if (parentNode.getRightNode() !== undefined && parentNode.getRightNode().getValue() === val) {
                    var index = generation[height].indexOf(tmpNode);
                    generation[height].splice(index, 1);
                    checkGeneration();
                    parentNode.setRightNode(undefined);
                    tmpNode = undefined;
                } else {
                    console.error("error, parentNode is ", parentNode, ".");
                    return;
                }
            } else {
                maxNode = findMax(tmpNode.getLeftNode(), height + 1);
                var maxNodeHeight = maxNode[1];
                maxNode = maxNode[0];
                var maxNodeParent = maxNode.getParent();
                var maxNodeChild = maxNode.getLeftNode();
                if (maxNodeChild !== undefined) {
                    maxNodeChild.setParent(maxNodeParent);
                    maxNodeParent.setRightNode(maxNodeChild);
                    //移動肉粽
                    moveGeneration(maxNodeChild,maxNodeHeight);
                } else {
                    maxNodeParent.setRightNode(undefined);
                }
                //replace
                var index = generation[height].indexOf(tmpNode);
                generation[height][index] = maxNode;
                //remove
                var index = generation[maxNodeHeight].indexOf(maxNode);
                generation[maxNodeHeight].splice(index, 1);
                checkGeneration();
                tmpNode.setValue(maxNode.getValue());
            }
        }
        count -= 1;
    }

    this.query = function(node, val) {
        if (val === node.getValue()) {
            return node;
        }
        if (node.getLeftNode() === undefined && node.getRightNode() === undefined) {
            console.warn("Has not this value and return the parent node.");//傳空值
            return node;
        }
        if (val < node.getValue()) {
            return this.query(node.getLeftNode(), val);
        } else if (val > node.getValue()) {
            return this.query(node.getRightNode(), val);
        }
    }
    this.test = function() {
        //checking code use
        console.log("test");
    }
    var checkInit = function() {
        if (root === undefined) {
            return false;
        }
        return true;
    }
    var compare = function(val) {
        var tmpNode = root;
        var newNodeValue = val;
        for (var i = 0; i < generation.length; i++) {
            if (newNodeValue > tmpNode.getValue()) {
                var node = tmpNode.getRightNode();
                if (node === undefined) {
                    return [tmpNode, i];
                }
                tmpNode = node;
            } else if (newNodeValue < tmpNode.getValue()) {
                var node = tmpNode.getLeftNode();
                if (node === undefined) {
                    return [tmpNode, i];
                }
                tmpNode = node;
            } else if (newNodeValue === tmpNode.getValue()) {
                return [tmpNode, i];
            }
        }
    }
    var findMax = function(node, height) {
        var tmpNode = node;

        while (tmpNode.getRightNode() !== undefined) {
            tmpNode = tmpNode.getRightNode();
            height += 1;
        }
        return [tmpNode, height];
    }
    var moveGeneration = function(node, height) {
        if (node === undefined) {
            return;
        }
        var nodeHeight = height + 1;
        //remove
        var index = generation[nodeHeight].indexOf(node);
        generation[nodeHeight].splice(index, 1);
        checkGeneration();
        //add
        var length = generation[height].length;
        generation[height].push(node);
        moveGeneration(node.getLeftNode(), nodeHeight);
        moveGeneration(node.getRightNode(), nodeHeight);
    }
    var checkGeneration = function () {
        var arr = generation;
        for (var i = 0; i < arr.length; i++) {
            if (arr[i].length <= 0 ) {
                arr.splice(i, 1);
            }
        }
    }
}
/*
var a = new BiTree()
a.init(5)
a.add(3)
a.add(1)
a.add(2)
a.add(4)
a.add(7)
a.traverse(a.getRoot())
var b = a.getRoot().getRightNode();
a.getBrother(b);
var b = a.query(a.getRoot(),3);
b.getValue();
var b = a.query(a.getRoot(),8);
b.getValue();
var a = new BiTree()
a.init(50)
a.add(30)
a.add(10)
a.add(20)
a.add(40)
a.add(70)
a.add(60)
a.add(18)
a.add(45)
a.traverse(a.getRoot())
a.delete(30)
a.traverse(a.getRoot())
a.getDepth();
a.getCount();
 */<commit_msg>fix bug and add annotation<commit_after>/**
 * [Node Class]
 */
var Node = function() {
    var value = undefined;
    var leftNode = undefined;
    var rightNode = undefined;
    var parent = undefined;

    /**
     * [getValue 取得節點值]
     * @return {[Number]} [回傳節點值]
     */
    this.getValue = function() {
        return value;
    }

    /**
     * [setValue 設定節點值]
     * @param {[Number]} val [設定節點值]
     */
    this.setValue = function(val) {
        value = val;
    }

    /**
     * [getLeftNode 取得左側節點]
     * @return {[Node]} [回傳左側節點]
     */
    this.getLeftNode = function() {
        return leftNode;
    }

    /**
     * [setLeftNode 設定左側節點]
     * @param {[Node]} node [設定左側節點]
     */
    this.setLeftNode = function(node) {
        leftNode = node;
    }

    /**
     * [getRightNode 取得右側節點]
     * @return {[Node]} [回傳右側節點]
     */
    this.getRightNode = function() {
        return rightNode;
    }

    /**
     * [setRightNode 設定右側節點]
     * @param {[Node]} node [設定右側節點]
     */
    this.setRightNode = function(node) {
        rightNode = node;
    }

    /**
     * [getParent 取得父節點]
     * @return {[Node]} [回傳父節點]
     */
    this.getParent = function() {
        return parent;
    }

    /**
     * [setParent 設定父節點]
     * @param {[type]} node [設定父節點]
     */
    this.setParent = function(node) {
        parent = node;
    }
}

/**
 * [BiTree Class]
 */
var BiTree = function() {
    var depth = 0;
    var count = 0;
    var root = undefined;
    var generation = [];

    /**
     * [init 初始化Bi Tree物件]
     * @param  {[number]} val [初始化傳入的參數]
     */
    this.init = function() { //直接初始化
        root = new Node();
    }

    /**
     * [add 加入數值進行排序]
     * @param {[number]} val [要加入的數值]
     * @return {[bool]} [回傳是否成功新增，成功回傳True，失敗回傳False]
     */
    this.add = function(val) {
        //檢查Root是否有值
        if (root.getValue() === undefined) {
            root.setValue(val);
            generation[0] = [root];
            count = 1;
            return true;
        }
        //比較val該至於哪個節點底下
        var getNodeFather = compare(val);

        var fatherNode = getNodeFather[0];
        var fatherValue = fatherNode.getValue();
        var height = getNodeFather[1] + 1; //length
        //如果有了，則不新增，並回傳false
        if (fatherValue === val) {
            console.log("Already has this value.");
            return false;
        }
        //新增節點
        var tmpNode = new Node();
        tmpNode.setValue(val);
        tmpNode.setParent(fatherNode);

        //將節點新增至generation(同代陣列)中
        if (generation[height] !== undefined) {
            generation[height].push(tmpNode);
        } else {
            generation[height] = [tmpNode];
        }
        if (val > fatherValue) {
            fatherNode.setRightNode(tmpNode);
        } else if (val < fatherValue) {
            fatherNode.setLeftNode(tmpNode);
        }
        //節點總數+1
        count += 1;
        return true;
    }

    /**
     * [getLevel 取得Tree的深度]
     * @return {[number]} [回傳目前的節點深度]
     */
    this.getLevel = function() {
        return generation.length;
    }

    /**
     * [getCount 取得節點總數]
     * @return {[number]} [回傳目前的節點數量]
     */
    this.getCount = function() {
        return count;
    }

    /**
     * [getRoot 取得樹根節點]
     * @return {[Node]} [回傳樹根的節點]
     */
    this.getRoot = function() {
        return root;
    }

    /**
     * [inorderTraverse 前序遍歷BiTree]
     * @param  {[Node]} node [傳入要遍歷的節點]
     */
    this.inorderTraverse = function(node) {
        if (node === undefined) {
            return;
        }
        console.log(node.getValue());
        this.inorderTraverse(node.getLeftNode());
        this.inorderTraverse(node.getRightNode());
    }

    /**
     * [preorderTraverse 中序遍歷BiTree]
     * @param  {[Node]} node [傳入要遍歷的節點]
     */
    this.preorderTraverse = function(node) {
        if (node === undefined) {
            return;
        }
        this.preorderTraverse(node.getLeftNode());
        console.log(node.getValue());
        this.preorderTraverse(node.getRightNode());
    }

    /**
     * [postorderTraverse 後序遍歷BiTree]
     * @param  {[Node]} node [傳入要遍歷的節點]
     */
    this.postorderTraverse = function(node) {
        if (node === undefined) {
            return;
        }
        this.postorderTraverse(node.getLeftNode());
        this.postorderTraverse(node.getRightNode());
        console.log(node.getValue());
    }

    /**
     * [getBrother 取得兄弟節點]
     * @param  {[Node]} node [傳入要尋找兄弟的節點]
     * @return {[NOde]}      [回傳兄弟節點]
     */
    this.getBrother = function(node) {
        if(node === undefined){
            node = root;
        }
        if(node === root){
            console.warn("root has not brother.");
            return;
        }
        var tmpNodeValue = node.getValue();
        var tmpParentNode = node.getParent();
        var tmpNode = undefined;
        if (tmpParentNode.getLeftNode().getValue() === tmpNodeValue) {
            tmpNode = tmpParentNode.getRightNode();
        }
        tmpNode = tmpParentNode.getLeftNode();
        if (tmpNode !== undefined) {
            return tmpNode;
        }
        return;
    }

    /**
     * [deleteVal 刪除指定數值]
     * @param  {[Number]} val [傳入要刪除的數值]
     */
    this.deleteVal = function(val) { //刪除整個tree方法
        //使用者未輸入值
        if(val === undefined){
            console.warn("Please keyin param like delete(val).");
            return;
        }
        //取得父節點
        var tmpNode = compare(val);
        var maxNode = undefined;

        var height = tmpNode[1]; //index to length
        tmpNode = tmpNode[0];

        if (tmpNode.getValue() === val) {//如果父節點值與val相同
            //無子節點的情況
            if (tmpNode.getLeftNode() === undefined && tmpNode.getRightNode() === undefined) {
                var parentNode = tmpNode.getParent();
                //判斷是父節點的左側節點還是右側節點，以進行移除父節點的關聯
                if (parentNode.getLeftNode() !== undefined && parentNode.getLeftNode().getValue() === val) {
                    var index = generation[height].indexOf(tmpNode);
                    generation[height].splice(index, 1);
                    checkGeneration();
                    parentNode.setLeftNode(undefined);
                    tmpNode = undefined;
                } else if (parentNode.getRightNode() !== undefined && parentNode.getRightNode().getValue() === val) {
                    var index = generation[height].indexOf(tmpNode);
                    generation[height].splice(index, 1);
                    checkGeneration();
                    parentNode.setRightNode(undefined);
                    tmpNode = undefined;
                } else {
                    console.error("error, parentNode is ", parentNode, ".");
                    return;
            }
            } else {//有子節點的情況
                //取得節點最大值，並進行替換作業(以維持二元素的架構)
                maxNode = findMax(tmpNode.getLeftNode(), height + 1);//取得中序立即前行者(inorder immediate successor)
                var maxNodeHeight = maxNode[1];
                maxNode = maxNode[0];
                var maxNodeParent = maxNode.getParent();
                var maxNodeChild = maxNode.getLeftNode();
                //如果節點最大值的左側有節點時，必須要將左側節點與最大值節點的父節點關聯，才可移動最大值節點
                if (maxNodeChild !== undefined) {
                    maxNodeChild.setParent(maxNodeParent);
                    maxNodeParent.setRightNode(maxNodeChild);
                    //移動肉粽
                    moveGeneration(maxNodeChild, maxNodeHeight);
                } else {
                    maxNodeParent.setRightNode(undefined);
                }
                //同代這列的替換及移除
                //replace
                var index = generation[height].indexOf(tmpNode);
                generation[height][index] = maxNode;
                //remove
                var index = generation[maxNodeHeight].indexOf(maxNode);
                generation[maxNodeHeight].splice(index, 1);
                checkGeneration();
                tmpNode.setValue(maxNode.getValue());
            }
        }
        count -= 1;
    }

    /**
     * [deleteTree 刪除指定節點及子節點]
     * @param  {[Node]} node [[傳入要刪除的節點]
     * @return {[bool]}      [回傳是否刪除成功，成功回傳True，不成功回傳False]
     */
    this.deleteTree = function(node) {
        var tmp = compare(node.getValue());
        if (tmp[0] !== node) {
            console.warn("has not this node");
            return false;
        }
        var fatherNode = tmp[0].getParent();
        var height = tmp[1];
        //判斷是父節點的左側還是右側節點，並移除關聯
        if (fatherNode.getRightNode() === node) {
            fatherNode.setRightNode(undefined);
        }
        if (fatherNode.getLeftNode() === node) {
            fatherNode.setLeftNode(undefined);
        }
        // to do
        // 移除關聯，取得高度，移除同代陣列，移除節點，移除總數
        // var tmp = compare(node.value);
        // var index = generation[maxNodeHeight].indexOf(maxNode);
        // generation[maxNodeHeight].splice(index, 1);
        // checkGeneration();
        // tmpNode.setValue(maxNode.getValue());
    }
    /**
     * [query 由指定的節點開始查詢數值]
     * @param  {[Node]} node [傳入要查詢的節點]
     * @param  {[Number]} val  [傳入要查詢的數值]
     * @return {[Node]}      [回傳查詢到的節點]
     */
    this.query = function(node, val) {
        //使用者未傳入值
        if(node === undefined && val === undefined){
            console.warn("Please keyin param which is node and val.");
            return;
        }
        //找到相同的值
        if (val === node.getValue()) {
            return node;
        }
        //節點無子節點時
        if (node.getLeftNode() === undefined && node.getRightNode() === undefined) {
            console.warn("Has not this value.");
            return;
        }

        if (val < node.getValue()) {
            if (node.getLeftNode() === undefined) {
                console.warn("Has not this value.");
                return;
            }
            //
            return this.query(node.getLeftNode(), val);
        } else if (val > node.getValue()) {
            if (node.getRightNode() === undefined) {
                console.warn("Has not this value.");
                return;
            }
            return this.query(node.getRightNode(), val);
        }
    }

    /**
     * [test 測試用]
     */
    this.test = function() {
        //checking code use
        console.log("test");
    }

    /**
     * [checkInit 檢查是否初始化]
     * @return {[bool]} [已經初始化，回傳True，否則回傳False]
     */
    var checkInit = function() {
        if (root === undefined) {
            return false;
        }
        return true;
    }

    /**
     * [compare 將二元樹與所傳入的值進行比較，回傳所傳入的值的父節點及父節點的所屬深度]
     * @param  {[Number]} val [欲進行比較的值]
     * @return {[Array]}     [回傳陣列，其長度為2，Array[0]為所傳入的值的父節點，Array[1]為該父節點的節點深度]
     */
    var compare = function(val) {
        var tmpNode = root;
        var newNodeValue = val;
        for (var i = 0; i < generation.length; i++) {
            if (newNodeValue > tmpNode.getValue()) {//val比較大，向右
                var node = tmpNode.getRightNode();
                if (node === undefined) {
                    return [tmpNode, i];
                }
                tmpNode = node;
            } else if (newNodeValue < tmpNode.getValue()) {//val比較小，向左
                var node = tmpNode.getLeftNode();
                if (node === undefined) {
                    return [tmpNode, i];
                }
                tmpNode = node;
            } else if (newNodeValue === tmpNode.getValue()) {//一樣大，回傳該節點
                return [tmpNode, i];
            }
        }
    }

    /**
     * [findMax 取得最大值]
     * @param  {[Node]} node   [要搜尋的節點]
     * @param  {[Number]} height [該節點的深度值]
     * @return {[Array]}        [回傳陣列，其長度為2，Array[0]為傳入的節點中之最大值，Array[1]為該節點的節點深度]
     */
    var findMax = function(node, height) {
        var tmpNode = node;
        while (tmpNode.getRightNode() !== undefined) {
            tmpNode = tmpNode.getRightNode();
            height += 1;
        }
        return [tmpNode, height];
    }

    /**
     * [moveGeneration 移動同代陣列中的節點位置]
     * @param  {[Node]} node   [要被移動的節點]
     * @param  {[Number]} height [要被移動的節點的父節點深度]
     */
    var moveGeneration = function(node, height) {
        if (node === undefined) {
            return;
        }
        var nodeHeight = height + 1;
        //remove
        var index = generation[nodeHeight].indexOf(node);
        generation[nodeHeight].splice(index, 1);
        checkGeneration();
        //add
        var length = generation[height].length;
        generation[height].push(node);
        moveGeneration(node.getLeftNode(), nodeHeight);
        moveGeneration(node.getRightNode(), nodeHeight);
    }

    /**
     * [checkGeneration 檢查同代陣列，若該陣列中無值，則於同代陣列中刪除]
     */
    var checkGeneration = function() {
        var arr = generation;
        for (var i = 0; i < arr.length; i++) {
            if (arr[i].length <= 0) {
                arr.splice(i, 1);
            }
        }
    }

    this.init();
}
/*
var a = new BiTree()
a.init(5)
a.add(3)
a.add(1)
a.add(2)
a.add(4)
a.add(7)
a.traverse(a.getRoot())
var b = a.getRoot().getRightNode();
a.getBrother(b);
var b = a.query(a.getRoot(),3);
b.getValue();
var b = a.query(a.getRoot(),8);
b.getValue();
var a = new BiTree()
a.init(50)
a.add(30)
a.add(10)
a.add(20)
a.add(40)
a.add(70)
a.add(60)
a.add(18)
a.add(45)
a.traverse(a.getRoot())
a.delete(30)
a.traverse(a.getRoot())
a.getDepth();
a.getCount();
 */
<|endoftext|>


<commit_before>/**
 * @file Position static
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

export default {

    TOP_LEFT: 'top-left',
    TOP: 'top',
    TOP_RIGHT: 'top-right',

    BOTTOM_LEFT: 'bottom-left',
    BOTTOM: 'bottom',
    BOTTOM_RIGHT: 'bottom-right',

    LEFT_TOP: 'left-top',
    LEFT: 'left',
    LEFT_BOTTOM: 'left-bottom',

    RIGHT_TOP: 'right-top',
    RIGHT: 'right',
    RIGHT_BOTTOM: 'right-bottom'

};<commit_msg>update Position<commit_after>/**
 * @file Position static
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

export default {

    TOP_LEFT: 'top-left',
    TOP: 'top',
    TOP_RIGHT: 'top-right',

    BOTTOM_LEFT: 'bottom-left',
    BOTTOM: 'bottom',
    BOTTOM_RIGHT: 'bottom-right',

    LEFT_TOP: 'left-top',
    LEFT: 'left',
    LEFT_BOTTOM: 'left-bottom',

    RIGHT_TOP: 'right-top',
    RIGHT: 'right',
    RIGHT_BOTTOM: 'right-bottom',

    CENTER: 'center'

};<|endoftext|>


<commit_before>var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    common = require('./common'),
    ObjectId = Schema.ObjectId,
    utils = require('./../utils');

var tag_suggestions =  {
    tag_name: String,
    tag_offers: {type:[ObjectId], ref:'User',query:common.FIND_USER_QUERY}
};

var InformationItem = module.exports = utils.revertibleModel(new Schema({
    title: {type: String, required: true},
    tooltip:  {type: String, editable:false},
    subject_id:[{type:ObjectId, ref:'Subject',required:true}],
    subjects:[{type:ObjectId, ref:'Subject'}],
    category:{type:String, "enum":['test', 'statistics', 'infographic', 'graph'], required:true, editable:false},
    text_field:{type:Schema.Types.Text, required:true, editable:false},
    text_field_preview:{type:Schema.Types.Html},
    image_field: {type:Schema.Types.File,required:true},
    image_field_preview: {type:Schema.Types.File, editable:false},
    attachment:{type:Schema.Types.File},
    tags:{type:[String], index:true, editable:false},
    users:{type:[ObjectId], ref:'User',query:common.FIND_USER_QUERY,editable:false},
    discussions:[{type:ObjectId,  limit: 1000, ref:'Discussion', index:true}],
    is_visible:{type:Boolean, 'default':true},
    creation_date:{type:Date, 'default':Date.now,editable:false},
    is_hot_object:{type:Boolean, 'default':false, editable:false},
    is_hot_info_item: {type:Boolean, 'default':false, editable:false},
    tag_suggestions: {type:[tag_suggestions] ,editable:false},
    like_counter: {type: Number, 'default': 0, editable: false},
    view_counter: {type: Number, 'default': '0', editable:false},

    //this two fields are for user suggestion of InformationItem, when admin create this it will remain false
    created_by: {creator_id:{type: ObjectId, ref: 'User',query:common.FIND_USER_QUERY, editable: false}, did_user_created_this_item: {type: Boolean, 'default': false, editable: false}},
    discussion_counter:{type:Number,'default':0, editable:false},
    status: {type: String, "enum": ['approved', 'denied', 'waiting'], editable:false},
    gamification: {
        rewarded_creator_for_high_liked: {type: String, 'default': false, editable: false},
        rewarded_creator_for_approval: {type: String, 'default': false, editable: false}
    },
    gui_order:{type:Number,'default':9999999,editable:false},
    is_hidden:{type:Boolean,'default':true},
    _preview:{type:Schema.Types.Mixed,link:'/information_items/{_id}',editable:false}
}, {strict: true}));

InformationItem.methods.toString = function(){
    return this.title || '';
}<commit_msg>information-item -- dismiss unnecessary requirements<commit_after>var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    common = require('./common'),
    ObjectId = Schema.ObjectId,
    utils = require('./../utils');

var tag_suggestions =  {
    tag_name: String,
    tag_offers: {type:[ObjectId], ref:'User',query:common.FIND_USER_QUERY}
};

var InformationItem = module.exports = utils.revertibleModel(new Schema({
    title: {type: String, required: true},
    tooltip:  {type: String, editable:false},
    subject_id:[{type:ObjectId, ref:'Subject',required:true}],
    subjects:[{type:ObjectId, ref:'Subject'}],
    category:{type:String, "enum":['test', 'statistics', 'infographic', 'graph'], editable:false},
    text_field:{type:Schema.Types.Text, editable:false},
    text_field_preview:{type:Schema.Types.Html},
    image_field: {type:Schema.Types.File,required:true},
    image_field_preview: {type:Schema.Types.File, editable:false},
    attachment:{type:Schema.Types.File},
    tags:{type:[String], index:true, editable:false},
    users:{type:[ObjectId], ref:'User',query:common.FIND_USER_QUERY,editable:false},
    discussions:[{type:ObjectId,  limit: 1000, ref:'Discussion', index:true}],
    is_visible:{type:Boolean, 'default':true},
    creation_date:{type:Date, 'default':Date.now,editable:false},
    is_hot_object:{type:Boolean, 'default':false, editable:false},
    is_hot_info_item: {type:Boolean, 'default':false, editable:false},
    tag_suggestions: {type:[tag_suggestions] ,editable:false},
    like_counter: {type: Number, 'default': 0, editable: false},
    view_counter: {type: Number, 'default': '0', editable:false},

    //this two fields are for user suggestion of InformationItem, when admin create this it will remain false
    created_by: {creator_id:{type: ObjectId, ref: 'User',query:common.FIND_USER_QUERY, editable: false}, did_user_created_this_item: {type: Boolean, 'default': false, editable: false}},
    discussion_counter:{type:Number,'default':0, editable:false},
    status: {type: String, "enum": ['approved', 'denied', 'waiting'], editable:false},
    gamification: {
        rewarded_creator_for_high_liked: {type: String, 'default': false, editable: false},
        rewarded_creator_for_approval: {type: String, 'default': false, editable: false}
    },
    gui_order:{type:Number,'default':9999999,editable:false},
    is_hidden:{type:Boolean,'default':true},
    _preview:{type:Schema.Types.Mixed,link:'/information_items/{_id}',editable:false}
}, {strict: true}));

InformationItem.methods.toString = function(){
    return this.title || '';
}<|endoftext|>


<commit_before><commit_msg>Solved BitRoller using JavaScript<commit_after><|endoftext|>


<commit_before>import Vue from 'vue'
import Vuetify from 'vuetify'
import { regex } from 'vee-validate/dist/rules'
import { extend } from 'vee-validate'

import i18n from '@/plugins/i18n'
import formBaseComponents from '@/plugins/formBaseComponents'

import { mount as mountComponent } from '@vue/test-utils'
import EColorPicker from '../EColorPicker'

Vue.use(Vuetify)
Vue.use(formBaseComponents)

extend('regex', regex)

describe('An EColorPicker', () => {
  let vuetify

  const COLOR_1 = '#ff0000'
  const COLOR_2 = '#ff00ff'
  const INVALID_COLOR = 'some new color'

  const flushPromises = () => new Promise((resolve) => setImmediate(resolve))

  const waitForDebounce = () => new Promise((resolve) => setTimeout(resolve, 110))

  const createMouseEvent = (x, y) => {
    return {
      preventDefault: () => {},
      clientX: x,
      clientY: y
    }
  }

  const rectMock = {
    bottom: 0,
    height: 100,
    width: 100,
    left: 0,
    right: 0,
    top: 0,
    x: 0,
    y: 0
  }

  const mount = (options) => mountComponent(EColorPicker, { vuetify, i18n, ...options })

  beforeEach(() => {
    vuetify = new Vuetify()
  })

  test('renders', async () => {
    const wrapper = mount({
      propsData: {
        value: COLOR_1
      }
    })
    await flushPromises()
    expect(wrapper.find('input[type=text]').element.value).toBe(COLOR_1)
  })

  test('updates v-model when the value changes', async () => {
    const wrapper = mountComponent({
      data: () => ({ color: COLOR_1 }),
      template: '
<e-color-picker v-model="color"></e-color-picker>
',
      components: { 'e-color-picker': EColorPicker }
    }, {
      vuetify,
      i18n
    })
    expect(wrapper.vm.color).toBe(COLOR_1)
    const inputSpy = jest.fn()
    wrapper.findComponent(EColorPicker).vm.$on('input', (event) => inputSpy(event))
    const input = wrapper.find('input[type=text]')
    await input.setValue(COLOR_2)
    await waitForDebounce()
    expect(inputSpy).toBeCalledTimes(1)
    expect(inputSpy).toBeCalledWith(COLOR_2)
    expect(wrapper.vm.color).toBe(COLOR_2)
  })

  test('validates the input', async () => {
    const wrapper = mount({
      propsData: {
        value: COLOR_1,
        name: 'Color'
      }
    })
    const input = wrapper.find('input[type=text]')
    await input.setValue(INVALID_COLOR)
    await waitForDebounce()
    expect(wrapper.text()).toContain('Color is not valid.')
  })

  test('updates its value when a color is picked', async () => {
    // prevent "[Vuetify] Unable to locate target [data-app]" warnings
    document.body.setAttribute('data-app', 'true')
    const wrapper = mount({
      propsData: {
        value: COLOR_2
      }
    })
    await waitForDebounce()
    // open the color picker
    const openPicker = wrapper.find('button')
    await openPicker.trigger('click')
    // click inside the color picker canvas to select a different color
    const canvas = wrapper.findComponent({ name: 'v-color-picker-canvas' })
    canvas.vm.$el.getBoundingClientRect = () => rectMock
    canvas.vm.handleClick(createMouseEvent(10, 10))
    await flushPromises()
    // click the save button
    const closeButton = wrapper.find('[data-testid="action-ok"]')
    await closeButton.trigger('click')
    await waitForDebounce()
    expect(wrapper.find('input[type=text]').element.value).toBe('#E6CFE6')
  })
})
<commit_msg>Add test case for 3-digit color codes<commit_after>import Vue from 'vue'
import Vuetify from 'vuetify'
import { regex } from 'vee-validate/dist/rules'
import { extend } from 'vee-validate'

import i18n from '@/plugins/i18n'
import formBaseComponents from '@/plugins/formBaseComponents'

import { mount as mountComponent } from '@vue/test-utils'
import EColorPicker from '../EColorPicker'

Vue.use(Vuetify)
Vue.use(formBaseComponents)

extend('regex', regex)

describe('An EColorPicker', () => {
  let vuetify

  const COLOR_1 = '#ff0000'
  const COLOR_2 = '#ff00ff'
  const INVALID_COLOR = 'some new color'

  const flushPromises = () => new Promise((resolve) => setImmediate(resolve))

  const waitForDebounce = () => new Promise((resolve) => setTimeout(resolve, 110))

  const createMouseEvent = (x, y) => {
    return {
      preventDefault: () => {},
      clientX: x,
      clientY: y
    }
  }

  const rectMock = {
    bottom: 0,
    height: 100,
    width: 100,
    left: 0,
    right: 0,
    top: 0,
    x: 0,
    y: 0
  }

  const mount = (options) => mountComponent(EColorPicker, { vuetify, i18n, ...options })

  beforeEach(() => {
    vuetify = new Vuetify()
  })

  test('renders', async () => {
    const wrapper = mount({
      propsData: {
        value: COLOR_1
      }
    })
    await flushPromises()
    expect(wrapper.find('input[type=text]').element.value).toBe(COLOR_1)
  })

  test('updates v-model when the value changes', async () => {
    const wrapper = mountComponent({
      data: () => ({ color: COLOR_1 }),
      template: '
<e-color-picker v-model="color"></e-color-picker>
',
      components: { 'e-color-picker': EColorPicker }
    }, {
      vuetify,
      i18n
    })
    expect(wrapper.vm.color).toBe(COLOR_1)
    const inputSpy = jest.fn()
    wrapper.findComponent(EColorPicker).vm.$on('input', (event) => inputSpy(event))
    const input = wrapper.find('input[type=text]')
    await input.setValue(COLOR_2)
    await waitForDebounce()
    expect(inputSpy).toBeCalledTimes(1)
    expect(inputSpy).toBeCalledWith(COLOR_2)
    expect(wrapper.vm.color).toBe(COLOR_2)
  })

  test('validates the input', async () => {
    const wrapper = mount({
      propsData: {
        value: COLOR_1,
        name: 'Color'
      }
    })
    const input = wrapper.find('input[type=text]')
    await input.setValue(INVALID_COLOR)
    await waitForDebounce()
    expect(wrapper.text()).toContain('Color is not valid.')
  })

  test('updates its value when a color is picked', async () => {
    // prevent "[Vuetify] Unable to locate target [data-app]" warnings
    document.body.setAttribute('data-app', 'true')
    const wrapper = mount({
      propsData: {
        value: COLOR_2
      }
    })
    await waitForDebounce()
    // open the color picker
    const openPicker = wrapper.find('button')
    await openPicker.trigger('click')
    // click inside the color picker canvas to select a different color
    const canvas = wrapper.findComponent({ name: 'v-color-picker-canvas' })
    canvas.vm.$el.getBoundingClientRect = () => rectMock
    canvas.vm.handleClick(createMouseEvent(10, 10))
    await flushPromises()
    // click the save button
    const closeButton = wrapper.find('[data-testid="action-ok"]')
    await closeButton.trigger('click')
    await waitForDebounce()
    expect(wrapper.find('input[type=text]').element.value).toBe('#E6CFE6')
  })

  test('accepts 3-digit hex color codes', async () => {
    // prevent "[Vuetify] Unable to locate target [data-app]" warnings
    document.body.setAttribute('data-app', 'true')
    const wrapper = mount({
      propsData: {
        value: '#abc'
      }
    })
    await waitForDebounce()
    // open the color picker
    const openPicker = wrapper.find('button')
    await openPicker.trigger('click')
    // click the save button
    const closeButton = wrapper.find('[data-testid="action-ok"]')
    await closeButton.trigger('click')
    await waitForDebounce()
    expect(wrapper.find('input[type=text]').element.value).toBe('#AABBCC')
  })
})
<|endoftext|>


<commit_before>"use strict";
var warp10Finalize = require("warp10/finalize");
var eventDelegation = require("./event-delegation");
var win = window;
var defaultDocument = document;
var createFragmentNode = require("../vdom/morphdom/fragment")
    .___createFragmentNode;
var componentsUtil = require("./util");
var componentLookup = componentsUtil.___componentLookup;
var addComponentRootToKeyedElements =
    componentsUtil.___addComponentRootToKeyedElements;
var ComponentDef = require("./ComponentDef");
var registry = require("./registry");
var domData = require("./dom-data");
var componentsByDOMNode = domData.___componentByDOMNode;
var serverRenderedGlobals = {};
var serverComponentRootNodes = {};
var keyedElementsByComponentId = {};

var FLAG_WILL_RERENDER_IN_BROWSER = 1;

function indexServerComponentBoundaries(node, runtimeId, stack) {
    var componentId;
    var ownerId;
    var ownerComponent;
    var keyedElements;
    var nextSibling;
    var runtimeLength = runtimeId.length;
    stack = stack || [];

    node = node.firstChild;
    while (node) {
        nextSibling = node.nextSibling;
        if (node.nodeType === 8) {
            // Comment node
            var commentValue = node.nodeValue;
            if (commentValue.slice(0, runtimeLength) === runtimeId) {
                var firstChar = commentValue[runtimeLength];

                if (firstChar === "^" || firstChar === "#") {
                    stack.push(node);
                } else if (firstChar === "/") {
                    var endNode = node;
                    var startNode = stack.pop();
                    var rootNode;

                    if (startNode.parentNode === endNode.parentNode) {
                        rootNode = createFragmentNode(
                            startNode.nextSibling,
                            endNode
                        );
                    } else {
                        rootNode = createFragmentNode(
                            endNode.parentNode.firstChild,
                            endNode
                        );
                    }

                    componentId = startNode.nodeValue.substring(
                        runtimeLength + 1
                    );
                    firstChar = startNode.nodeValue[runtimeLength];

                    if (firstChar === "^") {
                        var parts = componentId.split(/ /g);
                        var key = parts[2];
                        ownerId = parts[1];
                        componentId = parts[0];
                        if ((ownerComponent = componentLookup[ownerId])) {
                            keyedElements = ownerComponent.___keyedElements;
                        } else {
                            keyedElements =
                                keyedElementsByComponentId[ownerId] ||
                                (keyedElementsByComponentId[ownerId] = {});
                        }
                        addComponentRootToKeyedElements(
                            keyedElements,
                            key,
                            rootNode,
                            componentId
                        );
                    }

                    serverComponentRootNodes[componentId] = rootNode;

                    startNode.parentNode.removeChild(startNode);
                    endNode.parentNode.removeChild(endNode);
                }
            }
        } else if (node.nodeType === 1) {
            // HTML element node
            var markoKey = node.getAttribute("data-marko-key");
            var markoProps = node.getAttribute("data-marko");
            if (markoKey) {
                var separatorIndex = markoKey.indexOf(" ");
                ownerId = markoKey.substring(separatorIndex + 1);
                markoKey = markoKey.substring(0, separatorIndex);
                if ((ownerComponent = componentLookup[ownerId])) {
                    keyedElements = ownerComponent.___keyedElements;
                } else {
                    keyedElements =
                        keyedElementsByComponentId[ownerId] ||
                        (keyedElementsByComponentId[ownerId] = {});
                }
                keyedElements[markoKey] = node;
            }
            if (markoProps) {
                markoProps = JSON.parse(markoProps);
                Object.keys(markoProps).forEach(function(key) {
                    if (key.slice(0, 2) === "on") {
                        eventDelegation.___addDelegatedEventHandler(
                            key.slice(2)
                        );
                    }
                });
            }
            indexServerComponentBoundaries(node, runtimeId, stack);
        }

        node = nextSibling;
    }
}

function invokeComponentEventHandler(component, targetMethodName, args) {
    var method = component[targetMethodName];
    if (!method) {
        throw Error("Method not found: " + targetMethodName);
    }

    method.apply(component, args);
}

function addEventListenerHelper(el, eventType, isOnce, listener) {
    var eventListener = listener;
    if (isOnce) {
        eventListener = function(event) {
            listener(event);
            el.removeEventListener(eventType, eventListener);
        };
    }

    el.addEventListener(eventType, eventListener, false);

    return function remove() {
        el.removeEventListener(eventType, eventListener);
    };
}

function addDOMEventListeners(
    component,
    el,
    eventType,
    targetMethodName,
    isOnce,
    extraArgs,
    handles
) {
    var removeListener = addEventListenerHelper(el, eventType, isOnce, function(
        event
    ) {
        var args = [event, el];
        if (extraArgs) {
            args = extraArgs.concat(args);
        }

        invokeComponentEventHandler(component, targetMethodName, args);
    });
    handles.push(removeListener);
}

function initComponent(componentDef, doc) {
    var component = componentDef.___component;

    if (!component || !component.___isComponent) {
        return; // legacy
    }

    component.___reset();
    component.___document = doc;

    var isExisting = componentDef.___isExisting;

    if (isExisting) {
        component.___removeDOMEventListeners();
    }

    var domEvents = componentDef.___domEvents;
    if (domEvents) {
        var eventListenerHandles = [];

        domEvents.forEach(function(domEventArgs) {
            // The event mapping is for a direct DOM event (not a custom event and not for bubblign dom events)

            var eventType = domEventArgs[0];
            var targetMethodName = domEventArgs[1];
            var eventEl = component.___keyedElements[domEventArgs[2]];
            var isOnce = domEventArgs[3];
            var extraArgs = domEventArgs[4];

            addDOMEventListeners(
                component,
                eventEl,
                eventType,
                targetMethodName,
                isOnce,
                extraArgs,
                eventListenerHandles
            );
        });

        if (eventListenerHandles.length) {
            component.___domEventListenerHandles = eventListenerHandles;
        }
    }

    if (component.___mounted) {
        component.___emitLifecycleEvent("update");
    } else {
        component.___mounted = true;
        component.___emitLifecycleEvent("mount");
    }
}

/**
 * This method is used to initialized components associated with UI components
 * rendered in the browser. While rendering UI components a "components context"
 * is added to the rendering context to keep up with which components are rendered.
 * When ready, the components can then be initialized by walking the component tree
 * in the components context (nested components are initialized before ancestor components).
 * @param  {Array<marko-components/lib/ComponentDef>} componentDefs An array of ComponentDef instances
 */
function initClientRendered(componentDefs, doc) {
    // Ensure that event handlers to handle delegating events are
    // always attached before initializing any components
    eventDelegation.___init(doc);

    doc = doc || defaultDocument;
    var len = componentDefs.length;
    var componentDef;
    var i;

    for (i = len; i--; ) {
        componentDef = componentDefs[i];
        trackComponent(componentDef);
    }

    for (i = len; i--; ) {
        componentDef = componentDefs[i];
        initComponent(componentDef, doc);
    }
}

/**
 * This method initializes all components that were rendered on the server by iterating over all
 * of the component IDs.
 */
function initServerRendered(renderedComponents, doc) {
    var type = typeof renderedComponents;
    var runtimeId;

    if (type !== "object") {
        var componentsKey =
            "$" +
            (type === "string"
                ? renderedComponents + "_components"
                : "components");
        renderedComponents = win[componentsKey];

        if (renderedComponents && renderedComponents.forEach) {
            renderedComponents.forEach(function(renderedComponent) {
                initServerRendered(renderedComponent, doc);
            });
        }

        win[componentsKey] = {
            concat: initServerRendered
        };

        return;
    }

    doc = doc || defaultDocument;

    renderedComponents = warp10Finalize(renderedComponents);

    runtimeId = renderedComponents.r;
    var componentDefs = renderedComponents.w;
    var typesArray = renderedComponents.t;
    var markoGlobalsKey = "$" + runtimeId + "G";

    // Ensure that event handlers to handle delegating events are
    // always attached before initializing any components
    indexServerComponentBoundaries(doc, runtimeId);
    eventDelegation.___init(doc);

    var globals = win[markoGlobalsKey];
    if (globals) {
        serverRenderedGlobals = warp10Finalize(globals);
        delete win[markoGlobalsKey];
    }

    // hydrate components top down (leaf nodes last)
    // and return an array of functions to mount these components
    var componentMountFns = componentDefs.map(function(componentDef) {
        componentDef = ComponentDef.___deserialize(
            componentDef,
            typesArray,
            serverRenderedGlobals,
            registry
        );

        var mount = hydrateComponentAndGetMount(componentDef, doc);

        if (!mount) {
            // hydrateComponentAndGetMount will return false if there is not rootNode
            // for the component.  If this is the case, we'll wait until the
            // DOM has fully loaded to attempt to init the component again.
            doc.addEventListener("DOMContentLoaded", function() {
                mount = hydrateComponentAndGetMount(componentDef, doc);

                if (!mount) {
                    indexServerComponentBoundaries(doc, runtimeId);
                    mount = hydrateComponentAndGetMount(componentDef, doc);
                }

                mount();
            });
        }

        return mount;
    });

    // mount components bottom up (leaf nodes first)
    componentMountFns.reverse().forEach(function(mount) {
        if (mount) mount();
    });
}

function hydrateComponentAndGetMount(componentDef, doc) {
    var componentId = componentDef.id;
    var component = componentDef.___component;
    var rootNode = serverComponentRootNodes[componentId];
    var renderResult;

    if (rootNode) {
        delete serverComponentRootNodes[componentId];

        component.___rootNode = rootNode;
        componentsByDOMNode.set(rootNode, component);
        component.___keyedElements =
            keyedElementsByComponentId[componentId] || {};

        delete keyedElementsByComponentId[componentId];

        if (componentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER) {
            component.___document = doc;
            renderResult = component.___rerender(component.___input, true);
            trackComponent(componentDef);
            return function mount() {
                renderResult.afterInsert(doc);
            };
        } else {
            trackComponent(componentDef);
        }

        return function mount() {
            initComponent(componentDef, doc);
        };
    }
}

function trackComponent(componentDef) {
    var component = componentDef.___component;
    if (component) {
        componentLookup[component.id] = component;
    }
}

exports.___initClientRendered = initClientRendered;
exports.___initServerRendered = initServerRendered;
<commit_msg>Fix init order for out of order components (#1436)<commit_after>"use strict";
var warp10Finalize = require("warp10/finalize");
var eventDelegation = require("./event-delegation");
var win = window;
var defaultDocument = document;
var createFragmentNode = require("../vdom/morphdom/fragment")
    .___createFragmentNode;
var componentsUtil = require("./util");
var componentLookup = componentsUtil.___componentLookup;
var addComponentRootToKeyedElements =
    componentsUtil.___addComponentRootToKeyedElements;
var ComponentDef = require("./ComponentDef");
var registry = require("./registry");
var domData = require("./dom-data");
var componentsByDOMNode = domData.___componentByDOMNode;
var serverRenderedGlobals = {};
var serverComponentRootNodes = {};
var keyedElementsByComponentId = {};

var FLAG_WILL_RERENDER_IN_BROWSER = 1;

function indexServerComponentBoundaries(node, runtimeId, stack) {
    var componentId;
    var ownerId;
    var ownerComponent;
    var keyedElements;
    var nextSibling;
    var runtimeLength = runtimeId.length;
    stack = stack || [];

    node = node.firstChild;
    while (node) {
        nextSibling = node.nextSibling;
        if (node.nodeType === 8) {
            // Comment node
            var commentValue = node.nodeValue;
            if (commentValue.slice(0, runtimeLength) === runtimeId) {
                var firstChar = commentValue[runtimeLength];

                if (firstChar === "^" || firstChar === "#") {
                    stack.push(node);
                } else if (firstChar === "/") {
                    var endNode = node;
                    var startNode = stack.pop();
                    var rootNode;

                    if (startNode.parentNode === endNode.parentNode) {
                        rootNode = createFragmentNode(
                            startNode.nextSibling,
                            endNode
                        );
                    } else {
                        rootNode = createFragmentNode(
                            endNode.parentNode.firstChild,
                            endNode
                        );
                    }

                    componentId = startNode.nodeValue.substring(
                        runtimeLength + 1
                    );
                    firstChar = startNode.nodeValue[runtimeLength];

                    if (firstChar === "^") {
                        var parts = componentId.split(/ /g);
                        var key = parts[2];
                        ownerId = parts[1];
                        componentId = parts[0];
                        if ((ownerComponent = componentLookup[ownerId])) {
                            keyedElements = ownerComponent.___keyedElements;
                        } else {
                            keyedElements =
                                keyedElementsByComponentId[ownerId] ||
                                (keyedElementsByComponentId[ownerId] = {});
                        }
                        addComponentRootToKeyedElements(
                            keyedElements,
                            key,
                            rootNode,
                            componentId
                        );
                    }

                    serverComponentRootNodes[componentId] = rootNode;

                    startNode.parentNode.removeChild(startNode);
                    endNode.parentNode.removeChild(endNode);
                }
            }
        } else if (node.nodeType === 1) {
            // HTML element node
            var markoKey = node.getAttribute("data-marko-key");
            var markoProps = node.getAttribute("data-marko");
            if (markoKey) {
                var separatorIndex = markoKey.indexOf(" ");
                ownerId = markoKey.substring(separatorIndex + 1);
                markoKey = markoKey.substring(0, separatorIndex);
                if ((ownerComponent = componentLookup[ownerId])) {
                    keyedElements = ownerComponent.___keyedElements;
                } else {
                    keyedElements =
                        keyedElementsByComponentId[ownerId] ||
                        (keyedElementsByComponentId[ownerId] = {});
                }
                keyedElements[markoKey] = node;
            }
            if (markoProps) {
                markoProps = JSON.parse(markoProps);
                Object.keys(markoProps).forEach(function(key) {
                    if (key.slice(0, 2) === "on") {
                        eventDelegation.___addDelegatedEventHandler(
                            key.slice(2)
                        );
                    }
                });
            }
            indexServerComponentBoundaries(node, runtimeId, stack);
        }

        node = nextSibling;
    }
}

function invokeComponentEventHandler(component, targetMethodName, args) {
    var method = component[targetMethodName];
    if (!method) {
        throw Error("Method not found: " + targetMethodName);
    }

    method.apply(component, args);
}

function addEventListenerHelper(el, eventType, isOnce, listener) {
    var eventListener = listener;
    if (isOnce) {
        eventListener = function(event) {
            listener(event);
            el.removeEventListener(eventType, eventListener);
        };
    }

    el.addEventListener(eventType, eventListener, false);

    return function remove() {
        el.removeEventListener(eventType, eventListener);
    };
}

function addDOMEventListeners(
    component,
    el,
    eventType,
    targetMethodName,
    isOnce,
    extraArgs,
    handles
) {
    var removeListener = addEventListenerHelper(el, eventType, isOnce, function(
        event
    ) {
        var args = [event, el];
        if (extraArgs) {
            args = extraArgs.concat(args);
        }

        invokeComponentEventHandler(component, targetMethodName, args);
    });
    handles.push(removeListener);
}

function initComponent(componentDef, doc) {
    var component = componentDef.___component;

    if (!component || !component.___isComponent) {
        return; // legacy
    }

    component.___reset();
    component.___document = doc;

    var isExisting = componentDef.___isExisting;

    if (isExisting) {
        component.___removeDOMEventListeners();
    }

    var domEvents = componentDef.___domEvents;
    if (domEvents) {
        var eventListenerHandles = [];

        domEvents.forEach(function(domEventArgs) {
            // The event mapping is for a direct DOM event (not a custom event and not for bubblign dom events)

            var eventType = domEventArgs[0];
            var targetMethodName = domEventArgs[1];
            var eventEl = component.___keyedElements[domEventArgs[2]];
            var isOnce = domEventArgs[3];
            var extraArgs = domEventArgs[4];

            addDOMEventListeners(
                component,
                eventEl,
                eventType,
                targetMethodName,
                isOnce,
                extraArgs,
                eventListenerHandles
            );
        });

        if (eventListenerHandles.length) {
            component.___domEventListenerHandles = eventListenerHandles;
        }
    }

    if (component.___mounted) {
        component.___emitLifecycleEvent("update");
    } else {
        component.___mounted = true;
        component.___emitLifecycleEvent("mount");
    }
}

/**
 * This method is used to initialized components associated with UI components
 * rendered in the browser. While rendering UI components a "components context"
 * is added to the rendering context to keep up with which components are rendered.
 * When ready, the components can then be initialized by walking the component tree
 * in the components context (nested components are initialized before ancestor components).
 * @param  {Array<marko-components/lib/ComponentDef>} componentDefs An array of ComponentDef instances
 */
function initClientRendered(componentDefs, doc) {
    // Ensure that event handlers to handle delegating events are
    // always attached before initializing any components
    eventDelegation.___init(doc);

    doc = doc || defaultDocument;
    var len = componentDefs.length;
    var componentDef;
    var i;

    for (i = len; i--; ) {
        componentDef = componentDefs[i];
        trackComponent(componentDef);
    }

    for (i = len; i--; ) {
        componentDef = componentDefs[i];
        initComponent(componentDef, doc);
    }
}

/**
 * This method initializes all components that were rendered on the server by iterating over all
 * of the component IDs.
 */
function initServerRendered(renderedComponents, doc) {
    var type = typeof renderedComponents;
    var runtimeId;

    if (type !== "object") {
        var componentsKey =
            "$" +
            (type === "string"
                ? renderedComponents + "_components"
                : "components");
        renderedComponents = win[componentsKey];

        if (renderedComponents && renderedComponents.forEach) {
            renderedComponents.forEach(function(renderedComponent) {
                initServerRendered(renderedComponent, doc);
            });
        }

        win[componentsKey] = {
            concat: initServerRendered
        };

        return;
    }

    doc = doc || defaultDocument;

    renderedComponents = warp10Finalize(renderedComponents);

    runtimeId = renderedComponents.r;
    var componentDefs = renderedComponents.w;
    var typesArray = renderedComponents.t;
    var markoGlobalsKey = "$" + runtimeId + "G";

    // Ensure that event handlers to handle delegating events are
    // always attached before initializing any components
    indexServerComponentBoundaries(doc, runtimeId);
    eventDelegation.___init(doc);

    var globals = win[markoGlobalsKey];
    if (globals) {
        serverRenderedGlobals = warp10Finalize(globals);
        delete win[markoGlobalsKey];
    }

    // hydrate components top down (leaf nodes last)
    // and return an array of functions to mount these components
    var deferredDefs;
    componentDefs
        .map(function(componentDef) {
            componentDef = ComponentDef.___deserialize(
                componentDef,
                typesArray,
                serverRenderedGlobals,
                registry
            );

            var mount = hydrateComponentAndGetMount(componentDef, doc);

            if (!mount) {
                // hydrateComponentAndGetMount will return false if there is not rootNode
                // for the component.  If this is the case, we'll wait until the
                // DOM has fully loaded to attempt to init the component again.
                if (deferredDefs) {
                    deferredDefs.push(componentDef);
                } else {
                    deferredDefs = [componentDef];
                    doc.addEventListener("DOMContentLoaded", function() {
                        indexServerComponentBoundaries(doc, runtimeId);
                        deferredDefs
                            .map(function(componentDef) {
                                return hydrateComponentAndGetMount(
                                    componentDef,
                                    doc
                                );
                            })
                            .reverse()
                            .forEach(tryInvoke);
                    });
                }
            }

            return mount;
        })
        .reverse()
        .forEach(tryInvoke);
}

function hydrateComponentAndGetMount(componentDef, doc) {
    var componentId = componentDef.id;
    var component = componentDef.___component;
    var rootNode = serverComponentRootNodes[componentId];
    var renderResult;

    if (rootNode) {
        delete serverComponentRootNodes[componentId];

        component.___rootNode = rootNode;
        componentsByDOMNode.set(rootNode, component);
        component.___keyedElements =
            keyedElementsByComponentId[componentId] || {};

        delete keyedElementsByComponentId[componentId];

        if (componentDef.___flags & FLAG_WILL_RERENDER_IN_BROWSER) {
            component.___document = doc;
            renderResult = component.___rerender(component.___input, true);
            trackComponent(componentDef);
            return function mount() {
                renderResult.afterInsert(doc);
            };
        } else {
            trackComponent(componentDef);
        }

        return function mount() {
            initComponent(componentDef, doc);
        };
    }
}

function trackComponent(componentDef) {
    var component = componentDef.___component;
    if (component) {
        componentLookup[component.id] = component;
    }
}

function tryInvoke(fn) {
    if (fn) fn();
}

exports.___initClientRendered = initClientRendered;
exports.___initServerRendered = initServerRendered;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import coalesceNonElementChildren from './coalesceNonElementChildren';

const systemButtonOpacity = 0.2;

export default class Button extends Component {
  static propTypes = {
    ...TouchableOpacity.propTypes,
    allowFontScaling: Text.propTypes.allowFontScaling,
    containerStyle: View.propTypes.style,
    disabled: PropTypes.bool,
    style: Text.propTypes.style,
    styleDisabled: Text.propTypes.style,
  };

  render() {
    let touchableProps = {
      activeOpacity: this._computeActiveOpacity(),
    };
    if (!this.props.disabled) {
      touchableProps.onPress = this.props.onPress;
      touchableProps.onPressIn = this.props.onPressIn;
      touchableProps.onPressOut = this.props.onPressOut;
      touchableProps.onLongPress = this.props.onLongPress;
    }

    return (
      <TouchableOpacity
        {...touchableProps}
        testID={this.props.testID}
        style={this.props.containerStyle}
        accessibilityTraits="button"
        accessibilityComponentType="button">
        {this._renderGroupedChildren()}
      </TouchableOpacity>
    );
  }

  _renderGroupedChildren() {
    let { disabled } = this.props;
    let style = [
      styles.text,
      disabled ? styles.disabledText : null,
      this.props.style,
      disabled ? this.props.styleDisabled : null,
    ];

    let children = coalesceNonElementChildren(this.props.children, (children, index) => {
      return (
        <Text key={index} style={style} allowFontScaling={this.props.allowFontScaling}>
          {children}
        </Text>
      );
    });

    switch (children.length) {
      case 0:
        return null;
      case 1:
        return children[0];
      default:
        return <View style={styles.group}>{children}</View>;
    }
  }

  _computeActiveOpacity() {
    if (this.props.disabled) {
      return 1;
    }
    return this.props.activeOpacity != null ?
      this.props.activeOpacity :
      systemButtonOpacity;
  }
};

const styles = StyleSheet.create({
  text: {
    color: '#007aff',
    fontFamily: 'system',
    fontSize: 17,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  disabledText: {
    color: '#dcdcdc',
  },
  group: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});
<commit_msg>Adjust font weight<commit_after>import React, { Component, PropTypes } from 'react';
import {
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import coalesceNonElementChildren from './coalesceNonElementChildren';

const systemButtonOpacity = 0.2;

export default class Button extends Component {
  static propTypes = {
    ...TouchableOpacity.propTypes,
    allowFontScaling: Text.propTypes.allowFontScaling,
    containerStyle: View.propTypes.style,
    disabled: PropTypes.bool,
    style: Text.propTypes.style,
    styleDisabled: Text.propTypes.style,
  };

  render() {
    let touchableProps = {
      activeOpacity: this._computeActiveOpacity(),
    };
    if (!this.props.disabled) {
      touchableProps.onPress = this.props.onPress;
      touchableProps.onPressIn = this.props.onPressIn;
      touchableProps.onPressOut = this.props.onPressOut;
      touchableProps.onLongPress = this.props.onLongPress;
    }

    return (
      <TouchableOpacity
        {...touchableProps}
        testID={this.props.testID}
        style={this.props.containerStyle}
        accessibilityTraits="button"
        accessibilityComponentType="button">
        {this._renderGroupedChildren()}
      </TouchableOpacity>
    );
  }

  _renderGroupedChildren() {
    let { disabled } = this.props;
    let style = [
      styles.text,
      disabled ? styles.disabledText : null,
      this.props.style,
      disabled ? this.props.styleDisabled : null,
    ];

    let children = coalesceNonElementChildren(this.props.children, (children, index) => {
      return (
        <Text key={index} style={style} allowFontScaling={this.props.allowFontScaling}>
          {children}
        </Text>
      );
    });

    switch (children.length) {
      case 0:
        return null;
      case 1:
        return children[0];
      default:
        return <View style={styles.group}>{children}</View>;
    }
  }

  _computeActiveOpacity() {
    if (this.props.disabled) {
      return 1;
    }
    return this.props.activeOpacity != null ?
      this.props.activeOpacity :
      systemButtonOpacity;
  }
};

const styles = StyleSheet.create({
  text: {
    color: '#007aff',
    fontSize: 17,
    fontWeight: '500',
    textAlign: 'center',
  },
  disabledText: {
    color: '#dcdcdc',
  },
  group: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
});
<|endoftext|>


<commit_before>import { Damage } from '../damage';
import { Team } from '../utility/team';
import * as matrices from '../utility/matrices';
import * as arrayUtils from '../utility/arrayUtils';
import { Effect } from '../effect';
import { getDirectionFromDelta } from '../utility/position';

/** Creates the abilities
 * @param {Object} G the game object
 * @return {void}
 */
export default G => {
	G.abilities[3] = [
		// First Ability: Toxic Spores
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			triggerFunc: function() {
				if (this.isUpgraded()) {
					return 'onUnderAttack onAttack';
				}
				return 'onUnderAttack';
			},

			priority: 10,

			// require() :
			require: function(damage) {
				if (!this.testRequirements()) {
					return false;
				}

				// Check that attack is melee from actual creature, not from trap
				if (damage && damage.melee !== undefined) {
					return damage.melee && !damage.isFromTrap;
				}
				// Always return true so that ability is highlighted in UI
				return true;
			},

			// activate() :
			activate: function(damage) {
				let ability = this;
				let creature = this.creature;

				if (!damage || !damage.melee) {
					return;
				}

				// ability may trigger both onAttack and onUnderAttack;
				// the target should be the other creature
				let target = damage.attacker === creature ? damage.target : damage.attacker;

				let optArg = {
					alterations: ability.effects[0],
					creationTurn: G.turn - 1,
					stackable: true
				};

				ability.end();

				// Spore Contamination
				let effect = new Effect(
					ability.title, // Name
					creature, // Caster
					target, // Target
					'', // Trigger
					optArg, // Optional arguments
					G
				);

				target.addEffect(effect, undefined, 'Contaminated');

				G.log(
					'%CreatureName' + target.id + "%'s regrowth is lowered by " + ability.effects[0].regrowth
				);

				ability.setUsed(false); // Infinite triggering
			}
		},

		//	Second Ability: Supper Chomp
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.enemy,

			// require() :
			require: function() {
				if (!this.testRequirements()) {
					return false;
				}

				// At least one target
				if (
					!this.atLeastOneTarget(this.creature.getHexMap(matrices.frontnback2hex), {
						team: this._targetTeam
					})
				) {
					return false;
				}
				return true;
			},

			// query() :
			query: function() {
				let uncle = this.creature;
				let ability = this;

				G.grid.queryCreature({
					fnOnConfirm: function() {
						ability.animation(...arguments);
					},
					team: this._targetTeam,
					id: uncle.id,
					flipped: uncle.flipped,
					hexes: uncle.getHexMap(matrices.frontnback2hex)
				});
			},

			// activate() :
			activate: function(target) {
				let ability = this;
				ability.end();

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage type
					1, // Area
					[], // Effects
					G
				);

				let dmg = target.takeDamage(damage);

				if (dmg.damageObj.status === '') {
					let amount = dmg.damages.total;

					// If upgraded, heal immediately up to the amount of health lost so far;
					// use the remainder as regrowth
					if (this.isUpgraded()) {
						let healthLost = this.creature.stats.health - this.creature.health;
						if (healthLost > 0) {
							let healAmount = Math.min(amount, healthLost);
							amount -= healAmount;
							this.creature.heal(healAmount, false);
						}
					}

					// Regrowth bonus
					if (amount > 0) {
						ability.creature.addEffect(
							new Effect(
								ability.title, // Name
								ability.creature, // Caster
								ability.creature, // Target
								'', // Trigger
								{
									turnLifetime: 1,
									deleteTrigger: 'onStartPhase',
									alterations: {
										regrowth: amount
									}
								}, // Optional arguments
								G
							),
							'%CreatureName' + ability.creature.id + '% gained ' + amount + ' regrowth for now', // Custom log
							'Regrowth++'
						); // Custom hint
					}
				}

				// Remove frogger bonus if its found
				ability.creature.effects.forEach(function(effect) {
					if (effect.name == 'Frogger Bonus') {
						effect.deleteEffect();
					}
				});
			}
		},

		// Third Ability: Frogger Jump
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			require: function() {
				// Must be able to move
				if (!this.creature.stats.moveable) {
					this.message = G.msg.abilities.notmoveable;
					return false;
				}
				return this.testRequirements() && this.creature.stats.moveable;
			},

			fnOnSelect: function(hex) {
				this.creature.tracePosition({
					x: hex.x,
					y: hex.y,
					overlayClass: 'creature moveto selected player' + this.creature.team
				});
			},

			// query() :
			query: function() {
				let ability = this;
				let uncle = this.creature;

				// Don't jump over creatures if we're not upgraded, or we are in a second
				// "low" jump
				let stopOnCreature = !this.isUpgraded() || this._isSecondLowJump();
				let hexes = this._getHexRange(stopOnCreature);

				G.grid.queryHexes({
					fnOnSelect: function() {
						ability.fnOnSelect(...arguments);
					},
					fnOnConfirm: function() {
						if (arguments[0].x == ability.creature.x && arguments[0].y == ability.creature.y) {
							// Prevent null movement
							ability.query();
							return;
						}
						ability.animation(...arguments);
					},
					size: uncle.size,
					flipped: uncle.player.flipped,
					id: uncle.id,
					hexes: hexes,
					hexesDashed: [],
					hideNonTarget: true
				});
			},

			// activate() :
			activate: function(hex) {
				let ability = this;
				ability.end(false, true); // Defered ending

				// If upgraded and we haven't leapt over creatures/obstacles, allow a second
				// jump of the same kind
				if (this.isUpgraded() && !this._isSecondLowJump()) {
					// Check if we've leapt over creatures by finding all "low" jumps (jumps
					// not over creatures), and finding whether this jump was a "low" one
					let lowJumpHexes = this._getHexRange(true);
					let isLowJump = false;
					for (let i = 0; i < lowJumpHexes.length; i++) {
						if (lowJumpHexes[i].x === hex.x && lowJumpHexes[i].y === hex.y) {
							isLowJump = true;
						}
					}
					if (isLowJump) {
						this.setUsed(false);
					}
				}

				// Jump directly to hex
				ability.creature.moveTo(hex, {
					ignoreMovementPoint: true,
					ignorePath: true,
					callback: function() {
						// Shake the screen upon landing to simulate the jump
						G.Phaser.camera.shake(0.02, 100, true, G.Phaser.camera.SHAKE_VERTICAL, true);

						G.onStepIn(ability.creature, ability.creature.hexagons[0]);

						let interval = setInterval(function() {
							if (!G.freezedInput) {
								clearInterval(interval);
								G.UI.selectAbility(-1);
								G.activeCreature.queryMove();
							}
						}, 100);
					}
				});

				// Frogger Leap bonus
				ability.creature.addEffect(
					new Effect(
						'Offense Bonus', // Name
						ability.creature, // Caster
						ability.creature, // Target
						'onStepIn onEndPhase', // Trigger
						{
							effectFn: function(effect) {
								effect.deleteEffect();
							},
							alterations: ability.effects[0]
						}, // Optional arguments
						G
					)
				);
			},

			_getHexRange: function(stopOnCreature) {
				// Get the hex range of this ability
				let uncle = this.creature;
				let forward = G.grid.getHexMap(uncle.x, uncle.y, 0, false, matrices.straitrow);
				forward = arrayUtils.filterCreature(forward, false, stopOnCreature, uncle.id);
				let backward = G.grid.getHexMap(uncle.x, uncle.y, 0, true, matrices.straitrow);
				backward = arrayUtils.filterCreature(backward, false, stopOnCreature, uncle.id);
				// Combine and sort by X, left to right
				let hexes = forward.concat(backward).sort(function(a, b) {
					return a.x - b.x;
				});
				// Filter out any hexes that cannot accomodate the creature's size
				let run = 0;
				for (let i = 0; i < hexes.length; i++) {
					if (i === 0 || hexes[i - 1].x + 1 === hexes[i].x) {
						run++;
					} else {
						if (run < this.creature.size) {
							hexes.splice(i - run, run);
							i -= run;
						}
						run = 1;
					}
				}
				if (run < this.creature.size) {
					hexes.splice(hexes.length - run, run);
				}
				return hexes;
			},

			_isSecondLowJump: function() {
				return this.timesUsedThisTurn === 1;
			}
		},
		// Fourth Ability: Sabre Kick
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.enemy,

			// require() :
			require: function() {
				if (!this.testRequirements()) {
					return false;
				}

				let map = G.grid.getHexMap(
					this.creature.x - 2,
					this.creature.y - 2,
					0,
					false,
					matrices.frontnback2hex
				);
				// At least one target
				if (
					!this.atLeastOneTarget(map, {
						team: this._targetTeam
					})
				) {
					return false;
				}
				return true;
			},

			// query() :
			query: function() {
				let ability = this;
				let uncle = this.creature;

				G.grid.queryCreature({
					fnOnConfirm: function() {
						ability.animation(...arguments);
					},
					team: this._targetTeam,
					id: uncle.id,
					flipped: uncle.flipped,
					hexes: G.grid.getHexMap(uncle.x - 2, uncle.y - 2, 0, false, matrices.frontnback2hex)
				});
			},

			// activate() :
			activate: function(target) {
				let ability = this;
				ability.end();

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage Type
					1, // Area
					[], // Effects
					G
				);
				let result = target.takeDamage(damage);

				// If upgraded, knock back target by 1 hex
				if (this.isUpgraded() && !result.kill) {
					let dx = target.x - this.creature.x;
					let dy = target.y - this.creature.y;
					let dir = getDirectionFromDelta(target.y, dx, dy);
					let hexes = G.grid.getHexLine(target.x, target.y, dir, target.flipped);
					// The hex to knock back into is the second hex since the first is where
					// they are currently
					if (hexes.length >= 2 && hexes[1].isWalkable(target.size, target.id, true)) {
						target.moveTo(hexes[1], {
							callback: function() {
								G.activeCreature.queryMove();
							},
							ignoreMovementPoint: true,
							ignorePath: true,
							overrideSpeed: 500, // Custom speed for knockback
							animation: 'push'
						});
					}
				}

				// Remove Frogger Jump bonus if its found
				ability.creature.effects.forEach(function(effect) {
					if (effect.name == 'Offense Bonus') {
						effect.deleteEffect();
					}
				});
			}
		}
	];
};
<commit_msg>Another reason abilities need to be refactored... fixes #1563<commit_after>import { Damage } from '../damage';
import { Team } from '../utility/team';
import * as matrices from '../utility/matrices';
import * as arrayUtils from '../utility/arrayUtils';
import { Effect } from '../effect';
import { getDirectionFromDelta } from '../utility/position';

/** Creates the abilities
 * @param {Object} G the game object
 * @return {void}
 */
export default G => {
	G.abilities[28] = [
		// First Ability: Toxic Spores
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			triggerFunc: function() {
				if (this.isUpgraded()) {
					return 'onUnderAttack onAttack';
				}
				return 'onUnderAttack';
			},

			priority: 10,

			// require() :
			require: function(damage) {
				if (!this.testRequirements()) {
					return false;
				}

				// Check that attack is melee from actual creature, not from trap
				if (damage && damage.melee !== undefined) {
					return damage.melee && !damage.isFromTrap;
				}
				// Always return true so that ability is highlighted in UI
				return true;
			},

			// activate() :
			activate: function(damage) {
				let ability = this;
				let creature = this.creature;

				if (!damage || !damage.melee) {
					return;
				}

				// ability may trigger both onAttack and onUnderAttack;
				// the target should be the other creature
				let target = damage.attacker === creature ? damage.target : damage.attacker;

				let optArg = {
					alterations: ability.effects[0],
					creationTurn: G.turn - 1,
					stackable: true,
				};

				ability.end();

				// Spore Contamination
				let effect = new Effect(
					ability.title, // Name
					creature, // Caster
					target, // Target
					'', // Trigger
					optArg, // Optional arguments
					G,
				);

				target.addEffect(effect, undefined, 'Contaminated');

				G.log(
					'%CreatureName' + target.id + "%'s regrowth is lowered by " + ability.effects[0].regrowth,
				);

				ability.setUsed(false); // Infinite triggering
			},
		},

		//	Second Ability: Supper Chomp
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.enemy,

			// require() :
			require: function() {
				if (!this.testRequirements()) {
					return false;
				}

				// At least one target
				if (
					!this.atLeastOneTarget(this.creature.getHexMap(matrices.frontnback2hex), {
						team: this._targetTeam,
					})
				) {
					return false;
				}
				return true;
			},

			// query() :
			query: function() {
				let uncle = this.creature;
				let ability = this;

				G.grid.queryCreature({
					fnOnConfirm: function() {
						ability.animation(...arguments);
					},
					team: this._targetTeam,
					id: uncle.id,
					flipped: uncle.flipped,
					hexes: uncle.getHexMap(matrices.frontnback2hex),
				});
			},

			// activate() :
			activate: function(target) {
				let ability = this;
				ability.end();

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage type
					1, // Area
					[], // Effects
					G,
				);

				let dmg = target.takeDamage(damage);

				if (dmg.damageObj.status === '') {
					let amount = dmg.damages.total;

					// If upgraded, heal immediately up to the amount of health lost so far;
					// use the remainder as regrowth
					if (this.isUpgraded()) {
						let healthLost = this.creature.stats.health - this.creature.health;
						if (healthLost > 0) {
							let healAmount = Math.min(amount, healthLost);
							amount -= healAmount;
							this.creature.heal(healAmount, false);
						}
					}

					// Regrowth bonus
					if (amount > 0) {
						ability.creature.addEffect(
							new Effect(
								ability.title, // Name
								ability.creature, // Caster
								ability.creature, // Target
								'', // Trigger
								{
									turnLifetime: 1,
									deleteTrigger: 'onStartPhase',
									alterations: {
										regrowth: amount,
									},
								}, // Optional arguments
								G,
							),
							'%CreatureName' + ability.creature.id + '% gained ' + amount + ' regrowth for now', // Custom log
							'Regrowth++',
						); // Custom hint
					}
				}

				// Remove frogger bonus if its found
				ability.creature.effects.forEach(function(effect) {
					if (effect.name == 'Frogger Bonus') {
						effect.deleteEffect();
					}
				});
			},
		},

		// Third Ability: Frogger Jump
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			require: function() {
				// Must be able to move
				if (!this.creature.stats.moveable) {
					this.message = G.msg.abilities.notmoveable;
					return false;
				}
				return this.testRequirements() && this.creature.stats.moveable;
			},

			fnOnSelect: function(hex) {
				this.creature.tracePosition({
					x: hex.x,
					y: hex.y,
					overlayClass: 'creature moveto selected player' + this.creature.team,
				});
			},

			// query() :
			query: function() {
				let ability = this;
				let uncle = this.creature;

				// Don't jump over creatures if we're not upgraded, or we are in a second
				// "low" jump
				let stopOnCreature = !this.isUpgraded() || this._isSecondLowJump();
				let hexes = this._getHexRange(stopOnCreature);

				G.grid.queryHexes({
					fnOnSelect: function() {
						ability.fnOnSelect(...arguments);
					},
					fnOnConfirm: function() {
						if (arguments[0].x == ability.creature.x && arguments[0].y == ability.creature.y) {
							// Prevent null movement
							ability.query();
							return;
						}
						ability.animation(...arguments);
					},
					size: uncle.size,
					flipped: uncle.player.flipped,
					id: uncle.id,
					hexes: hexes,
					hexesDashed: [],
					hideNonTarget: true,
				});
			},

			// activate() :
			activate: function(hex) {
				let ability = this;
				ability.end(false, true); // Defered ending

				// If upgraded and we haven't leapt over creatures/obstacles, allow a second
				// jump of the same kind
				if (this.isUpgraded() && !this._isSecondLowJump()) {
					// Check if we've leapt over creatures by finding all "low" jumps (jumps
					// not over creatures), and finding whether this jump was a "low" one
					let lowJumpHexes = this._getHexRange(true);
					let isLowJump = false;
					for (let i = 0; i < lowJumpHexes.length; i++) {
						if (lowJumpHexes[i].x === hex.x && lowJumpHexes[i].y === hex.y) {
							isLowJump = true;
						}
					}
					if (isLowJump) {
						this.setUsed(false);
					}
				}

				// Jump directly to hex
				ability.creature.moveTo(hex, {
					ignoreMovementPoint: true,
					ignorePath: true,
					callback: function() {
						// Shake the screen upon landing to simulate the jump
						G.Phaser.camera.shake(0.02, 100, true, G.Phaser.camera.SHAKE_VERTICAL, true);

						G.onStepIn(ability.creature, ability.creature.hexagons[0]);

						let interval = setInterval(function() {
							if (!G.freezedInput) {
								clearInterval(interval);
								G.UI.selectAbility(-1);
								G.activeCreature.queryMove();
							}
						}, 100);
					},
				});

				// Frogger Leap bonus
				ability.creature.addEffect(
					new Effect(
						'Offense Bonus', // Name
						ability.creature, // Caster
						ability.creature, // Target
						'onStepIn onEndPhase', // Trigger
						{
							effectFn: function(effect) {
								effect.deleteEffect();
							},
							alterations: ability.effects[0],
						}, // Optional arguments
						G,
					),
				);
			},

			_getHexRange: function(stopOnCreature) {
				// Get the hex range of this ability
				let uncle = this.creature;
				let forward = G.grid.getHexMap(uncle.x, uncle.y, 0, false, matrices.straitrow);
				forward = arrayUtils.filterCreature(forward, false, stopOnCreature, uncle.id);
				let backward = G.grid.getHexMap(uncle.x, uncle.y, 0, true, matrices.straitrow);
				backward = arrayUtils.filterCreature(backward, false, stopOnCreature, uncle.id);
				// Combine and sort by X, left to right
				let hexes = forward.concat(backward).sort(function(a, b) {
					return a.x - b.x;
				});
				// Filter out any hexes that cannot accomodate the creature's size
				let run = 0;
				for (let i = 0; i < hexes.length; i++) {
					if (i === 0 || hexes[i - 1].x + 1 === hexes[i].x) {
						run++;
					} else {
						if (run < this.creature.size) {
							hexes.splice(i - run, run);
							i -= run;
						}
						run = 1;
					}
				}
				if (run < this.creature.size) {
					hexes.splice(hexes.length - run, run);
				}
				return hexes;
			},

			_isSecondLowJump: function() {
				return this.timesUsedThisTurn === 1;
			},
		},
		// Fourth Ability: Sabre Kick
		{
			// Type : Can be "onQuery", "onStartPhase", "onDamage"
			trigger: 'onQuery',

			_targetTeam: Team.enemy,

			// require() :
			require: function() {
				if (!this.testRequirements()) {
					return false;
				}

				let map = G.grid.getHexMap(
					this.creature.x - 2,
					this.creature.y - 2,
					0,
					false,
					matrices.frontnback2hex,
				);
				// At least one target
				if (
					!this.atLeastOneTarget(map, {
						team: this._targetTeam,
					})
				) {
					return false;
				}
				return true;
			},

			// query() :
			query: function() {
				let ability = this;
				let uncle = this.creature;

				G.grid.queryCreature({
					fnOnConfirm: function() {
						ability.animation(...arguments);
					},
					team: this._targetTeam,
					id: uncle.id,
					flipped: uncle.flipped,
					hexes: G.grid.getHexMap(uncle.x - 2, uncle.y - 2, 0, false, matrices.frontnback2hex),
				});
			},

			// activate() :
			activate: function(target) {
				let ability = this;
				ability.end();

				let damage = new Damage(
					ability.creature, // Attacker
					ability.damages, // Damage Type
					1, // Area
					[], // Effects
					G,
				);
				let result = target.takeDamage(damage);

				// If upgraded, knock back target by 1 hex
				if (this.isUpgraded() && !result.kill) {
					let dx = target.x - this.creature.x;
					let dy = target.y - this.creature.y;
					let dir = getDirectionFromDelta(target.y, dx, dy);
					let hexes = G.grid.getHexLine(target.x, target.y, dir, target.flipped);
					// The hex to knock back into is the second hex since the first is where
					// they are currently
					if (hexes.length >= 2 && hexes[1].isWalkable(target.size, target.id, true)) {
						target.moveTo(hexes[1], {
							callback: function() {
								G.activeCreature.queryMove();
							},
							ignoreMovementPoint: true,
							ignorePath: true,
							overrideSpeed: 500, // Custom speed for knockback
							animation: 'push',
						});
					}
				}

				// Remove Frogger Jump bonus if its found
				ability.creature.effects.forEach(function(effect) {
					if (effect.name == 'Offense Bonus') {
						effect.deleteEffect();
					}
				});
			},
		},
	];
};
<|endoftext|>


<commit_before><commit_msg>componentizd!<commit_after><|endoftext|>