<commit_before>var root = document.getElementById('root');
var mark_lines = [];
var current_line = null;
var need_paint = true;
var new_line_count = 0;
var background_image = new Image();
var font_size = 20;
var font_style = "18px mono";
var mark_handle_half_size = 6;
var cursor_pos = new Point(0, 0);
var edit_file_name;

function invalidate() {
    need_paint = true;
}

function Point(x, y) {
    this.x = x;
    this.y = y;
    this.distanceTo = function(p) {
        return Math.sqrt((p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y));
    }
    this.radius = 5;
    this.draw = function(ctx, vertical, dash) {
        var mark_x = this.x,
            mark_y = this.y;
        var dst_x = this.x,
            dst_y = this.y;
        var offset_x = mark_handle_half_size,
            offset_y = mark_handle_half_size;
        if (dash) {
            offset_x = root.width;
            offset_y = root.height;
        };
        if (vertical) {
            mark_x = this.x - offset_x;
            dst_x = this.x + offset_x;
        } else {
            mark_y = this.y - offset_y;
            dst_y = this.y + offset_y;
        }
        if (dash) {
            ctx.save();
            ctx.setLineDash([4, 4]);
        }
        ctx.beginPath();
        ctx.moveTo(mark_x, mark_y);
        ctx.lineTo(dst_x, dst_y);
        ctx.stroke();
        if (dash) {
            ctx.restore();
        }
    }
}

function LineMark(start, end) {
    this.start = start;
    this.end = end;
    this._cache_length = null;
    this.length = function() {
        if (this == current_line) {
            this._cache_length = this.start.distanceTo(this.end) + 1;
        }
        return this._cache_length;
    }
    this.isVertical = function() {
        return this.end.x == this.start.x;
    }
    this.draw = function(ctx) {
        ctx.beginPath();
        ctx.moveTo(this.start.x, this.start.y);
        ctx.lineTo(this.end.x, this.end.y);
        ctx.stroke();
        this.start.draw(ctx, this.isVertical(), this == current_line);
        this.end.draw(ctx, this.isVertical(), this == current_line);
        var text = "" + Math.floor(this.length());
        var center_x = (this.start.x + this.end.x) / 2;
        var center_y = (this.start.y + this.end.y) / 2;
        ctx.save();
        ctx.strokeStyle = "white";
        ctx.lineWidth = 3;
        ctx.strokeText(text, center_x, center_y);
        ctx.restore();
        ctx.fillText(text, center_x, center_y);

    }
}

function render() {
    if (!need_paint) {
        return;
    }
    var ctx = root.getContext('2d');
    ctx.fillStyle = "white";
    ctx.font = font_style;
    ctx.lineWidth = 0.5;
    ctx.fillRect(0, 0, root.width, root.height);
    if (background_image.src) {
        ctx.drawImage(background_image, 0, 0);
    };
    ctx.strokeStyle = "red";
    ctx.fillStyle = "red";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.save();
    ctx.translate(0.5, 0.5);
    for (var i in mark_lines) {
        mark_lines[i].draw(ctx);
    }
    if (current_line != null) {
        current_line.draw(ctx);
    } else {
        // draw cursor
        ctx.beginPath();
        ctx.moveTo(cursor_pos.x - mark_handle_half_size, cursor_pos.y);
        ctx.lineTo(cursor_pos.x + mark_handle_half_size, cursor_pos.y);
        ctx.moveTo(cursor_pos.x, cursor_pos.y - mark_handle_half_size);
        ctx.lineTo(cursor_pos.x, cursor_pos.y + mark_handle_half_size);
        ctx.stroke();
    }
    if (!background_image.src) {
        var tf = ctx.font;
        ctx.font = '120px serif';
        ctx.fillText("Drop image here!", root.width / 2, root.height / 2);
        ctx.font = tf;
    };
    ctx.restore();
    need_paint = false;
}

root.onmousedown = function(event) {
    if (event.which != 1) {
        return;
    }
    current_line = new LineMark(new Point(Math.floor(event.layerX), Math.floor(event.layerY)),
        new Point(Math.floor(event.layerX), Math.floor(event.layerY)));
    new_line_count = 0;
    invalidate();
}

function adjustLineDirection(line) {
    var dst_x, dst_y;
    var abs_x = Math.abs(line.end.x - line.start.x);
    var abs_y = Math.abs(line.end.y - line.start.y);
    if (abs_x < abs_y) {
        // vertical mode
        dst_x = line.start.x;
        dst_y = line.end.y;
    } else {
        // horizonal mode
        dst_x = line.end.x;
        dst_y = line.start.y;
    }
    line.end.x = dst_x;
    line.end.y = dst_y;
}

root.onmouseup = function(event) {
    if (event.which != 1) {
        return;
    }
    if (current_line != null && new_line_count == 1) {
        var pix_x = Math.floor(event.layerX);
        var pix_y = Math.floor(event.layerY);
        current_line.end.x = Math.floor(pix_x);
        current_line.end.y = Math.floor(pix_y);
        adjustLineDirection(current_line);
        if (current_line.length() > 0) {
            mark_lines[mark_lines.length] = current_line;
        }
    }
    current_line = null;
    cursor_pos.x = pix_x;
    cursor_pos.y = pix_y;
    invalidate();
}

root.onmousemove = function(event) {
    if (event.which != 1 && current_line != null) {
        return;
    }
    var pix_x = Math.floor(event.layerX);
    var pix_y = Math.floor(event.layerY);
    if (current_line != null) {
        current_line.end.x = pix_x;
        current_line.end.y = pix_y;
        adjustLineDirection(current_line);
        new_line_count = 1;
    } else if (cursor_pos.x != pix_x || cursor_pos.y != pix_y) {
        cursor_pos.x = pix_x;
        cursor_pos.y = pix_y;
    }
    invalidate();
}

function handleFiles(files) {
    for (var i = 0; i < files.length; i++) {
        var f = files[i];
        if (!f.type.match("image.*")) {
            continue;
        }
        var reader = new FileReader();
        reader.onload = (function(theFile) {
            return function(e) {
                background_image.src = e.target.result;
                document.title = "Editing " + theFile.name;
                invalidate();
                edit_file_name = theFile.name;
            }
        })(f);
        reader.readAsDataURL(f);
        break;
    };
}

function handleSelectFile(event) {
    handleFiles(event.target.files);
}

function handleDragOver(event) {
    event.stopPropagation();
    event.preventDefault();
    event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}

function handleDrop(event) {
    event.stopPropagation();
    event.preventDefault();
    var files = event.dataTransfer.files;
    handleFiles(files);
}

function save() {
    if (mark_lines.length > 0) {
        var blob = new Blob([JSON.stringify(mark_lines)], {
            type: "application/json;charset=utf-8"
        });
        saveAs(edit_file_name + ".json");
    };
}

function SaveDatFileBro(localstorage) {
    localstorage.root.getFile("info.txt", {
        create: true
    }, function(DatFile) {
        DatFile.createWriter(function(DatContent) {
            var blob = new Blob(["Lorem Ipsum"], {
                type: "text/plain"
            });
            DatContent.write(blob);
        });
    });
}

var select_file = document.getElementById('select-file');
select_file.addEventListener("change", handleSelectFile, false);
root.addEventListener("drop", handleDrop, false);
root.addEventListener("dragover", handleDragOver, false);
navigator.webkitPersistentStorage.requestQuota(1024 * 1024, function() {
    window.webkitRequestFileSystem(window.PERSISTENT, 1024 * 1024, SaveDatFileBro);
})
setInterval(render, 15);<commit_msg>separate mouse handler<commit_after>var MARK_MODE = {
    'LINE': 0,
    'COLOR': 1,
    'RECT': 2
}

var root = document.getElementById('root');
var mark_lines = [];
var current_line = null;
var need_paint = true;
var new_line_count = 0;
var background_image = new Image();
var font_size = 20;
var font_style = "18px mono";
var mark_handle_half_size = 6;
var cursor_pos = new Point(0, 0);
var edit_file_name;
var mark_mode = MARK_MODE.LINE;
var mouse_handers = [];

function invalidate() {
    need_paint = true;
}

function Point(x, y) {
    this.x = x;
    this.y = y;
    this.distanceTo = function(p) {
        return Math.sqrt((p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y));
    }
    this.radius = 5;
    this.draw = function(ctx, vertical, dash) {
        var mark_x = this.x,
            mark_y = this.y;
        var dst_x = this.x,
            dst_y = this.y;
        var offset_x = mark_handle_half_size,
            offset_y = mark_handle_half_size;
        if (dash) {
            offset_x = root.width;
            offset_y = root.height;
        };
        if (vertical) {
            mark_x = this.x - offset_x;
            dst_x = this.x + offset_x;
        } else {
            mark_y = this.y - offset_y;
            dst_y = this.y + offset_y;
        }
        if (dash) {
            ctx.save();
            ctx.setLineDash([4, 4]);
        }
        ctx.beginPath();
        ctx.moveTo(mark_x, mark_y);
        ctx.lineTo(dst_x, dst_y);
        ctx.stroke();
        if (dash) {
            ctx.restore();
        }
    }
}

function LineMark(start, end) {
    this.start = start;
    this.end = end;
    this._cache_length = null;
    this.length = function() {
        if (this == current_line) {
            this._cache_length = this.start.distanceTo(this.end) + 1;
        }
        return this._cache_length;
    }
    this.isVertical = function() {
        return this.end.x == this.start.x;
    }
    this.draw = function(ctx) {
        ctx.beginPath();
        ctx.moveTo(this.start.x, this.start.y);
        ctx.lineTo(this.end.x, this.end.y);
        ctx.stroke();
        this.start.draw(ctx, this.isVertical(), this == current_line);
        this.end.draw(ctx, this.isVertical(), this == current_line);
        var text = "" + Math.floor(this.length());
        var center_x = (this.start.x + this.end.x) / 2;
        var center_y = (this.start.y + this.end.y) / 2;
        ctx.save();
        ctx.strokeStyle = "white";
        ctx.lineWidth = 3;
        ctx.strokeText(text, center_x, center_y);
        ctx.restore();
        ctx.fillText(text, center_x, center_y);

    }
}

function render() {
    if (!need_paint) {
        return;
    }
    var ctx = root.getContext('2d');
    ctx.fillStyle = "white";
    ctx.font = font_style;
    ctx.lineWidth = 0.5;
    ctx.fillRect(0, 0, root.width, root.height);
    if (background_image.src) {
        ctx.drawImage(background_image, 0, 0);
    };
    ctx.strokeStyle = "red";
    ctx.fillStyle = "red";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.save();
    ctx.translate(0.5, 0.5);
    for (var i in mark_lines) {
        mark_lines[i].draw(ctx);
    }
    if (current_line != null) {
        current_line.draw(ctx);
    } else {
        // draw cursor
        ctx.beginPath();
        ctx.moveTo(cursor_pos.x - mark_handle_half_size, cursor_pos.y);
        ctx.lineTo(cursor_pos.x + mark_handle_half_size, cursor_pos.y);
        ctx.moveTo(cursor_pos.x, cursor_pos.y - mark_handle_half_size);
        ctx.lineTo(cursor_pos.x, cursor_pos.y + mark_handle_half_size);
        ctx.stroke();
    }
    if (!background_image.src) {
        var tf = ctx.font;
        ctx.font = '120px serif';
        ctx.fillText("Drop image here!", root.width / 2, root.height / 2);
        ctx.font = tf;
    };
    ctx.restore();
    need_paint = false;
}

function adjustLineDirection(line) {
    var dst_x, dst_y;
    var abs_x = Math.abs(line.end.x - line.start.x);
    var abs_y = Math.abs(line.end.y - line.start.y);
    if (abs_x < abs_y) {
        // vertical mode
        dst_x = line.start.x;
        dst_y = line.end.y;
    } else {
        // horizonal mode
        dst_x = line.end.x;
        dst_y = line.start.y;
    }
    line.end.x = dst_x;
    line.end.y = dst_y;
}

function LineModeHandler() {

    this.onmousedown = function(event) {
        if (event.which != 1) {
            return;
        }
        current_line = new LineMark(new Point(Math.floor(event.layerX), Math.floor(event.layerY)),
            new Point(Math.floor(event.layerX), Math.floor(event.layerY)));
        new_line_count = 0;
        invalidate();
    }

    this.onmouseup = function(event) {
        if (event.which != 1) {
            return;
        }
        if (current_line != null && new_line_count == 1) {
            var pix_x = Math.floor(event.layerX);
            var pix_y = Math.floor(event.layerY);
            current_line.end.x = Math.floor(pix_x);
            current_line.end.y = Math.floor(pix_y);
            adjustLineDirection(current_line);
            if (current_line.length() > 0) {
                mark_lines[mark_lines.length] = current_line;
            }
        }
        current_line = null;
        cursor_pos.x = pix_x;
        cursor_pos.y = pix_y;
        invalidate();
    }

    this.onmousemove = function(event) {
        if (event.which != 1 && current_line != null) {
            return;
        }
        var pix_x = Math.floor(event.layerX);
        var pix_y = Math.floor(event.layerY);
        if (current_line != null) {
            current_line.end.x = pix_x;
            current_line.end.y = pix_y;
            adjustLineDirection(current_line);
            new_line_count = 1;
        } else if (cursor_pos.x != pix_x || cursor_pos.y != pix_y) {
            cursor_pos.x = pix_x;
            cursor_pos.y = pix_y;
        }
        invalidate();
    }

}

function handleFiles(files) {
    for (var i = 0; i < files.length; i++) {
        var f = files[i];
        if (!f.type.match("image.*")) {
            continue;
        }
        var reader = new FileReader();
        reader.onload = (function(theFile) {
            return function(e) {
                background_image.src = e.target.result;
                document.title = "Editing " + theFile.name;
                invalidate();
                edit_file_name = theFile.name;
                var savedMark = window.localStorage.getItem(edit_file_name);
                if (savedMark) {
                    var saved_lines = JSON.parse(savedMark);
                    mark_lines = [];
                    for (var i = 0; i < saved_lines.length; i++) {
                        var line = saved_lines[i];
                        mark_lines[i] = new LineMark(new Point(line.start.x, line.start.y),
                            new Point(line.end.x, line.end.y));
                        mark_lines[i]._cache_length = line._cache_length;
                    };
                    invalidate();
                }
            }
        })(f);
        reader.readAsDataURL(f);
        break;
    };
}

function handleSelectFile(event) {
    handleFiles(event.target.files);
}

function handleDragOver(event) {
    event.stopPropagation();
    event.preventDefault();
    event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}

function handleDrop(event) {
    event.stopPropagation();
    event.preventDefault();
    var files = event.dataTransfer.files;
    handleFiles(files);
}

function save() {
    if (mark_lines.length > 0) {
        window.localStorage.setItem(edit_file_name, JSON.stringify(mark_lines));
    };
}

function mouse_handler_proxy () {
    this.onmousedown = function (event) {
        return mouse_handers[mark_mode].onmousedown(event);
    }
    this.onmouseup = function (event) {
        return mouse_handers[mark_mode].onmouseup(event);
    }
    this.onmousemove = function (event) {
        return mouse_handers[mark_mode].onmousemove(event);
    }
}

mouse_handers[MARK_MODE.LINE] = new LineModeHandler();
var mouse_proxy = new mouse_handler_proxy();
var select_file = document.getElementById('select-file');
select_file.addEventListener("change", handleSelectFile, false);
root.addEventListener("drop", handleDrop, false);
root.addEventListener("dragover", handleDragOver, false);
root.addEventListener("mousedown", mouse_proxy.onmousedown, false);
root.addEventListener("mousemove", mouse_proxy.onmousemove, false);
root.addEventListener("mouseup", mouse_proxy.onmouseup, false);
setInterval(render, 15);

document.addEventListener("keydown", function(event) {
    if (event.keycode == 49) {

    } else if (event.keycode == 50) {

    }
})<|endoftext|>


<commit_before>function Markov(options) {
    if (!(this instanceof Markov)) return new Markov(options);
    options = options || {};
    this.stateSize = options.stateSize || 1;
    this.transitions = {};
    this.start = this.createState();
    this.transitions[this.start] = [];
    if (options.key) this.key = options.key;
}

Markov.prototype.key = function(token) {
    return token;
};

Markov.prototype.createState = function() {
    var n = this.stateSize;
    var state = new Array(n);
    for (var i=0; i<n; i++) state[i] = null;
    var m = Math.min(n, arguments.length);
    var j;
    for (i=n-m, j=0; i<n; i++, j++) state[i] = arguments[j];
    return state;
};

Markov.prototype.save = function() {
    return {
        stateSize: this.stateSize,
        transitions: this.transitions,
    };
};

Markov.load = function(data, options) {
    return (new Markov(options)).load(data);
};

Markov.prototype.load = function(data) {
    this.stateSize = data.stateSize;
    this.transitions = data.transitions;
    return this;
};

Markov.prototype.addTransition = function addTransition(state, token) {
    return this.addWeightedTransition(state, 1, token);
};

Markov.prototype.addWeightedTransition = function addWeightedTransition(state, w, token) {
    if (this.transitions.hasOwnProperty(state)) {
        return this.inSort(this.transitions[state], w, token);
    } else {
        this.transitions[state] = [[w, token]];
        return this.transitions[state];
    }
};

Markov.prototype.addWeightedTransitions = function addWeightedTransitions(state, newWTokens) {
    var self = this;
    newWTokens.forEach(function(wToken) {
        self.addWeightedTransition(state, wToken[0], wToken[1]);
    });
};

Markov.prototype.inSort = function inSort(wTokens, w, token) {
    var lo = 0, hi = wTokens.length-1;
    while (lo <= hi) {
        var q = Math.floor(lo / 2 + hi / 2);
        if   (this.tokenRel(token, wTokens[q][1])) hi = q-1;
        else                                       lo = q+1;
    }
    if (wTokens[lo] && wTokens[lo][1] === token) {
        wTokens[lo][0] += w;
    } else {
        wTokens.splice(lo, 0, [w, token]);
    }
    return wTokens;
};

Markov.prototype.tokenRel = function tokenRel(a, b) {
    return ('' + a) <= ('' + b);
};

Markov.prototype.addTokens = function addTokens(tokens) {
    var self = this;
    step(this.createState(), 0);
    function step(last, i) {
        var token = tokens[i];
        if (token === undefined) {
            if (i === 0) return;
            token = null;
        }
        self.addTransition(last, token);
        last.shift();
        last.push(self.key(token));
        if (i >= tokens.length) return;
        step(last, i+1);
    }
};

Markov.prototype.merge = function merge(other) {
    if (this.stateSize !== other.stateSize) {
        throw new Error('cannot merge markovs with differing state size');
    }
    return this.mergeTransitions(other.transitions);
};

Markov.prototype.mergeTransitions = function merge(transitions) {
    var self = this;
    Object.keys(transitions).forEach(function(state) {
        self.addWeightedTransitions(state, transitions[state]);
    });
    return self;
};

Markov.prototype.choose = function choose(state, rand) {
    rand = rand || Math.random;
    var wTokens = this.transitions[state];
    var r = null;
    if (wTokens) {
        r = wTokens[0][1];
        var bestK = Math.pow(rand(), 1/wTokens[0][0]);
        for (var i=1, n=wTokens.length; i<n; i++) {
            var k = Math.pow(rand(), 1/wTokens[i][0]);
            if (k > bestK) {
                bestK = k;
                r = wTokens[i][1];
            }
        }
    }
    return r;
};

Markov.prototype.chain = function chain(maxLength, state, rand) {
    maxLength = maxLength || Infinity;
    rand = rand || Math.random;
    var result = [];
    var self = this;
    return step(state || this.createState());
    function step(state) {
        var token = self.choose(state, rand);
        if (token !== null) {
            result.push(token);
            if (result.length < maxLength) {
                state.shift();
                state.push(self.key(token));
                return step(state);
            }
        }
        return result;
    }
};

Markov.prototype.generatePhrase = function(n, minLength) {
    var phrase = '';
    while (phrase.length < minLength) {
        phrase = this.chain(n).join(' ');
    }
    return phrase;
};

Markov.makeMap = function(data) {
    var map = {};
    if (data.transitions) {
        var markov = Markov.load(data);
        map[markov.stateSize] = markov;
    } else {
        Object.keys(data).forEach(function(key) {
            map[key] = Markov.load(data[key]);
        });
    }
    map.get = function getMarkov(k) {
        if (this[k]) return this[k];
        var best = null;
        Object.keys(this).forEach(function(key) {
            var markov = this[key];
            if (!best ||
                (markov.stateSize <= k && markov.stateSize > best.stateSize)
            ) best = markov;
        });
        if (best) this[k] = best;
        return best;
    };
    map.generatePhrase = function(n, minLength) {
        var markov = this.get(n);
        if (!markov) throw new Error('no markov available for ' + n + '-phrases');
        var phrase = markov.generatePhrase(n, minLength);
        return phrase;
    };
    return map;
};

module.exports = Markov;
<commit_msg>Move tokenRel declaration<commit_after>function Markov(options) {
    if (!(this instanceof Markov)) return new Markov(options);
    options = options || {};
    this.stateSize = options.stateSize || 1;
    this.transitions = {};
    this.start = this.createState();
    this.transitions[this.start] = [];
    if (options.key) this.key = options.key;
}

Markov.prototype.tokenRel = function tokenRel(a, b) {
    return ('' + a) <= ('' + b);
};

Markov.prototype.key = function(token) {
    return token;
};

Markov.prototype.createState = function() {
    var n = this.stateSize;
    var state = new Array(n);
    for (var i=0; i<n; i++) state[i] = null;
    var m = Math.min(n, arguments.length);
    var j;
    for (i=n-m, j=0; i<n; i++, j++) state[i] = arguments[j];
    return state;
};

Markov.prototype.save = function() {
    return {
        stateSize: this.stateSize,
        transitions: this.transitions,
    };
};

Markov.load = function(data, options) {
    return (new Markov(options)).load(data);
};

Markov.prototype.load = function(data) {
    this.stateSize = data.stateSize;
    this.transitions = data.transitions;
    return this;
};

Markov.prototype.addTransition = function addTransition(state, token) {
    return this.addWeightedTransition(state, 1, token);
};

Markov.prototype.addWeightedTransition = function addWeightedTransition(state, w, token) {
    if (this.transitions.hasOwnProperty(state)) {
        return this.inSort(this.transitions[state], w, token);
    } else {
        this.transitions[state] = [[w, token]];
        return this.transitions[state];
    }
};

Markov.prototype.addWeightedTransitions = function addWeightedTransitions(state, newWTokens) {
    var self = this;
    newWTokens.forEach(function(wToken) {
        self.addWeightedTransition(state, wToken[0], wToken[1]);
    });
};

Markov.prototype.inSort = function inSort(wTokens, w, token) {
    var lo = 0, hi = wTokens.length-1;
    while (lo <= hi) {
        var q = Math.floor(lo / 2 + hi / 2);
        if   (this.tokenRel(token, wTokens[q][1])) hi = q-1;
        else                                       lo = q+1;
    }
    if (wTokens[lo] && wTokens[lo][1] === token) {
        wTokens[lo][0] += w;
    } else {
        wTokens.splice(lo, 0, [w, token]);
    }
    return wTokens;
};

Markov.prototype.addTokens = function addTokens(tokens) {
    var self = this;
    step(this.createState(), 0);
    function step(last, i) {
        var token = tokens[i];
        if (token === undefined) {
            if (i === 0) return;
            token = null;
        }
        self.addTransition(last, token);
        last.shift();
        last.push(self.key(token));
        if (i >= tokens.length) return;
        step(last, i+1);
    }
};

Markov.prototype.merge = function merge(other) {
    if (this.stateSize !== other.stateSize) {
        throw new Error('cannot merge markovs with differing state size');
    }
    return this.mergeTransitions(other.transitions);
};

Markov.prototype.mergeTransitions = function merge(transitions) {
    var self = this;
    Object.keys(transitions).forEach(function(state) {
        self.addWeightedTransitions(state, transitions[state]);
    });
    return self;
};

Markov.prototype.choose = function choose(state, rand) {
    rand = rand || Math.random;
    var wTokens = this.transitions[state];
    var r = null;
    if (wTokens) {
        r = wTokens[0][1];
        var bestK = Math.pow(rand(), 1/wTokens[0][0]);
        for (var i=1, n=wTokens.length; i<n; i++) {
            var k = Math.pow(rand(), 1/wTokens[i][0]);
            if (k > bestK) {
                bestK = k;
                r = wTokens[i][1];
            }
        }
    }
    return r;
};

Markov.prototype.chain = function chain(maxLength, state, rand) {
    maxLength = maxLength || Infinity;
    rand = rand || Math.random;
    var result = [];
    var self = this;
    return step(state || this.createState());
    function step(state) {
        var token = self.choose(state, rand);
        if (token !== null) {
            result.push(token);
            if (result.length < maxLength) {
                state.shift();
                state.push(self.key(token));
                return step(state);
            }
        }
        return result;
    }
};

Markov.prototype.generatePhrase = function(n, minLength) {
    var phrase = '';
    while (phrase.length < minLength) {
        phrase = this.chain(n).join(' ');
    }
    return phrase;
};

Markov.makeMap = function(data) {
    var map = {};
    if (data.transitions) {
        var markov = Markov.load(data);
        map[markov.stateSize] = markov;
    } else {
        Object.keys(data).forEach(function(key) {
            map[key] = Markov.load(data[key]);
        });
    }
    map.get = function getMarkov(k) {
        if (this[k]) return this[k];
        var best = null;
        Object.keys(this).forEach(function(key) {
            var markov = this[key];
            if (!best ||
                (markov.stateSize <= k && markov.stateSize > best.stateSize)
            ) best = markov;
        });
        if (best) this[k] = best;
        return best;
    };
    map.generatePhrase = function(n, minLength) {
        var markov = this.get(n);
        if (!markov) throw new Error('no markov available for ' + n + '-phrases');
        var phrase = markov.generatePhrase(n, minLength);
        return phrase;
    };
    return map;
};

module.exports = Markov;
<|endoftext|>


<commit_before>module.exports = MantaCouch;

var follow = require('follow');
var url = require('url');
var crypto = require('crypto');
var assert = require('assert');

var parse = require('parse-json-response');

var cuttlefish = require('cuttlefish');

var path = require('path');
var fs = require('fs');
var PassThrough = require('stream').PassThrough;
var EE = require('events').EventEmitter;
var util = require('util');

if (!PassThrough)
  throw new Error('This module requires Node 0.10 or above');

util.inherits(MantaCouch, EE);

function MantaCouch(opts) {
  if (!(this instanceof MantaCouch))
    return new MantaCouch(opts);

  EE.call(this);

  if (!opts || typeof opts !== 'object')
    throw new TypeError('opts object required');

  this.opts = opts;

  if (!opts.client || opts.client.constructor.name !== 'MantaClient')
    throw new TypeError('opts.client of type MantaClient is required');
  this.client = opts.client;

  if (opts.seqFile && typeof opts.seqFile !== 'string')
    throw new TypeError('opts.seqFile must be of type string');
  this.seqFile = opts.seqFile || null;
  if (this.seqFile)
    this.seqFile = path.resolve(this.seqFile);

  if (!opts.path || typeof opts.path !== 'string')
    throw new TypeError('opts.path is required');
  this.path = opts.path
    .replace(/\/+$/, '')
    .replace(/^~~/, '/' + this.client.user);

  if (!opts.db || !url.parse(opts.db).protocol)
    throw new TypeError('opts.db url is required');
  this.db = opts.db.replace(/\/+$/, '');

  this.http = url.parse(this.db).protocol === 'https:' ?
    require('https') : require('http');

  if (opts.inactivity_ms && typeof opts.inactivity_ms !== 'number')
    throw new TypeError('opts.inactivity_ms must be of type number');
  this.inactivity_ms = opts.inactivity_ms;

  if (opts.seq && typeof opts.seq !== 'number')
    throw new TypeError('opts.seq must be of type number');
  this.seq = opts.seq || 0;

  if (opts.concurrency && typeof opts.concurrency !== 'number')
    throw new TypeError('opts.concurrency must be of type number');
  this.concurrency = opts.concurrency;

  this.delete = !!opts.delete;

  this.following = false;
  this.savingSeq = false;
  this.start();
}

MantaCouch.prototype.saveSeq = function(file) {
  file = file || this.seqFile;
  if (!file && !this.seqFile)
    return
  if (!file)
    throw new Error('invalid sequence file: ' + file);
  if (!this.savingSeq)
    fs.writeFile(file, this.seq + '\n', 'ascii', this.afterSave.bind(this));
  this.savingSeq = true;
}

MantaCouch.prototype.start = function() {
  if (this.following)
    throw new Error('Cannot read sequence after follow starts');
  if (!this.seqFile) {
    this.seq = 0;
    this.onReadSeq();
  } else
    fs.readFile(this.seqFile, 'ascii', this.onReadSeq.bind(this));
}

MantaCouch.prototype.onReadSeq = function(er, data) {
  if (er && er.code === 'ENOENT')
    data = 0;
  else if (er)
    return this.emit('error', er);

  if (data === undefined)
    data = null
  if (!+data && +data !== 0)
    return this.emit('error', new Error('invalid data in seqFile'));

  data = +data;
  this.seq = +data;
  this.follow = follow({
    db: this.db,
    since: this.seq,
    inactivity_ms: this.inactivity_ms
  }, this.onChange.bind(this));
  this.following = true;
}

MantaCouch.prototype.afterSave = function(er) {
  if (er)
    this.emit('error', er);
  this.savingSeq = false;
}

MantaCouch.prototype.onChange = function(er, change) {
  if (er)
    return this.emit('error', er);

  this.seq = change.seq;

  // Please don't delete the entire store in Manta, kthx
  if (!change.id)
    return;

  if (change.deleted)
    this.rm(change);
  else
    this.put(change);
}

MantaCouch.prototype.rm = function(change) {
  if (this.delete) {
    this.emit('rm', change);
    this.pause();
    this.client.rmr(this.path + '/' + change.id, this.onRm.bind(this, change));
  }
}

MantaCouch.prototype.onRm = function(change, er) {
  if (!er || er.statusCode === 404)
    this.resume();
  else
    this.emit('error', er);
}

MantaCouch.prototype.stop =
MantaCouch.prototype.close =
MantaCouch.prototype.destroy = function() {
  if (this.client)
    this.client.close();
  if (this.follow)
    this.follow.stop();
}

MantaCouch.prototype.put = function(change) {
  if (change.id !== encodeURIComponent(change.id)) {
    console.error('WARNING: Skipping %j\nWARNING: See %s', change.id,
                 'https://github.com/joyent/node-manta/issues/157')
    return
  }

  this.pause();
  var query = 'att_encoding_info=true&revs=true';
  var u = url.parse(this.db + '/' + change.id + '?' + query);
  this.http.get(u, parse(function(er, doc, res) {
    if (er)
      return this.emit('error', er);
    change.doc = doc;
    this._put(change);
  }.bind(this)))
}

MantaCouch.prototype._put = function(change) {
  this.emit('put', change);
  var doc = change.doc;

  var files = Object.keys(doc._attachments || {}).reduce(function (s, k) {
    var att = doc._attachments[k];
    // Gzip-encoded attachments are lying liars playing lyres
    if (att.encoding === 'gzip') {
      delete att.digest;
      delete att.length;
    }
    s['_attachments/' + k] = doc._attachments[k];
    return s;
  }, {});

  var json = new Buffer(JSON.stringify(doc) + '\n', 'utf8');
  files['doc.json'] = {
    type: 'application/json',
    name: 'doc.json',
    length: json.length
  };

  cuttlefish({
    path: this.path + '/' + doc._id,
    client: this.client,
    files: files,
    getMd5: this.getMd5.bind(this, change, json),
    request: this.getFile.bind(this, change, json),
    delete: this.delete,
    concurrency: this.concurrency
  })
    .on('send', this.emit.bind(this, 'send', change))
    .on('delete', this.emit.bind(this, 'delete', change))
    .on('error', this.emit.bind(this, 'error'))
    .on('complete', this.onCuttleComplete.bind(this, change));
}

MantaCouch.prototype.getMd5 = function(change, json, file, cb) {
  if (file.name === 'doc.json')
    var md5 = crypto.createHash('md5').update(json).digest('base64');
  cb(null, md5);
}

MantaCouch.prototype.getFile = function(change, json, file, cb) {
  if (file.name === 'doc.json')
    this.streamDoc(json, file, cb)
  else
    this.getAttachment(change, file, cb)
}

MantaCouch.prototype.streamDoc = function(json, file, cb) {
  var s = new PassThrough();
  s.end(json);
  cb(null, s);
}

MantaCouch.prototype.getAttachment = function(change, file, cb) {
  var a = path.dirname(file.name).replace(/^_attachments/, change.id);
  var f = encodeURIComponent(path.basename(file.name))
  a += '/' + f
  this.emit('attachment', change, file);
  var u = this.db + '/' + a;
  this.http.get(u, function(res) {
    cb(null, res);
  }).on('error', cb);
}

MantaCouch.prototype.onCuttleComplete = function(change, results) {
  this.emit('complete', change, results);
  this.resume();
};

MantaCouch.prototype.pause = function() {
  this.follow.pause();
};

MantaCouch.prototype.resume = function() {
  this.saveSeq();
  this.follow.resume();
}
<commit_msg>Save seq file atomically<commit_after>module.exports = MantaCouch;

var follow = require('follow');
var url = require('url');
var crypto = require('crypto');
var assert = require('assert');

var parse = require('parse-json-response');

var cuttlefish = require('cuttlefish');

var path = require('path');
var fs = require('fs');
var PassThrough = require('stream').PassThrough;
var EE = require('events').EventEmitter;
var util = require('util');

if (!PassThrough)
  throw new Error('This module requires Node 0.10 or above');

util.inherits(MantaCouch, EE);

function MantaCouch(opts) {
  if (!(this instanceof MantaCouch))
    return new MantaCouch(opts);

  EE.call(this);

  if (!opts || typeof opts !== 'object')
    throw new TypeError('opts object required');

  this.opts = opts;

  if (!opts.client || opts.client.constructor.name !== 'MantaClient')
    throw new TypeError('opts.client of type MantaClient is required');
  this.client = opts.client;

  if (opts.seqFile && typeof opts.seqFile !== 'string')
    throw new TypeError('opts.seqFile must be of type string');
  this.seqFile = opts.seqFile || null;
  if (this.seqFile)
    this.seqFile = path.resolve(this.seqFile);

  if (!opts.path || typeof opts.path !== 'string')
    throw new TypeError('opts.path is required');
  this.path = opts.path
    .replace(/\/+$/, '')
    .replace(/^~~/, '/' + this.client.user);

  if (!opts.db || !url.parse(opts.db).protocol)
    throw new TypeError('opts.db url is required');
  this.db = opts.db.replace(/\/+$/, '');

  this.http = url.parse(this.db).protocol === 'https:' ?
    require('https') : require('http');

  if (opts.inactivity_ms && typeof opts.inactivity_ms !== 'number')
    throw new TypeError('opts.inactivity_ms must be of type number');
  this.inactivity_ms = opts.inactivity_ms;

  if (opts.seq && typeof opts.seq !== 'number')
    throw new TypeError('opts.seq must be of type number');
  this.seq = opts.seq || 0;

  if (opts.concurrency && typeof opts.concurrency !== 'number')
    throw new TypeError('opts.concurrency must be of type number');
  this.concurrency = opts.concurrency;

  this.delete = !!opts.delete;

  this.following = false;
  this.savingSeq = false;
  this.start();
}

MantaCouch.prototype.saveSeq = function(file) {
  file = file || this.seqFile;
  if (!file && !this.seqFile)
    return
  if (!file)
    throw new Error('invalid sequence file: ' + file);
  if (this.savingSeq)
    return

  this.savingSeq = true;
  var t = file + '.TMP'
  var data = this.seq + '\n'
  fs.writeFile(t, data, 'ascii', function(er) {
    if (er)
      return this.afterSave(er)
    fs.rename(t, file, this.afterSave.bind(this))
  }.bind(this))
}

MantaCouch.prototype.start = function() {
  if (this.following)
    throw new Error('Cannot read sequence after follow starts');
  if (!this.seqFile) {
    this.seq = 0;
    this.onReadSeq();
  } else
    fs.readFile(this.seqFile, 'ascii', this.onReadSeq.bind(this));
}

MantaCouch.prototype.onReadSeq = function(er, data) {
  if (er && er.code === 'ENOENT')
    data = 0;
  else if (er)
    return this.emit('error', er);

  if (data === undefined)
    data = null
  if (!+data && +data !== 0)
    return this.emit('error', new Error('invalid data in seqFile'));

  data = +data;
  this.seq = +data;
  this.follow = follow({
    db: this.db,
    since: this.seq,
    inactivity_ms: this.inactivity_ms
  }, this.onChange.bind(this));
  this.following = true;
}

MantaCouch.prototype.afterSave = function(er) {
  if (er)
    this.emit('error', er);
  this.savingSeq = false;
}

MantaCouch.prototype.onChange = function(er, change) {
  if (er)
    return this.emit('error', er);

  this.seq = change.seq;

  // Please don't delete the entire store in Manta, kthx
  if (!change.id)
    return;

  if (change.deleted)
    this.rm(change);
  else
    this.put(change);
}

MantaCouch.prototype.rm = function(change) {
  if (this.delete) {
    this.emit('rm', change);
    this.pause();
    this.client.rmr(this.path + '/' + change.id, this.onRm.bind(this, change));
  }
}

MantaCouch.prototype.onRm = function(change, er) {
  if (!er || er.statusCode === 404)
    this.resume();
  else
    this.emit('error', er);
}

MantaCouch.prototype.stop =
MantaCouch.prototype.close =
MantaCouch.prototype.destroy = function() {
  if (this.client)
    this.client.close();
  if (this.follow)
    this.follow.stop();
}

MantaCouch.prototype.put = function(change) {
  if (change.id !== encodeURIComponent(change.id)) {
    console.error('WARNING: Skipping %j\nWARNING: See %s', change.id,
                 'https://github.com/joyent/node-manta/issues/157')
    return
  }

  this.pause();
  var query = 'att_encoding_info=true&revs=true';
  var u = url.parse(this.db + '/' + change.id + '?' + query);
  this.http.get(u, parse(function(er, doc, res) {
    if (er)
      return this.emit('error', er);
    change.doc = doc;
    this._put(change);
  }.bind(this)))
}

MantaCouch.prototype._put = function(change) {
  this.emit('put', change);
  var doc = change.doc;

  var files = Object.keys(doc._attachments || {}).reduce(function (s, k) {
    var att = doc._attachments[k];
    // Gzip-encoded attachments are lying liars playing lyres
    if (att.encoding === 'gzip') {
      delete att.digest;
      delete att.length;
    }
    s['_attachments/' + k] = doc._attachments[k];
    return s;
  }, {});

  var json = new Buffer(JSON.stringify(doc) + '\n', 'utf8');
  files['doc.json'] = {
    type: 'application/json',
    name: 'doc.json',
    length: json.length
  };

  cuttlefish({
    path: this.path + '/' + doc._id,
    client: this.client,
    files: files,
    getMd5: this.getMd5.bind(this, change, json),
    request: this.getFile.bind(this, change, json),
    delete: this.delete,
    concurrency: this.concurrency
  })
    .on('send', this.emit.bind(this, 'send', change))
    .on('delete', this.emit.bind(this, 'delete', change))
    .on('error', this.emit.bind(this, 'error'))
    .on('complete', this.onCuttleComplete.bind(this, change));
}

MantaCouch.prototype.getMd5 = function(change, json, file, cb) {
  if (file.name === 'doc.json')
    var md5 = crypto.createHash('md5').update(json).digest('base64');
  cb(null, md5);
}

MantaCouch.prototype.getFile = function(change, json, file, cb) {
  if (file.name === 'doc.json')
    this.streamDoc(json, file, cb)
  else
    this.getAttachment(change, file, cb)
}

MantaCouch.prototype.streamDoc = function(json, file, cb) {
  var s = new PassThrough();
  s.end(json);
  cb(null, s);
}

MantaCouch.prototype.getAttachment = function(change, file, cb) {
  var a = path.dirname(file.name).replace(/^_attachments/, change.id);
  var f = encodeURIComponent(path.basename(file.name))
  a += '/' + f
  this.emit('attachment', change, file);
  var u = this.db + '/' + a;
  this.http.get(u, function(res) {
    cb(null, res);
  }).on('error', cb);
}

MantaCouch.prototype.onCuttleComplete = function(change, results) {
  this.emit('complete', change, results);
  this.resume();
};

MantaCouch.prototype.pause = function() {
  this.follow.pause();
};

MantaCouch.prototype.resume = function() {
  this.saveSeq();
  this.follow.resume();
}
<|endoftext|>


<commit_before>'use strict';

let images = [
  "avatar/avatar_0.jpg",
  "avatar/avatar_1.jpg",
  "avatar/avatar_2.jpg",
  "avatar/avatar_3.jpg",
  "avatar/avatar_4.jpg",
  "avatar/avatar_5.jpg",
  "avatar/avatar_6.jpg",
  "avatar/avatar_7.jpg",
  "avatar/avatar_0.jpg",
  "avatar/avatar_1.jpg",
  "avatar/avatar_2.jpg",
  "avatar/avatar_3.jpg",
  "avatar/avatar_4.jpg",
  "avatar/avatar_5.jpg",
  "avatar/avatar_6.jpg",
  "avatar/avatar_7.jpg"
];

let card_values = [];
let card_id = [];
let cards_flip = 0;

// Shuffle the cards
Array.prototype.memory_card_shuffle = function () {
  let i = this.length, rand, temp;
  while (--i > 0) {
    rand = Math.floor(Math.random() * (i + 1));
    temp = this[rand];
    this[rand] = this[i];
    this[i] = temp;
  }
};

// Create a new memory board
function memoryFlipCard(card, value){

  if (card.innerHTML == "" && card_values.length < 2) {
    card.style.background = "url(" + value + ") no-repeat";
    card.style.backgroundSize = "80px 80px";

    if (card_values.length == 0) {
      card_values.push(value);
      card_id.push(card.id);
    } else if (card_values.length == 1) {
      card_values.push(value);
      card_id.push(card.id);
    }

    if(card_values.length == 2) {
      if (card_values[0] == card_values[1]) {
        cards_flip += 2;

        // clear both arrays
        card_values = [];
        card_id = [];

        // check if the board is clear
        if (cards_flip === images.length) {
          document.getElementById('message').innerHTML = "Congratulations, you have finished the board in!
 If you like to play again, just click on the button below.";
        }
      }

    }
  }
}
<commit_msg>added a function to flip the two cards back if not matching<commit_after>'use strict';

let images = [
  "avatar/avatar_0.jpg",
  "avatar/avatar_1.jpg",
  "avatar/avatar_2.jpg",
  "avatar/avatar_3.jpg",
  "avatar/avatar_4.jpg",
  "avatar/avatar_5.jpg",
  "avatar/avatar_6.jpg",
  "avatar/avatar_7.jpg",
  "avatar/avatar_0.jpg",
  "avatar/avatar_1.jpg",
  "avatar/avatar_2.jpg",
  "avatar/avatar_3.jpg",
  "avatar/avatar_4.jpg",
  "avatar/avatar_5.jpg",
  "avatar/avatar_6.jpg",
  "avatar/avatar_7.jpg"
];

let card_values = [];
let card_id = [];
let cards_flip = 0;

// Shuffle the cards
Array.prototype.memory_card_shuffle = function () {
  let i = this.length, rand, temp;
  while (--i > 0) {
    rand = Math.floor(Math.random() * (i + 1));
    temp = this[rand];
    this[rand] = this[i];
    this[i] = temp;
  }
};

// Create a new memory board
function memoryFlipCard(card, value){

  if (card.innerHTML == "" && card_values.length < 2) {
    card.style.background = "url(" + value + ") no-repeat";
    card.style.backgroundSize = "80px 80px";

    if (card_values.length == 0) {
      card_values.push(value);
      card_id.push(card.id);
    } else if (card_values.length == 1) {
      card_values.push(value);
      card_id.push(card.id);
    }

    if(card_values.length == 2) {
      if (card_values[0] == card_values[1]) {
        cards_flip += 2;

        // clear both arrays
        card_values = [];
        card_id = [];

        // check if the board is clear
        if (cards_flip === images.length) {
          document.getElementById('message').innerHTML = "Congratulations, you have finished the board in!
 If you like to play again, just click on the button below.";
        }
      } else {
        // Flip the two cards back over
        function flipTwoBack() {
          let card_1 = document.getElementById(card_id[0]);
          let card_2 = document.getElementById(card_id[1]);

          card_1.style.background = 'url("avatar/william.jpg") no-repeat';
          card_1.style.backgroundSize = "80px 80px";
          card_1.innerHTML = "";
          card_2.style.background = 'url("avatar/william.jpg") no-repeat';
          card_2.style.backgroundSize = "80px 80px";
          card_2.innerHTML = "";

          // clear both arrays
          card_values = [];
          card_id = [];
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>module.exports.create = createMemoryStore;

function createMemoryStore(previousStore) {
  var store = {};

  function setter(key, options, value) {
    if (value === undefined || options.maxAge === 0) {
      delete store[key];
      return;
    }

    var expires = (options.maxAge < 0) ? 0 : (new Date().getTime() + options.maxAge * 1000);
    store[key] = {expires: expires, value: value};
    return store[key];
  }

  function getter(key, options, done) {
    var ret = store[key];
    if (!ret || (ret.expires && ret.expires < new Date().getTime())) {
      delete store[key];
      return done('Not Found');
    }
    return done(null, ret.value, ret);
  }

  function chainedGetter(key, options, done) {
    getter(key, options, function (err, value, entry) {
      if (!err || !previousStore) return done(err, value, entry);
      return previousStore.get(key, options, onReadThrough);
    });

    function onReadThrough(err, value, entry) {
      if (err) return done(err, value, entry);
      var now = new Date().getTime();
      if (entry.expires < now) return done(err, value, entry);

      var newOptions = Object.create(options);
      newOptions.maxAge = Math.floor((entry.expires - now) / 1000);
      return done(err, value, setter(key, newOptions, value) || entry);
    }
  }

  return {
    set: function (key, options, value) {
      if (previousStore) previousStore.set(key, options, value);
      return setter(key, options, value);
    },
    get: function (key, options, done) {
      if (options.simulatedAsyncDelay) return setTimeout(function () {
        chainedGetter(key, options, done);
      }, options.simulatedAsyncDelay);
      return chainedGetter(key, options, done);
    },
    clear: function () {
      store = {};
    }
  };
}

<commit_msg>cleanup<commit_after>module.exports.create = createMemoryStore;

function createMemoryStore(previousStore) {
  var store = {};

  function setter(key, options, value) {
    if (value === undefined || options.maxAge === 0) {
      delete store[key];
      return;
    }

    var expires = (options.maxAge < 0) ? 0 : (new Date().getTime() + options.maxAge * 1000);
    store[key] = {expires: expires, value: value};
    return store[key];
  }

  function getter(key, options, done) {
    var ret = store[key];
    if (!ret || (ret.expires && ret.expires < new Date().getTime())) {
      delete store[key];
      return done('Not Found');
    }
    return done(null, ret.value, ret);
  }

  function chainedGetter(key, options, done) {
    getter(key, options, function (err, value, entry) {
      if (!err || !previousStore) return done(err, value, entry);
      return previousStore.get(key, options, onReadThrough);
    });

    function onReadThrough(err, value, entry) {
      if (err) return done(err, value, entry);
      var now = new Date().getTime();
      if (entry.expires < now) return done(err, value, entry);

      var newOptions = Object.create(options);
      newOptions.maxAge = Math.floor((entry.expires - now) / 1000);
      return done(err, value, setter(key, newOptions, value));
    }
  }

  return {
    set: function (key, options, value) {
      if (previousStore) previousStore.set(key, options, value);
      return setter(key, options, value);
    },
    get: function (key, options, done) {
      if (options.simulatedAsyncDelay) return setTimeout(function () {
        chainedGetter(key, options, done);
      }, options.simulatedAsyncDelay);
      return chainedGetter(key, options, done);
    },
    clear: function () {
      store = {};
    }
  };
}

<|endoftext|>


<commit_before>
;(function($) {

   $.fn.collection = function(options) {

      var defaults = {
         container: 'body',
         enable_up: true,
         up: '[ ▲ ]',
         before_up: function(collection, element) { return true; },
         after_up: function(collection, element) { return true; },
         enable_down: true,
         down: '[ ▼ ]',
         before_down: function(collection, element) { return true; },
         after_down: function(collection, element) { return true; },
         enable_add: true,
         add: '[ + ]',
         before_add: function(collection, element) { return true; },
         after_add: function(collection, element) { return true; },
         enable_remove: true,
         remove: '[ - ]',
         before_remove: function(collection, element) { return true; },
         after_remove: function(collection, element) { return true; },
         min: 0,
         max: 100
      };

      var randomNumber = function() {
         var rand = '' + Math.random() * 1000 * new Date().getTime();
         return rand.replace('.', '').split('').sort(function () {
            return 0.5 - Math.random();
         }).join('');
      };

      var getOrCreateId = function(obj) {
         if (!obj.attr('id')) {
            var generated_id;
            do {
               generated_id = 'i' + randomNumber();
            } while ($('#' + generated_id).length > 0);
            obj.attr('id', generated_id);
         }
         return obj.attr('id');
      };

      var getFieldValue = function (selector) {
         try {
            var jqElem = $(selector);
         } catch (e) {
            return null;
         }
         if (jqElem.length === 0) {
            return null;
         } else if (jqElem.is('input[type="checkbox"]')) {
            return (jqElem.prop('checked') === true ? true : false);
         } else if (jqElem.is('input[type="radio"]') && jqElem.attr('name') !== undefined) {
            return $('input[name="' + jqElem.attr('name') + '"]:checked').val();
         } else if (jqElem.prop('value') !== undefined) {
            return jqElem.val();
         } else {
            return jqElem.html();
         }
      };

      var putFieldValue = function (selector, value) {
         try {
            var jqElem = $(selector);
         } catch (e) {
            return;
         }
         if (jqElem.length === 0) {
            return null;
         } else if (jqElem.is('input[type="checkbox"]') && value) {
            jqElem.attr('checked', true);
         } else if (jqElem.prop('value') !== undefined) {
            jqElem.val(value);
         } else {
            jqElem.html(value);
         }
      };

      var dumpCollectionActions = function(collection, settings) {

         var init = collection.find('.collection-tmp').length === 0;
         var elements = collection.find('> div').not('.collection-tmp');

         if (settings.enable_add) {
            if (init) {
                collection.append('

');
                if (settings.add) {
                    collection.append(
                            $(settings.add)
                                .addClass('collection-action collection-main-add')
                                .data('collection', collection.attr('id'))
                    );
                }
            }
            var mainAdd = collection.find('.collection-main-add').css('display', 'initial');
            var adds = collection.find('.collection-add');
            if (adds.length > 0) {
                mainAdd.css('display', 'none');
                collection.find('> div').not('.collection-tmp').each(function() {
                    var element = $(this);
                    element.find('.collection-add').data('element', getOrCreateId(element));
                });
            }
            if (init) {
                adds.addClass('collection-action').data('collection', collection.attr('id'));
            }
            if (elements.length >= settings.max) {
                collection.find('.collection-add, .collection-main-add').css('display', 'none');
            } else {
                collection.find('.collection-add').css('display', 'initial');
            }
         }

         elements.each(function() {
            var element = $(this);

            if (element.find('> .collection-actions').length === 0) {
                var actions = $('

');
                element.append(actions);
            }

            var buttons = [
               {'enabled': 'enable_up', 'class': 'collection-up', 'html': settings.up, 'condition': elements.index(element) !== 0},
               {'enabled': 'enable_remove', 'class': 'collection-remove', 'html': settings.remove, 'condition': elements.length > settings.min},
               {'enabled': 'enable_down', 'class': 'collection-down', 'html': settings.down, 'condition': elements.index(element) !== elements.length - 1}
            ];

            $.each(buttons, function(index, button) {
               if (settings[button.enabled]) {
                   var action = element.find('.' + button.class);
                   if (action.length === 0 && button.html) {
                       action = $(button.html)
                           .appendTo(actions)
                           .addClass(button.class);
                   }
                   if (button.condition) {
                      action.css('display', 'initial');
                   } else {
                      action.css('display', 'none');
                   }
                   action
                     .addClass('collection-action')
                     .data('collection', collection.attr('id'))
                     .data('element', getOrCreateId(element));
               }
            });
         });

      };

      var swapFields = function(collection, elements, oldIndex, newIndex) {
         var oldField= elements.eq(oldIndex);
         var newField = elements.eq(newIndex);
         $.each(collection.data('collection-skeletons'), function(index, name) {
            var oldName = name.replace(/__name__/g, oldIndex);
            var newName = name.replace(/__name__/g, newIndex);
            var swap = getFieldValue(oldField.find("[name='" + oldName + "']"));
            putFieldValue(oldField.find("[name='" + oldName + "']"), getFieldValue(newField.find("[name='" + newName + "']")));
            putFieldValue(newField.find("[name='" + newName + "']"), swap);
         });
      };

      var settings = $.extend(true, {}, defaults, options);

      if ($(settings.container).length === 0) {
         console.log("jquery.collection.js: a container should exist to handle events.");
         return false;
      }

      var elems = $(this);
      elems.each(function() {

         var elem = $(this);
         if (elem.data('collection') !== undefined) {
            var collection = $('#' + elem.data('collection'));
            if (collection.length === 0) {
               console.log("jquery.collection.js: the given collection field does not exist.");
               return true;
            }
         } else {
             collection = elem;
         }

         if (collection.data('prototype') === null) {
            console.log("jquery.collection.js: the given collection field has no prototype, check that your field has the prototype option set to true.");
            return true;
         }

         collection.data('collection-settings', settings);

         var skeletons = [];
         $(collection.data('prototype')).find('[name]').each(function() {
            var that = $(this);
            skeletons.push(that.attr('name'));
         });
         collection.data('collection-skeletons', skeletons);

         dumpCollectionActions(collection, settings);

         var container = $(settings.container);

         container
            .undelegate('.collection-action', 'click')
            .delegate('.collection-action', 'click', function(e) {
               var that = $(this);
               var collection = $('#' + that.data('collection'));
               var settings = collection.data('collection-settings');
               var elements = collection.find('> div').not('.collection-tmp');

               if ((that.is('.collection-add') || that.is('.collection-main-add')) && settings.enable_add &&
                       elements.length < settings.max && settings.before_add(collection, element)) {
                  var prototype = collection.data('prototype');
                  for (var index = 0; (index < settings.max); index++) {
                     var code = $(prototype.replace(/__name__/g, index));
                     var tmp = collection.find('> .collection-tmp');
                     var id = tmp.html(code).find(':input').first().attr('id');
                     tmp.empty();
                     if (container.find('#' + id).length === 0) {
                        if (that.data('element') === undefined) {
                           tmp.before(code);
                        } else {
                           $('#' + that.data('element')).after(code);
                        }
                        var action = code.find('.collection-add');
                        if (action.length > 0) {
                           action.addClass('collection-action').data('collection', collection.attr('id'));
                        }
                        if (!settings.after_add(collection, element)) {
                            code.remove();
                        }
                        break;
                     }
                  }
               }

               var element = $('#' + that.data('element'));
               var index = elements.index(element);

               if (that.is('.collection-remove') && settings.enable_remove &&
                       elements.length > settings.min && settings.before_remove(collection, element)) {
                    var backup = element.clone({withDataAndEvents: true});
                    element.remove();
                    if (!settings.after_remove(collection, backup)) {
                       var elements = collection.find('> div').not('.collection-tmp');
                       if (index === elements.length) {
                           elements.eq(index - 1).after(backup);
                       } else {
                           elements.eq(index).before(backup);
                       }
                    }
               }

               if (that.is('.collection-up') && settings.enable_up) {
                  if (index !== 0 && settings.before_up(collection, element)) {
                     swapFields(collection, elements, index, index - 1);
                     if (!settings.after_up(collection, element)) {
                         swapFields(collection, elements, index - 1, index);
                     }
                  }
               }

               if (that.is('.collection-down') && settings.enable_down) {
                  if (index !== (elements.length - 1) && settings.before_down(collection, element)) {
                     swapFields(collection, elements, index, index + 1);
                     if (!settings.after_down(collection, elements)) {
                         swapFields(collection, elements, index + 1, index);
                     }
                  }
               }

               dumpCollectionActions(collection, settings);
               e.preventDefault();
            })
         ;

      });

      return true;
   };

})(jQuery);
<commit_msg>Update jquery.collection.js<commit_after>/*
* jquery.collection.js v0.0.3
*
* Copyright (c) 2015 alain tiemblo <alain at fuz dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

;(function($) {

   $.fn.collection = function(options) {

      var defaults = {
         container: 'body',
         enable_up: true,
         up: '[ ▲ ]',
         before_up: function(collection, element) { return true; },
         after_up: function(collection, element) { return true; },
         enable_down: true,
         down: '[ ▼ ]',
         before_down: function(collection, element) { return true; },
         after_down: function(collection, element) { return true; },
         enable_add: true,
         add: '[ + ]',
         before_add: function(collection, element) { return true; },
         after_add: function(collection, element) { return true; },
         enable_remove: true,
         remove: '[ - ]',
         before_remove: function(collection, element) { return true; },
         after_remove: function(collection, element) { return true; },
         min: 0,
         max: 100
      };

      var randomNumber = function() {
         var rand = '' + Math.random() * 1000 * new Date().getTime();
         return rand.replace('.', '').split('').sort(function () {
            return 0.5 - Math.random();
         }).join('');
      };

      var getOrCreateId = function(obj) {
         if (!obj.attr('id')) {
            var generated_id;
            do {
               generated_id = 'i' + randomNumber();
            } while ($('#' + generated_id).length > 0);
            obj.attr('id', generated_id);
         }
         return obj.attr('id');
      };

      var getFieldValue = function (selector) {
         try {
            var jqElem = $(selector);
         } catch (e) {
            return null;
         }
         if (jqElem.length === 0) {
            return null;
         } else if (jqElem.is('input[type="checkbox"]')) {
            return (jqElem.prop('checked') === true ? true : false);
         } else if (jqElem.is('input[type="radio"]') && jqElem.attr('name') !== undefined) {
            return $('input[name="' + jqElem.attr('name') + '"]:checked').val();
         } else if (jqElem.prop('value') !== undefined) {
            return jqElem.val();
         } else {
            return jqElem.html();
         }
      };

      var putFieldValue = function (selector, value) {
         try {
            var jqElem = $(selector);
         } catch (e) {
            return;
         }
         if (jqElem.length === 0) {
            return null;
         } else if (jqElem.is('input[type="checkbox"]') && value) {
            jqElem.attr('checked', true);
         } else if (jqElem.prop('value') !== undefined) {
            jqElem.val(value);
         } else {
            jqElem.html(value);
         }
      };

      var dumpCollectionActions = function(collection, settings) {

         var init = collection.find('.collection-tmp').length === 0;
         var elements = collection.find('> div').not('.collection-tmp');

         if (settings.enable_add) {
            if (init) {
                collection.append('

');
                if (settings.add) {
                    collection.append(
                            $(settings.add)
                                .addClass('collection-action collection-main-add')
                                .data('collection', collection.attr('id'))
                    );
                }
            }
            var mainAdd = collection.find('.collection-main-add').css('display', 'initial');
            var adds = collection.find('.collection-add');
            if (adds.length > 0) {
                mainAdd.css('display', 'none');
                collection.find('> div').not('.collection-tmp').each(function() {
                    var element = $(this);
                    element.find('.collection-add').data('element', getOrCreateId(element));
                });
            }
            if (init) {
                adds.addClass('collection-action').data('collection', collection.attr('id'));
            }
            if (elements.length >= settings.max) {
                collection.find('.collection-add, .collection-main-add').css('display', 'none');
            } else {
                collection.find('.collection-add').css('display', 'initial');
            }
         }

         elements.each(function() {
            var element = $(this);

            if (element.find('> .collection-actions').length === 0) {
                var actions = $('

');
                element.append(actions);
            }

            var buttons = [
               {'enabled': 'enable_up', 'class': 'collection-up', 'html': settings.up, 'condition': elements.index(element) !== 0},
               {'enabled': 'enable_remove', 'class': 'collection-remove', 'html': settings.remove, 'condition': elements.length > settings.min},
               {'enabled': 'enable_down', 'class': 'collection-down', 'html': settings.down, 'condition': elements.index(element) !== elements.length - 1}
            ];

            $.each(buttons, function(index, button) {
               if (settings[button.enabled]) {
                   var action = element.find('.' + button.class);
                   if (action.length === 0 && button.html) {
                       action = $(button.html)
                           .appendTo(actions)
                           .addClass(button.class);
                   }
                   if (button.condition) {
                      action.css('display', 'initial');
                   } else {
                      action.css('display', 'none');
                   }
                   action
                     .addClass('collection-action')
                     .data('collection', collection.attr('id'))
                     .data('element', getOrCreateId(element));
               }
            });
         });

      };

      var swapFields = function(collection, elements, oldIndex, newIndex) {
         var oldField= elements.eq(oldIndex);
         var newField = elements.eq(newIndex);
         $.each(collection.data('collection-skeletons'), function(index, name) {
            var oldName = name.replace(/__name__/g, oldIndex);
            var newName = name.replace(/__name__/g, newIndex);
            var swap = getFieldValue(oldField.find("[name='" + oldName + "']"));
            putFieldValue(oldField.find("[name='" + oldName + "']"), getFieldValue(newField.find("[name='" + newName + "']")));
            putFieldValue(newField.find("[name='" + newName + "']"), swap);
         });
      };

      var settings = $.extend(true, {}, defaults, options);

      if ($(settings.container).length === 0) {
         console.log("jquery.collection.js: a container should exist to handle events.");
         return false;
      }

      var elems = $(this);
      elems.each(function() {

         var elem = $(this);
         if (elem.data('collection') !== undefined) {
            var collection = $('#' + elem.data('collection'));
            if (collection.length === 0) {
               console.log("jquery.collection.js: the given collection field does not exist.");
               return true;
            }
         } else {
             collection = elem;
         }

         if (collection.data('prototype') === null) {
            console.log("jquery.collection.js: the given collection field has no prototype, check that your field has the prototype option set to true.");
            return true;
         }

         collection.data('collection-settings', settings);

         var skeletons = [];
         $(collection.data('prototype')).find('[name]').each(function() {
            var that = $(this);
            skeletons.push(that.attr('name'));
         });
         collection.data('collection-skeletons', skeletons);

         dumpCollectionActions(collection, settings);

         var container = $(settings.container);

         container
            .undelegate('.collection-action', 'click')
            .delegate('.collection-action', 'click', function(e) {
               var that = $(this);
               var collection = $('#' + that.data('collection'));
               var settings = collection.data('collection-settings');
               var elements = collection.find('> div').not('.collection-tmp');

               if ((that.is('.collection-add') || that.is('.collection-main-add')) && settings.enable_add &&
                       elements.length < settings.max && settings.before_add(collection, element)) {
                  var prototype = collection.data('prototype');
                  for (var index = 0; (index < settings.max); index++) {
                     var code = $(prototype.replace(/__name__/g, index));
                     var tmp = collection.find('> .collection-tmp');
                     var id = tmp.html(code).find(':input').first().attr('id');
                     tmp.empty();
                     if (container.find('#' + id).length === 0) {
                        if (that.data('element') === undefined) {
                           tmp.before(code);
                        } else {
                           $('#' + that.data('element')).after(code);
                        }
                        var action = code.find('.collection-add');
                        if (action.length > 0) {
                           action.addClass('collection-action').data('collection', collection.attr('id'));
                        }
                        if (!settings.after_add(collection, element)) {
                            code.remove();
                        }
                        break;
                     }
                  }
               }

               var element = $('#' + that.data('element'));
               var index = elements.index(element);

               if (that.is('.collection-remove') && settings.enable_remove &&
                       elements.length > settings.min && settings.before_remove(collection, element)) {
                    var backup = element.clone({withDataAndEvents: true});
                    element.remove();
                    if (!settings.after_remove(collection, backup)) {
                       var elements = collection.find('> div').not('.collection-tmp');
                       if (index === elements.length) {
                           elements.eq(index - 1).after(backup);
                       } else {
                           elements.eq(index).before(backup);
                       }
                    }
               }

               if (that.is('.collection-up') && settings.enable_up) {
                  if (index !== 0 && settings.before_up(collection, element)) {
                     swapFields(collection, elements, index, index - 1);
                     if (!settings.after_up(collection, element)) {
                         swapFields(collection, elements, index - 1, index);
                     }
                  }
               }

               if (that.is('.collection-down') && settings.enable_down) {
                  if (index !== (elements.length - 1) && settings.before_down(collection, element)) {
                     swapFields(collection, elements, index, index + 1);
                     if (!settings.after_down(collection, elements)) {
                         swapFields(collection, elements, index + 1, index);
                     }
                  }
               }

               dumpCollectionActions(collection, settings);
               e.preventDefault();
            })
         ;

      });

      return true;
   };

})(jQuery);
<|endoftext|>