<commit_before>// WrapUp base class

// core requires

var fs          = require("fs"),
    readFile    = fs.readFileSync,
    writeFile   = fs.writeFileSync,
    watchFile   = fs.watchFile,
    unwatchFile = fs.unwatchFile,
    path        = require("path")

//@* i love sebmarkbage
var Module = require("module")

// dependencies

var ujs     = require("uglify-js"),
    parser  = ujs.parser,
    uglify  = ujs.uglify

var prime   = require("prime/prime"),
    Emitter = require("prime/util/emitter")

// util methods

var replaces = function(str, obj){
    for (var key in obj) str = str.replace(new RegExp("@" + key, "g"), obj[key])
    return str
}

var exclude = function(src, excludes){
    if (!excludes || !excludes.length) return src
    return src.replace(/\/\*\(([\w\s.&]+)\)\?\*\/([\s\S]*?)\/\*\:([\w\s.]+)?\*\//g, function(match, exclusions, method, otherwise){
        if (otherwise === undefined) otherwise = ""
        exclusions = exclusions.split("&&").map(function(x){
            return x.trim()
        })
        for (var i = 0, l = exclusions.length; i < l; i++) if (excludes.indexOf(exclusions[i]) > -1) return otherwise
        return method
    })
}

// templates

var ipath = path.join(__dirname, "../includes/"),
    boil  = "(function(modules){" + readFile(ipath + "require.js", "utf-8") + "\n@wrup\n@named\n@nameless})({@modules})",
    wrup  = "window['@wrup'] = function(id){return (id = map[id]) ? require(id) : null}",
    req   = "require('@id')",
    glob  = 'window@object["@name"] = ' + req,
    mods  = "'@id': function(require, module, exports, global){@src}"

// WrapUp

var WrapUp = prime({

    inherits: Emitter,

    constructor: function(){
        this.packages = {}
        this.modules  = {}
        this.named    = {}
        this.nameless = []
        this.required = []

        this.index = 0
    },

    exclude: function(){
        if (!this.excludes) this.excludes = []
        Array.prototype.push.apply(this.excludes, Array.prototype.slice.call(arguments))
        return this
    },

    scan: function(what, from){

        var modulefull = this.resolve(what, from)

        if (modulefull == null){
            this.emit("error:resolve", what, from)
            return null
        }

        if (modulefull === true){
            this.emit("error:native", what, from)
            return null
        }

        var module = this.modules[modulefull]

        if (module) return module.uid

        var src = readFile(modulefull, "utf-8").toString()

        // custom excludes
        src = exclude(src, this.excludes)

        var ast
        try {
            // parse the code with ujs to see if there are any errors
            // and so we can walk through the AST to find require()s
            ast = parser.parse(src)
        } catch (err){
            this.emit("error:js", modulefull, from, err)
            return null
        }

        module = this.modules[modulefull] = {
            uid: (this.index++).toString(36),
            deps: []
        }

        var self = this

        var walkAST = function(ast){
            if (ast[0] == 'call'){
                // is it a require() call
                if (ast[1][0] == 'name' && ast[1][1] == 'require'){
                    // get the first argument, it should be a string
                    var dep = ast[2][0] && ast[2][0][0] == 'string' && ast[2][0][1]
                    if (dep !== false){
                        var k = self.scan(dep, modulefull)
                        if (k){
                            module.deps.push(k)
                            ast[2][0][1] = k
                        } else {
                            ast[0] = 'name'
                            ast[1] = 'null'
                            ast.pop()
                        }
                        return
                    }
                }
            }
            // no require call; recurse into the other subtrees
            for (var i = 0; i < ast.length; i++){
                if (Array.isArray(ast[i])) walkAST(ast[i])
            }
        }

        walkAST(ast)
        module.src = uglify.gen_code(ast)

        return module.uid
    },

    resolve: function(what, from){
        // this makes sure we always use the same directory for a specified package (the first it encounters)
        // this might not be ideal, but duplicating packages for the web is even worse

        var module = this.nodeResolve(what, from)

        if (!module) return null // cannot find module
        if (module.indexOf("/") !== 0) return true // native require

        if (this.modules[module]) return module

        var jsonpath = this.findJSON(module)
        if (!jsonpath) return module // not part of any package

        var pkgpath = path.dirname(jsonpath) + "/"
        var modulepath = module.replace(pkgpath, "")

        var json = require(jsonpath)
        var id = json.name + "@" + json.version
        var prevpkgpath = this.packages[id]
        pkgpath = prevpkgpath || (this.packages[id] = pkgpath)

        return prevpkgpath ? this.nodeResolve(path.join(pkgpath, modulepath), from) : module
    },

    nodeResolve: function(module, from){ //resolve module from cwd or relative to another module.
        from = (from == null) ? path.join(process.cwd(), "wrup.js") : path.resolve(from)
        var m = new Module(from)
        m.filename = from
        m.paths = Module._nodeModulePaths(path.dirname(from))
        try {
            return Module._resolveFilename(module, m)
        } catch (err){}
        return null
    },

    findJSON: function(file){
        var dirname = file
        while (dirname = path.dirname(dirname)){
            var json = path.join(dirname, "package.json")
            if (path.existsSync(json)) return json
            if (dirname === "/") break
        }
        return null
    },

    require: function(namespace, module){

        this.required.push([namespace, module])

        if (module == null){ // either [namespace, module] or [module]
            module = namespace
            namespace = null
        }

        var id = null
        if (!namespace || !this.named[namespace]){
            if (module == null) return this
            id = this.scan(module)
            if (id){
                if (namespace) this.named[namespace] = id
                else if (this.nameless.indexOf(id) === -1) this.nameless.push(id)
            }
        } else {
            this.emit("error:namespace", namespace, module)
        }
        return this
    },

    log: function(prefix){

        if (this.logging) return this

        if (prefix == null) prefix = "ERROR: "

        this.on("error:js", function(module, from, err){
            console.error(prefix + "the module %s required by %s had a javascript error at line %d, column %d: %s", module, from || 'you', err.line, err.col, err.message)
        })

        this.on("error:resolve", function(module, from){
            console.error(prefix + "the module %s required by %s could not be resolved", module, from || 'you')
        })

        this.on("error:native", function(module, from){
            console.error(prefix + "the module %s required by %s is a native require", module, from || 'you')
        })

        this.on("error:package", function(pkg){
            console.error(prefix + "the package %s could not be resolved", pkg)
        })

        this.on("error:namespace", function(namespace, module){
            console.error(prefix + "the namespace %s was already in use, could not include %s", namespace, module)
        })

        this.on("error:access", function(){
            console.error(prefix + "both --globalize and --wrup are false, modules are not accessible")
        })

        this.on("error:empty", function(){
            console.error(prefix + "no modules required")
        })

        this.on("error:graphviz", function(err){
            console.error(prefix + "graphviz not found")
        })

        this.on("error:internal", function(err){
            console.error(prefix + "internal wrapup error at line %d, column %d: %s", err.line, err.col, err.message)
        })

        this.logging = true

        return this

    },

    up: function(options){

        if (options == null) options = {}

        if (options.wrup == null) options.wrup = false
        if (options.wrup === true) options.wrup = "wrup"
        if (options.globalize == null) options.globalize = "window"
        if (options.compress == null) options.compress = false
        if (options.watch == null) options.watch = false
        if (options.output == null) options.output = false

        if (!options.wrup && !options.globalize){
            this.emit("error:access")
            return ""
        }

        var flat     = [],
            globals  = [],
            map      = {},
            requires = [],
            modules  = {}

        var self = this

        var watch = function(fullpath){

            var filedata = readFile(fullpath, "utf-8")

            watchFile(fullpath, {interval: 10}, function(){
                var data = readFile(fullpath, "utf-8")
                if (filedata !== data){

                    for (var p in self.modules) unwatchFile(p) // unwatch everything

                    self.emit("change", fullpath)
                    var required = self.required // save current requires
                    self.constructor() // resets WrapUp
                    required.forEach(function(r){ // re-requires
                        self.require(r[0], r[1])
                    })
                    self.up(options)
                }
            })

        }

        for (var fullpath in this.modules){
            var mod = this.modules[fullpath]
            flat.push(replaces(mods, {id: mod.uid, src: mod.src}))

            modules[path.relative(process.cwd(), fullpath)] = mod.deps.map(function(dep){
                for (var fullpath in this.modules){
                    if (this.modules[fullpath].uid === dep) return path.relative(process.cwd(), fullpath)
                }
            }, this)

            if (options.watch) watch(fullpath)
        }

        if (options.graph){

            try {
                var graphviz = require('graphviz')
            } catch (err){
                this.emit("error:graphviz")
                return ""
            }
            var graph = graphviz.digraph("G")

            for (var x in modules){
                var xn = graph.addNode(x)
                var deps = modules[x]
                deps.forEach(function(dep){
                    graph.addEdge(x, dep)
                })
            }

            var dot = graph.to_dot()

            if (options.output){
                var ext = path.extname(options.output)
                graph.output(ext.slice(1), options.output)
            }

            this.emit("done", dot)
            return dot
        }

        if (flat.length === 0){
            this.emit("error:empty")
            return ""
        }

        for (var ns in this.named){
            if (options.globalize) globals.push(replaces(glob, {
                object: (options.globalize === "window") ? '' : '["' + options.globalize + '"]',
                id: this.named[ns],
                name: ns
            }))
            map[ns] = this.named[ns]
        }

        this.nameless.forEach(function(mid){
            requires.push(replaces(req, {id: mid}))
        })

        var js = replaces(boil, {
            modules: flat.join(","),
            named: (options.globalize) ? globals.join("\n") : "",
            nameless: requires.join("\n"),
            wrup: (options.wrup) ? ", map = " + JSON.stringify(map) + "\n" + replaces(wrup, {wrup: options.wrup}) : ""
        })

        try {

            var ast = parser.parse(js)

            if (options.compress){
                ast = uglify.ast_mangle(ast)
                ast = uglify.ast_squeeze(ast)
            }

            js = uglify.gen_code(ast, {beautify: !options.compress})

            if (options.output) writeFile(options.output, js, "utf-8")

            this.emit("done", js)

            return js

        } catch(err){
            this.emit("error:internal", err)
        }

        return ""

    }

})

WrapUp.prototype.toString = WrapUp.prototype.up

module.exports = WrapUp
<commit_msg>modules with errors are still being watched now.<commit_after>// WrapUp base class

// core requires

var fs          = require("fs"),
    readFile    = fs.readFileSync,
    writeFile   = fs.writeFileSync,
    watchFile   = fs.watchFile,
    unwatchFile = fs.unwatchFile,
    path        = require("path")

//@* i love sebmarkbage
var Module = require("module")

// dependencies

var ujs     = require("uglify-js"),
    parser  = ujs.parser,
    uglify  = ujs.uglify

var prime   = require("prime/prime"),
    Emitter = require("prime/util/emitter")

// util methods

var replaces = function(str, obj){
    for (var key in obj) str = str.replace(new RegExp("@" + key, "g"), obj[key])
    return str
}

var exclude = function(src, excludes){
    if (!excludes || !excludes.length) return src
    return src.replace(/\/\*\(([\w\s.&]+)\)\?\*\/([\s\S]*?)\/\*\:([\w\s.]+)?\*\//g, function(match, exclusions, method, otherwise){
        if (otherwise === undefined) otherwise = ""
        exclusions = exclusions.split("&&").map(function(x){
            return x.trim()
        })
        for (var i = 0, l = exclusions.length; i < l; i++) if (excludes.indexOf(exclusions[i]) > -1) return otherwise
        return method
    })
}

// templates

var ipath = path.join(__dirname, "../includes/"),
    boil  = "(function(modules){" + readFile(ipath + "require.js", "utf-8") + "\n@wrup\n@named\n@nameless})({@modules})",
    wrup  = "window['@wrup'] = function(id){return (id = map[id]) ? require(id) : null}",
    req   = "require('@id')",
    glob  = 'window@object["@name"] = ' + req,
    mods  = "'@id': function(require, module, exports, global){@src}"

// WrapUp

var WrapUp = prime({

    inherits: Emitter,

    constructor: function(){
        this.packages = {}
        this.modules  = {}
        this.named    = {}
        this.nameless = []
        this.required = []

        this.index = 0
    },

    exclude: function(){
        if (!this.excludes) this.excludes = []
        Array.prototype.push.apply(this.excludes, Array.prototype.slice.call(arguments))
        return this
    },

    scan: function(what, from){

        var modulefull = this.resolve(what, from)

        if (modulefull == null){
            this.emit("error:resolve", what, from)
            return null
        }

        if (modulefull === true){
            this.emit("error:native", what, from)
            return null
        }

        var module = this.modules[modulefull]

        if (module) return module.uid

        var src = readFile(modulefull, "utf-8").toString()

        // custom excludes
        src = exclude(src, this.excludes)

        var ast
        try {
            // parse the code with ujs to see if there are any errors
            // and so we can walk through the AST to find require()s
            ast = parser.parse(src)
        } catch (err){
            this.emit("error:js", modulefull, from, err)
            this.modules[modulefull] = {err: true}
            return null
        }

        module = this.modules[modulefull] = {
            uid: (this.index++).toString(36),
            deps: []
        }

        var self = this

        var walkAST = function(ast){
            if (ast[0] == 'call'){
                // is it a require() call
                if (ast[1][0] == 'name' && ast[1][1] == 'require'){
                    // get the first argument, it should be a string
                    var dep = ast[2][0] && ast[2][0][0] == 'string' && ast[2][0][1]
                    if (dep !== false){
                        var k = self.scan(dep, modulefull)
                        if (k){
                            module.deps.push(k)
                            ast[2][0][1] = k
                        } else {
                            ast[0] = 'name'
                            ast[1] = 'null'
                            ast.pop()
                        }
                        return
                    }
                }
            }
            // no require call; recurse into the other subtrees
            for (var i = 0; i < ast.length; i++){
                if (Array.isArray(ast[i])) walkAST(ast[i])
            }
        }

        walkAST(ast)
        module.src = uglify.gen_code(ast)

        return module.uid
    },

    resolve: function(what, from){
        // this makes sure we always use the same directory for a specified package (the first it encounters)
        // this might not be ideal, but duplicating packages for the web is even worse

        var module = this.nodeResolve(what, from)

        if (!module) return null // cannot find module
        if (module.indexOf("/") !== 0) return true // native require

        if (this.modules[module]) return module

        var jsonpath = this.findJSON(module)
        if (!jsonpath) return module // not part of any package

        var pkgpath = path.dirname(jsonpath) + "/"
        var modulepath = module.replace(pkgpath, "")

        var json = require(jsonpath)
        var id = json.name + "@" + json.version
        var prevpkgpath = this.packages[id]
        pkgpath = prevpkgpath || (this.packages[id] = pkgpath)

        return prevpkgpath ? this.nodeResolve(path.join(pkgpath, modulepath), from) : module
    },

    nodeResolve: function(module, from){ //resolve module from cwd or relative to another module.
        from = (from == null) ? path.join(process.cwd(), "wrup.js") : path.resolve(from)
        var m = new Module(from)
        m.filename = from
        m.paths = Module._nodeModulePaths(path.dirname(from))
        try {
            return Module._resolveFilename(module, m)
        } catch (err){}
        return null
    },

    findJSON: function(file){
        var dirname = file
        while (dirname = path.dirname(dirname)){
            var json = path.join(dirname, "package.json")
            if (path.existsSync(json)) return json
            if (dirname === "/") break
        }
        return null
    },

    require: function(namespace, module){

        this.required.push([namespace, module])

        if (module == null){ // either [namespace, module] or [module]
            module = namespace
            namespace = null
        }

        var id = null
        if (!namespace || !this.named[namespace]){
            if (module == null) return this
            id = this.scan(module)
            if (id){
                if (namespace) this.named[namespace] = id
                else if (this.nameless.indexOf(id) === -1) this.nameless.push(id)
            }
        } else {
            this.emit("error:namespace", namespace, module)
        }
        return this
    },

    log: function(prefix){

        if (this.logging) return this

        if (prefix == null) prefix = "ERROR: "

        this.on("error:js", function(module, from, err){
            console.error(prefix + "the module %s required by %s had a javascript error at line %d, column %d: %s", module, from || 'you', err.line, err.col, err.message)
        })

        this.on("error:resolve", function(module, from){
            console.error(prefix + "the module %s required by %s could not be resolved", module, from || 'you')
        })

        this.on("error:native", function(module, from){
            console.error(prefix + "the module %s required by %s is a native require", module, from || 'you')
        })

        this.on("error:package", function(pkg){
            console.error(prefix + "the package %s could not be resolved", pkg)
        })

        this.on("error:namespace", function(namespace, module){
            console.error(prefix + "the namespace %s was already in use, could not include %s", namespace, module)
        })

        this.on("error:access", function(){
            console.error(prefix + "both --globalize and --wrup are false, modules are not accessible")
        })

        this.on("error:empty", function(){
            console.error(prefix + "no modules required")
        })

        this.on("error:graphviz", function(err){
            console.error(prefix + "graphviz not found")
        })

        this.on("error:internal", function(err){
            console.error(prefix + "internal wrapup error at line %d, column %d: %s", err.line, err.col, err.message)
        })

        this.logging = true

        return this

    },

    up: function(options){

        if (options == null) options = {}

        if (options.wrup == null) options.wrup = false
        if (options.wrup === true) options.wrup = "wrup"
        if (options.globalize == null) options.globalize = "window"
        if (options.compress == null) options.compress = false
        if (options.watch == null) options.watch = false
        if (options.output == null) options.output = false

        if (!options.wrup && !options.globalize){
            this.emit("error:access")
            return ""
        }

        var flat     = [],
            globals  = [],
            map      = {},
            requires = [],
            modules  = {}

        var self = this

        var watch = function(fullpath){

            var filedata = readFile(fullpath, "utf-8")

            watchFile(fullpath, {interval: 10}, function(){
                var data = readFile(fullpath, "utf-8")
                if (filedata !== data){

                    for (var p in self.modules) unwatchFile(p) // unwatch everything

                    self.emit("change", fullpath)
                    var required = self.required // save current requires
                    self.constructor() // resets WrapUp
                    required.forEach(function(r){ // re-requires
                        self.require(r[0], r[1])
                    })
                    self.up(options)
                }
            })

        }

        for (var fullpath in this.modules){
            var mod = this.modules[fullpath]

            if (!mod.err){

                flat.push(replaces(mods, {id: mod.uid, src: mod.src}))

                modules[path.relative(process.cwd(), fullpath)] = mod.deps.map(function(dep){
                    for (var fullpath in this.modules){
                        if (this.modules[fullpath].uid === dep) return path.relative(process.cwd(), fullpath)
                    }
                }, this)

            }

            if (options.watch) watch(fullpath)
        }

        if (options.graph){

            try {
                var graphviz = require('graphviz')
            } catch (err){
                this.emit("error:graphviz")
                return ""
            }
            var graph = graphviz.digraph("G")

            for (var x in modules){
                var xn = graph.addNode(x)
                var deps = modules[x]
                deps.forEach(function(dep){
                    graph.addEdge(x, dep)
                })
            }

            var dot = graph.to_dot()

            if (options.output){
                var ext = path.extname(options.output)
                graph.output(ext.slice(1), options.output)
            }

            this.emit("done", dot)
            return dot
        }

        if (flat.length === 0){
            this.emit("error:empty")
            return ""
        }

        for (var ns in this.named){
            if (options.globalize) globals.push(replaces(glob, {
                object: (options.globalize === "window") ? '' : '["' + options.globalize + '"]',
                id: this.named[ns],
                name: ns
            }))
            map[ns] = this.named[ns]
        }

        this.nameless.forEach(function(mid){
            requires.push(replaces(req, {id: mid}))
        })

        var js = replaces(boil, {
            modules: flat.join(","),
            named: (options.globalize) ? globals.join("\n") : "",
            nameless: requires.join("\n"),
            wrup: (options.wrup) ? ", map = " + JSON.stringify(map) + "\n" + replaces(wrup, {wrup: options.wrup}) : ""
        })

        try {

            var ast = parser.parse(js)

            if (options.compress){
                ast = uglify.ast_mangle(ast)
                ast = uglify.ast_squeeze(ast)
            }

            js = uglify.gen_code(ast, {beautify: !options.compress})

            if (options.output) writeFile(options.output, js, "utf-8")

            this.emit("done", js)

            return js

        } catch(err){
            this.emit("error:internal", err)
        }

        return ""

    }

})

WrapUp.prototype.toString = WrapUp.prototype.up

module.exports = WrapUp
<|endoftext|>


<commit_before>/**
 * Represents the writer which is used by a parser instance and is called upon to
 * translate the obtained intelligence to instructions leading to a functional
 * JavaScript implementation which relies on an engine to mediate template
 * completion.
 */
function Writer() {
	// Initialize the element insert. This is used to write an element definition before written control flow statements.
	this.elementInsert = -1;
	// Initialize the include depth. This is used to determine the op# variable name.
	this.includeDepth = 0;
	// Initialize the instructions. This is used to add each instruction which forms the functional JavaScript implementation.
	this.instructions = ['var r=[];'];
	// Initialize the partial depth. This is used to find partial type (differentiating between direct or include queue).
	this.partialDepth = 0;
	// Initialize the priorities. This is used to ensure the proper emitting order of instructions.
	this.priorities = ['data-include', 'data-partial', 'data-if', 'data-each'];
	// Initialize the variable depth. This is used to determine the v# variable name.
	this.variableDepth = 0;
}

/**
 * Arrange attributes to a ensure a proper emitting.
 *
 * @param attributes The attributes.
 * @param reverse Indicates whether the sorting is to be reversed.
 */
Writer.prototype.arrange = function (attributes, reverse) {
	// Initialize self.
	var self = this;
	// Initialize the find index function.
	var findIndex = function (key) {
		// Iterate through each priority.
		for (var i = 0; i < self.priorities.length; i++) {
			// Check if the priority matches the key.
			if (self.priorities[i] == key) {
				// Return the index.
				return i;
			}
		}
		// Return minus one.
		return -1;
	}
	// Sort the attributes.
	attributes.sort(function (a, b) {
		// Retrieve the priority index and sort.
		return reverse ? findIndex(a.key) < findIndex(b.key) : findIndex(a.key) > findIndex(b.key);
	});
};

/**
 * Begin a command statement.
 *
 * @param attributes The attributes.
 * @returns Indicates whether the element is to be disposed of.
 */
Writer.prototype.begin = function (attributes) {
	// Initialize the response.
	var response = false;
	// Initialize self.
	var self = this;
	// Define the function to clean the attribute and track the element insert.
	var cleanAttributeAndTrack = function (i) {
		// Check if the element insert is invalid.
		if (self.elementInsert == -1) {
			// Set the element insert.
			self.elementInsert = self.instructions.length;
		}
		// Delete the attribute.
		delete attributes[i];
		// Set the response.
		response = true;
	};
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes);
	// Set the element insert to invalid.
	this.elementInsert = -1;
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an each statement.
			case 'data-each':
				// Retrieve the name of the variable.
				var variable = 'v' + this.variableDepth + '.' + attributes[i].value;
				// Retrieve the name of the iterator.
				var iterator = 'i' + this.variableDepth;
				// Increment the variable depth.
				this.variableDepth += 1;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to open an if statement.
				this.instructions.push('if(' + variable + '){');
				// Push the instruction to start an iteration.
				this.instructions.push('for(var ' + iterator + '=0;' + iterator + '<' + variable + '.length;' + iterator + '++){');
				// Push the instruction to create a variable with the new depth.
				this.instructions.push('var v' + this.variableDepth + '=' + variable + '[' + iterator + '];');
				// Break from iteration.
				break;
			// Check if this is an if statement.
			case 'data-if':
				// Determine if the if statement is negative.
				var isNegative = attributes[i].value.charAt(0) === '!';
				// Retrieve the value.
				var ifValue = isNegative ? attributes[i].value.substr(1) : attributes[i].value;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to open an if statement.
				this.instructions.push('if(' + (isNegative ? '!' : '') + 'v' + this.variableDepth + '.' + ifValue + '){');
				// Break from iteration.
				break;
			// Check if this is a include statement.
			case 'data-include':
				// Increment the include depth.
				this.includeDepth += 1;
				// Increment the partial depth.
				this.partialDepth += 1;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to initialize the output partial array.
				this.instructions.push('var op' + this.includeDepth + '=[];');
				// Break from iteration.
				break;
			// Check if this is a partial statement.
			case 'data-partial':
				// Retrieve the value.
				var partialValue = attributes[i].value;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Check if the partial depth indicates being in an include scope.
				if (this.partialDepth !== 0) {
					// Push the instruction to open a partial.
					this.instructions.push('op' + this.includeDepth + '[\'' + partialValue.replace('\'', '\\\'') + '\']=function(){');
					// Push the instruction to initialize the partial response array.
					this.instructions.push('var r=[];');
				}
				// Decrement the partial depth.
				this.partialDepth -= 1;
				// Break from iteration.
				break;
		}
	}
	// Return the response.
	return response;
};

/**
 * End a command statement.
 *
 * @param attributes The attributes.
 */
Writer.prototype.end = function (attributes) {
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes, true);
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an if statement.
			case 'data-each':
				// Push the instruction to close an each statement.
				this.instructions.push('}}');
				// Decrement the variable depth.
				this.variableDepth -= 1;
				// Break from iteration.
				break;
			// Check if this is an if statement.
			case 'data-if':
				// Push the instruction to close an if statement.
				this.instructions.push('}');
				// Break from iteration.
				break;
			// Check if this is a include statement.
			case 'data-include':
				// Push the instruction to include a template using the output partial array.
				this.instructions.push('r.push(e.include(\'' + attributes[i].value.replace('\'', '\\\'') + '\',v' + this.variableDepth + ',op' + this.includeDepth + '));');
				// Decrement the include depth.
				this.includeDepth -= 1;
				// Decrement the partial depth.
				this.partialDepth -= 1;
				// Break from iteration.
				break;
			// Check if this is a partial statement.
			case 'data-partial':
				// Increment the partial depth.
				this.partialDepth += 1;
				// Check if the partial depth indicates being in an include scope.
				if (this.partialDepth !== 0) {
					// Push the instruction to return the partial response array.
					this.instructions.push('return r.join(\'\');');
					// Push the instruction to close a partial.
					this.instructions.push('};');
				} else {
					// Push the partial direct instruction.
					this.instructions.push('r.push(e.partial(\'' + attributes[i].value.replace('\'', '\\\'') + '\',ip));');
				}
				// Break from iteration.
				break;
		}
	}
};

/**
 * Invoke a non-nested command statement.
 *
 * @param attributes The attributes.
 */
Writer.prototype.invoke = function (attributes) {
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes);
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an include statement.
			case 'data-include':
				// Push the include instruction.
				this.instructions.push('r.push(e.include(\'' + attributes[i].value.replace('\'', '\\\'') + '\',v' + this.variableDepth + '));');
				// Delete the attribute.
				delete attributes[i];
				// Return true.
				return true;
				// Check if this is a partial statement.
			case 'data-partial':
				// Push the partial direct instruction.
				this.instructions.push('r.push(e.partial(\'' + attributes[i].value.replace('\'', '\\\'') + '\',ip));');
				// Delete the attribute.
				delete attributes[i];
				// Return true.
				return true;
		}
	}
	// Return false.
	return false;
};

/**
 * Add a variable using each handler.
 *
 * @param name The variable name.
 * @param handlers Each handler.
 * @param requiresEscaping Indicates whether HTML escaping is required.
 * @param isOpeningElement Indicates whether this literal is an opening element.
 */
Writer.prototype.variable = function (name, handlers, requiresEscaping, isOpeningElement) {
	// Initialize the value.
	var variable = 'v' + this.variableDepth + (name === 'this' ? '' : '.' + name);
	// Check if the variable is an escaped variable.
	if (requiresEscaping) {
		// Push the escape handler on the handler list.
		handlers.push('escape');
	}
	// Iterate through each handler.
	for (var i = 0; i < handlers.length; i += 1) {
		// Set the value to include the handler wrapped variable.
		variable = 'e.handler(\'' + handlers[i] + '\',' + variable + ')';
	}
	// Check if this literal is an opening element.
	if (isOpeningElement) {
		// Insert the variable instruction.
		this.instructions.splice(this.elementInsert, 0, 'r.push(' + variable + ');');
		// Increment the element insert location.
		this.elementInsert++;
	} else {
		// Push the variable instruction.
		this.instructions.push('r.push(' + variable + ');');
	}
};

/**
 * Add literal text.
 *
 * @param text The text.
 * @param isOpeningElement Indicates whether this literal is an opening element.
 */
Writer.prototype.text = function (text, isOpeningElement) {
	// Initialize the text to add.
	var textToAdd = 'r.push(\'' + text.replace('\'', '\\\'') + '\');';
	// Check if this literal is an opening element.
	if (isOpeningElement) {
		// Insert the text instruction.
		this.instructions.splice(this.elementInsert, 0, textToAdd);
		// Increment the element insert location.
		this.elementInsert++;
	} else {
		// Push the text instruction.
		this.instructions.push(textToAdd);
	}
};

/**
* Convert the written instructions to a functional JavaScript implementation.
*
* @returns The functional JavaScript implementation.
*/
Writer.prototype.toString = function () {
	// Join the instructions and return the output string.
	return this.instructions.join('') + 'return r.join(\'\');';
};

// Check if module is defined.
if (typeof module !== undefined) {
	// Export the writer.
	module.exports = Writer;
}<commit_msg>Fixed inserting opening literals (doctype, ...).<commit_after>/**
 * Represents the writer which is used by a parser instance and is called upon to
 * translate the obtained intelligence to instructions leading to a functional
 * JavaScript implementation which relies on an engine to mediate template
 * completion.
 */
function Writer() {
	// Initialize the element insert. This is used to write an element definition before written control flow statements.
	this.elementInsert = -1;
	// Initialize the include depth. This is used to determine the op# variable name.
	this.includeDepth = 0;
	// Initialize the instructions. This is used to add each instruction which forms the functional JavaScript implementation.
	this.instructions = ['var r=[];'];
	// Initialize the partial depth. This is used to find partial type (differentiating between direct or include queue).
	this.partialDepth = 0;
	// Initialize the priorities. This is used to ensure the proper emitting order of instructions.
	this.priorities = ['data-include', 'data-partial', 'data-if', 'data-each'];
	// Initialize the variable depth. This is used to determine the v# variable name.
	this.variableDepth = 0;
}

/**
 * Arrange attributes to a ensure a proper emitting.
 *
 * @param attributes The attributes.
 * @param reverse Indicates whether the sorting is to be reversed.
 */
Writer.prototype.arrange = function (attributes, reverse) {
	// Initialize self.
	var self = this;
	// Initialize the find index function.
	var findIndex = function (key) {
		// Iterate through each priority.
		for (var i = 0; i < self.priorities.length; i++) {
			// Check if the priority matches the key.
			if (self.priorities[i] == key) {
				// Return the index.
				return i;
			}
		}
		// Return minus one.
		return -1;
	}
	// Sort the attributes.
	attributes.sort(function (a, b) {
		// Retrieve the priority index and sort.
		return reverse ? findIndex(a.key) < findIndex(b.key) : findIndex(a.key) > findIndex(b.key);
	});
};

/**
 * Begin a command statement.
 *
 * @param attributes The attributes.
 * @returns Indicates whether the element is to be disposed of.
 */
Writer.prototype.begin = function (attributes) {
	// Initialize the response.
	var response = false;
	// Initialize self.
	var self = this;
	// Define the function to clean the attribute and track the element insert.
	var cleanAttributeAndTrack = function (i) {
		// Check if the element insert is invalid.
		if (self.elementInsert == -1) {
			// Set the element insert.
			self.elementInsert = self.instructions.length;
		}
		// Delete the attribute.
		delete attributes[i];
		// Set the response.
		response = true;
	};
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes);
	// Set the element insert to invalid.
	this.elementInsert = -1;
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an each statement.
			case 'data-each':
				// Retrieve the name of the variable.
				var variable = 'v' + this.variableDepth + '.' + attributes[i].value;
				// Retrieve the name of the iterator.
				var iterator = 'i' + this.variableDepth;
				// Increment the variable depth.
				this.variableDepth += 1;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to open an if statement.
				this.instructions.push('if(' + variable + '){');
				// Push the instruction to start an iteration.
				this.instructions.push('for(var ' + iterator + '=0;' + iterator + '<' + variable + '.length;' + iterator + '++){');
				// Push the instruction to create a variable with the new depth.
				this.instructions.push('var v' + this.variableDepth + '=' + variable + '[' + iterator + '];');
				// Break from iteration.
				break;
			// Check if this is an if statement.
			case 'data-if':
				// Determine if the if statement is negative.
				var isNegative = attributes[i].value.charAt(0) === '!';
				// Retrieve the value.
				var ifValue = isNegative ? attributes[i].value.substr(1) : attributes[i].value;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to open an if statement.
				this.instructions.push('if(' + (isNegative ? '!' : '') + 'v' + this.variableDepth + '.' + ifValue + '){');
				// Break from iteration.
				break;
			// Check if this is a include statement.
			case 'data-include':
				// Increment the include depth.
				this.includeDepth += 1;
				// Increment the partial depth.
				this.partialDepth += 1;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Push the instruction to initialize the output partial array.
				this.instructions.push('var op' + this.includeDepth + '=[];');
				// Break from iteration.
				break;
			// Check if this is a partial statement.
			case 'data-partial':
				// Retrieve the value.
				var partialValue = attributes[i].value;
				// Clean the attribute and track the element insert.
				cleanAttributeAndTrack(i);
				// Check if the partial depth indicates being in an include scope.
				if (this.partialDepth !== 0) {
					// Push the instruction to open a partial.
					this.instructions.push('op' + this.includeDepth + '[\'' + partialValue.replace('\'', '\\\'') + '\']=function(){');
					// Push the instruction to initialize the partial response array.
					this.instructions.push('var r=[];');
				}
				// Decrement the partial depth.
				this.partialDepth -= 1;
				// Break from iteration.
				break;
		}
	}
	// Return the response.
	return response;
};

/**
 * End a command statement.
 *
 * @param attributes The attributes.
 */
Writer.prototype.end = function (attributes) {
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes, true);
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an if statement.
			case 'data-each':
				// Push the instruction to close an each statement.
				this.instructions.push('}}');
				// Decrement the variable depth.
				this.variableDepth -= 1;
				// Break from iteration.
				break;
			// Check if this is an if statement.
			case 'data-if':
				// Push the instruction to close an if statement.
				this.instructions.push('}');
				// Break from iteration.
				break;
			// Check if this is a include statement.
			case 'data-include':
				// Push the instruction to include a template using the output partial array.
				this.instructions.push('r.push(e.include(\'' + attributes[i].value.replace('\'', '\\\'') + '\',v' + this.variableDepth + ',op' + this.includeDepth + '));');
				// Decrement the include depth.
				this.includeDepth -= 1;
				// Decrement the partial depth.
				this.partialDepth -= 1;
				// Break from iteration.
				break;
			// Check if this is a partial statement.
			case 'data-partial':
				// Increment the partial depth.
				this.partialDepth += 1;
				// Check if the partial depth indicates being in an include scope.
				if (this.partialDepth !== 0) {
					// Push the instruction to return the partial response array.
					this.instructions.push('return r.join(\'\');');
					// Push the instruction to close a partial.
					this.instructions.push('};');
				} else {
					// Push the partial direct instruction.
					this.instructions.push('r.push(e.partial(\'' + attributes[i].value.replace('\'', '\\\'') + '\',ip));');
				}
				// Break from iteration.
				break;
		}
	}
};

/**
 * Invoke a non-nested command statement.
 *
 * @param attributes The attributes.
 */
Writer.prototype.invoke = function (attributes) {
	// Arrange attributes to a ensure a proper emitting.
	this.arrange(attributes);
	// Iterate through each attribute.
	for (var i = 0; i < attributes.length; i++) {
		// Switch based on the attribute key.
		switch (attributes[i].key) {
			// Check if this is an include statement.
			case 'data-include':
				// Push the include instruction.
				this.instructions.push('r.push(e.include(\'' + attributes[i].value.replace('\'', '\\\'') + '\',v' + this.variableDepth + '));');
				// Delete the attribute.
				delete attributes[i];
				// Return true.
				return true;
				// Check if this is a partial statement.
			case 'data-partial':
				// Push the partial direct instruction.
				this.instructions.push('r.push(e.partial(\'' + attributes[i].value.replace('\'', '\\\'') + '\',ip));');
				// Delete the attribute.
				delete attributes[i];
				// Return true.
				return true;
		}
	}
	// Return false.
	return false;
};

/**
 * Add a variable using each handler.
 *
 * @param name The variable name.
 * @param handlers Each handler.
 * @param requiresEscaping Indicates whether HTML escaping is required.
 * @param isOpeningElement Indicates whether this literal is an opening element.
 */
Writer.prototype.variable = function (name, handlers, requiresEscaping, isOpeningElement) {
	// Initialize the value.
	var variable = 'v' + this.variableDepth + (name === 'this' ? '' : '.' + name);
	// Check if the variable is an escaped variable.
	if (requiresEscaping) {
		// Push the escape handler on the handler list.
		handlers.push('escape');
	}
	// Iterate through each handler.
	for (var i = 0; i < handlers.length; i += 1) {
		// Set the value to include the handler wrapped variable.
		variable = 'e.handler(\'' + handlers[i] + '\',' + variable + ')';
	}
	// Check if this variable is an opening element.
	if (this.elementInsert !== -1 && isOpeningElement) {
		// Insert the variable instruction.
		this.instructions.splice(this.elementInsert, 0, 'r.push(' + variable + ');');
		// Increment the element insert location.
		this.elementInsert++;
	} else {
		// Push the variable instruction.
		this.instructions.push('r.push(' + variable + ');');
	}
};

/**
 * Add literal text.
 *
 * @param text The text.
 * @param isOpeningElement Indicates whether this literal is an opening element.
 */
Writer.prototype.text = function (text, isOpeningElement) {
	// Initialize the text to add.
	var textToAdd = 'r.push(\'' + text.replace('\'', '\\\'') + '\');';
	// Check if this literal is an opening element.
	if (this.elementInsert !== -1 && isOpeningElement) {
		// Insert the text instruction.
		this.instructions.splice(this.elementInsert, 0, textToAdd);
		// Increment the element insert location.
		this.elementInsert++;
	} else {
		// Push the text instruction.
		this.instructions.push(textToAdd);
	}
};

/**
* Convert the written instructions to a functional JavaScript implementation.
*
* @returns The functional JavaScript implementation.
*/
Writer.prototype.toString = function () {
	// Join the instructions and return the output string.
	return this.instructions.join('') + 'return r.join(\'\');';
};

// Check if module is defined.
if (typeof module !== undefined) {
	// Export the writer.
	module.exports = Writer;
}<|endoftext|>


<commit_before>(function () {

var sax;

if (typeof module !== 'undefined' && module.exports && !GLOBAL.xmldocAssumeBrowser) {
  // We're being used in a Node-like environment
  sax = require('sax');
}
else {
  // assume it's attached to the Window object in a browser
  sax = this.sax;

  if (!sax) // no sax for you!
    throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file.");
}

/*
XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument
behaves like an XmlElement by inheriting its attributes and functions.
*/

function XmlElement(tag) {
  // Capture the parser object off of the XmlDocument delegate
  var parser = delegates[delegates.length - 1].parser;

  this.name = tag.name;
  this.attr = tag.attributes;
  this.val = "";
  this.isValCdata = false;
  this.isValComment = false;
  this.children = [];
  this.firstChild = null;
  this.lastChild = null;

  // Assign parse information
  this.line = parser.line;
  this.column = parser.column;
  this.position = parser.position;
  this.startTagPosition = parser.startTagPosition;
}

// SaxParser handlers

XmlElement.prototype._opentag = function(tag) {

  var child = new XmlElement(tag);

  // add to our children array
  this.children.push(child);

  // update first/last pointers
  if (!this.firstChild) this.firstChild = child;
  this.lastChild = child;

  delegates.unshift(child);
};

XmlElement.prototype._closetag = function() {
  delegates.shift();
};

XmlElement.prototype._text = function(text) {
  this.val = text;
};

XmlElement.prototype._cdata = function(cdata) {
  this.val = cdata;
  this.isValCdata=true;
};

XmlElement.prototype._comment = function(comment) {
  this.val = comment;
  this.isValComment=true;
}

XmlElement.prototype._error = function(err) {
  throw err;
};

// Useful functions

XmlElement.prototype.eachChild = function(iterator, context) {
  for (var i=0, l=this.children.length; i<l; i++)
    if (iterator.call(context, this.children[i], i, this.children) === false) return;
};

XmlElement.prototype.childNamed = function(name) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if (child.name === name) return child;
  }
  return undefined;
};

XmlElement.prototype.childrenNamed = function(name) {
  var matches = [];

  for (var i=0, l=this.children.length; i<l; i++)
    if (this.children[i].name === name)
      matches.push(this.children[i]);

  return matches;
};

XmlElement.prototype.childWithAttribute = function(name,value) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if ( (value && child.attr[name] === value) || (!value && child.attr[name]) )
      return child;
  }
  return undefined;
};

XmlElement.prototype.descendantWithPath = function(path) {
  var descendant = this;
  var components = path.split('.');

  for (var i=0, l=components.length; i<l; i++)
    if (descendant)
      descendant = descendant.childNamed(components[i]);
    else
      return undefined;

  return descendant;
};

XmlElement.prototype.valueWithPath = function(path) {
  var components = path.split('@');
  var descendant = this.descendantWithPath(components[0]);
  if (descendant)
    return components.length > 1 ? descendant.attr[components[1]] : descendant.val;
  else
    return undefined;
};

// String formatting (for debugging)

XmlElement.prototype.toString = function(options) {
  return this.toStringWithIndent("", options);
};

XmlElement.prototype.toStringWithIndent = function(indent, options) {
  var s = indent + "<" + this.name;
  var linebreak = options && options.compressed ? "" : "\n";
  var preserveWhitespace = options && options.preserveWhitespace;

  for (var name in this.attr)
    if (Object.prototype.hasOwnProperty.call(this.attr, name))
        s += " " + name + '="' + escapeXML(this.attr[name]) + '"';

  var finalVal = '';
  if (this.isValCdata){
    finalVal = '<![CDATA['+this.val+']]>';
  } else if (preserveWhitespace) {
    finalVal = escapeXML(this.val);
  } else{
    finalVal = escapeXML(this.val.trim());
  }
  if (options && options.trimmed && finalVal.length > 25)
    finalVal = finalVal.substring(0,25).trim() + "…";

  if (this.children.length) {
    s += ">" + linebreak;

    var childIndent = indent + (options && options.compressed ? "" : "  ");

    if (finalVal.length)
      s += childIndent + finalVal + linebreak;

    for (var i=0, l=this.children.length; i<l; i++)
      s += this.children[i].toStringWithIndent(childIndent, options) + linebreak;

    s += indent + "</" + this.name + ">";
  }
  else if (finalVal.length) {
    s += ">" + finalVal + "</" + this.name +">";
  }
  else s += "/>";

  return s;
};

/*
XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy
of XmlElements.
*/

function XmlDocument(xml) {
  xml && (xml = xml.toString().trim());

  if (!xml)
    throw new Error("No XML to parse!");

  // Stores doctype (if defined)
  this.doctype = "";

  // Expose the parser to the other delegates while the parser is running
  this.parser = sax.parser(true); // strict
  addParserEvents(this.parser);

  // We'll use the file-scoped "delegates" var to remember what elements we're currently
  // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy.
  // It's safe to use a global because JS is single-threaded.
  delegates = [this];

  this.parser.write(xml);

  // Remove the parser as it is no longer needed and should not be exposed to clients
  delete this.parser;
}

// make XmlDocument inherit XmlElement's methods
extend(XmlDocument.prototype, XmlElement.prototype);

XmlDocument.prototype._opentag = function(tag) {
  if (typeof this.children === 'undefined')
    // the first tag we encounter should be the root - we'll "become" the root XmlElement
    XmlElement.call(this,tag);
  else
    // all other tags will be the root element's children
    XmlElement.prototype._opentag.apply(this,arguments);
};

XmlDocument.prototype._doctype = function(doctype) {
  this.doctype += doctype;
}

// file-scoped global stack of delegates
var delegates = null;

/*
Helper functions
*/

function addParserEvents(parser) {
  parser.onopentag = parser_opentag;
  parser.onclosetag = parser_closetag;
  parser.ontext = parser_text;
  parser.oncdata = parser_cdata;
  parser.oncomment = parser_comment;
  parser.ondoctype = parser_doctype;
  parser.onerror = parser_error;
}

// create these closures and cache them by keeping them file-scoped
function parser_opentag() { delegates[0] && delegates[0]._opentag.apply(delegates[0],arguments) }
function parser_closetag() { delegates[0] && delegates[0]._closetag.apply(delegates[0],arguments) }
function parser_text() { delegates[0] && delegates[0]._text.apply(delegates[0],arguments) }
function parser_cdata() { delegates[0] && delegates[0]._cdata.apply(delegates[0],arguments) }
function parser_comment() { delegates[0] && delegates[0]._comment.apply(delegates[0],arguments) }
function parser_doctype() { delegates[0] && delegates[0]._doctype.apply(delegates[0],arguments) }
function parser_error() { delegates[0] && delegates[0]._error.apply(delegates[0],arguments) }

// a relatively standard extend method
function extend(destination, source) {
  for (var prop in source)
    if (source.hasOwnProperty(prop))
      destination[prop] = source[prop];
}

// escapes XML entities like "<", "&", etc.
function escapeXML(value){
  return value.replace(/&/g, '&').replace(/</g, "<").replace(/>/g, ">").replace(/'/g, ''').replace(/"/g, '"');
}

// Are we being used in a Node-like environment?
if (typeof module !== 'undefined' && module.exports && !GLOBAL.xmldocAssumeBrowser)
    module.exports.XmlDocument = XmlDocument;
else
    this.XmlDocument = XmlDocument;

})();
<commit_msg>change 'GLOBAL' -> 'global'<commit_after>(function () {

var sax;

if (typeof module !== 'undefined' && module.exports && !global.xmldocAssumeBrowser) {
  // We're being used in a Node-like environment
  sax = require('sax');
}
else {
  // assume it's attached to the Window object in a browser
  sax = this.sax;

  if (!sax) // no sax for you!
    throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file.");
}

/*
XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument
behaves like an XmlElement by inheriting its attributes and functions.
*/

function XmlElement(tag) {
  // Capture the parser object off of the XmlDocument delegate
  var parser = delegates[delegates.length - 1].parser;

  this.name = tag.name;
  this.attr = tag.attributes;
  this.val = "";
  this.isValCdata = false;
  this.isValComment = false;
  this.children = [];
  this.firstChild = null;
  this.lastChild = null;

  // Assign parse information
  this.line = parser.line;
  this.column = parser.column;
  this.position = parser.position;
  this.startTagPosition = parser.startTagPosition;
}

// SaxParser handlers

XmlElement.prototype._opentag = function(tag) {

  var child = new XmlElement(tag);

  // add to our children array
  this.children.push(child);

  // update first/last pointers
  if (!this.firstChild) this.firstChild = child;
  this.lastChild = child;

  delegates.unshift(child);
};

XmlElement.prototype._closetag = function() {
  delegates.shift();
};

XmlElement.prototype._text = function(text) {
  this.val = text;
};

XmlElement.prototype._cdata = function(cdata) {
  this.val = cdata;
  this.isValCdata=true;
};

XmlElement.prototype._comment = function(comment) {
  this.val = comment;
  this.isValComment=true;
}

XmlElement.prototype._error = function(err) {
  throw err;
};

// Useful functions

XmlElement.prototype.eachChild = function(iterator, context) {
  for (var i=0, l=this.children.length; i<l; i++)
    if (iterator.call(context, this.children[i], i, this.children) === false) return;
};

XmlElement.prototype.childNamed = function(name) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if (child.name === name) return child;
  }
  return undefined;
};

XmlElement.prototype.childrenNamed = function(name) {
  var matches = [];

  for (var i=0, l=this.children.length; i<l; i++)
    if (this.children[i].name === name)
      matches.push(this.children[i]);

  return matches;
};

XmlElement.prototype.childWithAttribute = function(name,value) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if ( (value && child.attr[name] === value) || (!value && child.attr[name]) )
      return child;
  }
  return undefined;
};

XmlElement.prototype.descendantWithPath = function(path) {
  var descendant = this;
  var components = path.split('.');

  for (var i=0, l=components.length; i<l; i++)
    if (descendant)
      descendant = descendant.childNamed(components[i]);
    else
      return undefined;

  return descendant;
};

XmlElement.prototype.valueWithPath = function(path) {
  var components = path.split('@');
  var descendant = this.descendantWithPath(components[0]);
  if (descendant)
    return components.length > 1 ? descendant.attr[components[1]] : descendant.val;
  else
    return undefined;
};

// String formatting (for debugging)

XmlElement.prototype.toString = function(options) {
  return this.toStringWithIndent("", options);
};

XmlElement.prototype.toStringWithIndent = function(indent, options) {
  var s = indent + "<" + this.name;
  var linebreak = options && options.compressed ? "" : "\n";
  var preserveWhitespace = options && options.preserveWhitespace;

  for (var name in this.attr)
    if (Object.prototype.hasOwnProperty.call(this.attr, name))
        s += " " + name + '="' + escapeXML(this.attr[name]) + '"';

  var finalVal = '';
  if (this.isValCdata){
    finalVal = '<![CDATA['+this.val+']]>';
  } else if (preserveWhitespace) {
    finalVal = escapeXML(this.val);
  } else{
    finalVal = escapeXML(this.val.trim());
  }
  if (options && options.trimmed && finalVal.length > 25)
    finalVal = finalVal.substring(0,25).trim() + "…";

  if (this.children.length) {
    s += ">" + linebreak;

    var childIndent = indent + (options && options.compressed ? "" : "  ");

    if (finalVal.length)
      s += childIndent + finalVal + linebreak;

    for (var i=0, l=this.children.length; i<l; i++)
      s += this.children[i].toStringWithIndent(childIndent, options) + linebreak;

    s += indent + "</" + this.name + ">";
  }
  else if (finalVal.length) {
    s += ">" + finalVal + "</" + this.name +">";
  }
  else s += "/>";

  return s;
};

/*
XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy
of XmlElements.
*/

function XmlDocument(xml) {
  xml && (xml = xml.toString().trim());

  if (!xml)
    throw new Error("No XML to parse!");

  // Stores doctype (if defined)
  this.doctype = "";

  // Expose the parser to the other delegates while the parser is running
  this.parser = sax.parser(true); // strict
  addParserEvents(this.parser);

  // We'll use the file-scoped "delegates" var to remember what elements we're currently
  // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy.
  // It's safe to use a global because JS is single-threaded.
  delegates = [this];

  this.parser.write(xml);

  // Remove the parser as it is no longer needed and should not be exposed to clients
  delete this.parser;
}

// make XmlDocument inherit XmlElement's methods
extend(XmlDocument.prototype, XmlElement.prototype);

XmlDocument.prototype._opentag = function(tag) {
  if (typeof this.children === 'undefined')
    // the first tag we encounter should be the root - we'll "become" the root XmlElement
    XmlElement.call(this,tag);
  else
    // all other tags will be the root element's children
    XmlElement.prototype._opentag.apply(this,arguments);
};

XmlDocument.prototype._doctype = function(doctype) {
  this.doctype += doctype;
}

// file-scoped global stack of delegates
var delegates = null;

/*
Helper functions
*/

function addParserEvents(parser) {
  parser.onopentag = parser_opentag;
  parser.onclosetag = parser_closetag;
  parser.ontext = parser_text;
  parser.oncdata = parser_cdata;
  parser.oncomment = parser_comment;
  parser.ondoctype = parser_doctype;
  parser.onerror = parser_error;
}

// create these closures and cache them by keeping them file-scoped
function parser_opentag() { delegates[0] && delegates[0]._opentag.apply(delegates[0],arguments) }
function parser_closetag() { delegates[0] && delegates[0]._closetag.apply(delegates[0],arguments) }
function parser_text() { delegates[0] && delegates[0]._text.apply(delegates[0],arguments) }
function parser_cdata() { delegates[0] && delegates[0]._cdata.apply(delegates[0],arguments) }
function parser_comment() { delegates[0] && delegates[0]._comment.apply(delegates[0],arguments) }
function parser_doctype() { delegates[0] && delegates[0]._doctype.apply(delegates[0],arguments) }
function parser_error() { delegates[0] && delegates[0]._error.apply(delegates[0],arguments) }

// a relatively standard extend method
function extend(destination, source) {
  for (var prop in source)
    if (source.hasOwnProperty(prop))
      destination[prop] = source[prop];
}

// escapes XML entities like "<", "&", etc.
function escapeXML(value){
  return value.replace(/&/g, '&').replace(/</g, "<").replace(/>/g, ">").replace(/'/g, ''').replace(/"/g, '"');
}

// Are we being used in a Node-like environment?
if (typeof module !== 'undefined' && module.exports && !global.xmldocAssumeBrowser)
    module.exports.XmlDocument = XmlDocument;
else
    this.XmlDocument = XmlDocument;

})();
<|endoftext|>


<commit_before>(function () {

var sax;

if (typeof module !== 'undefined' && module.exports) {
  // We're being used in a Node-like environment
  sax = require('sax');
}
else {
  // assume it's attached to the Window object in a browser
  sax = this.sax;

  if (!sax) // no sax for you!
    throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file.");
}

/*
XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument
behaves like an XmlElement by inheriting its attributes and functions.
*/

function XmlElement(tag) {
  // Capture the parser object off of the XmlDocument delegate
  var parser = delegates[delegates.length - 1].parser;

  this.name = tag.name;
  this.attr = tag.attributes || {};
  this.val = "";
  this.children = [];
  this.firstChild = null;
  this.lastChild = null;

  // Assign parse information
  this.line = parser.line;
  this.column = parser.column;
  this.position = parser.position;
  this.startTagPosition = parser.startTagPosition;
}

// SaxParser handlers

XmlElement.prototype._opentag = function(tag) {

  var child = new XmlElement(tag);

  // add to our children array
  this.children.push(child);

  // update first/last pointers
  if (!this.firstChild) this.firstChild = child;
  this.lastChild = child;

  delegates.unshift(child);
};

XmlElement.prototype._closetag = function() {
  delegates.shift();
};

XmlElement.prototype._text = function(text) {
  if (text) this.val += text;
};

XmlElement.prototype._cdata = function(cdata) {
  if (cdata) this.val += cdata;
};

XmlElement.prototype._error = function(err) {
  throw err;
};

// Useful functions

XmlElement.prototype.eachChild = function(iterator, context) {
  for (var i=0, l=this.children.length; i<l; i++)
    if (iterator.call(context, this.children[i], i, this.children) === false) return;
};

XmlElement.prototype.childNamed = function(name) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if (child.name === name) return child;
  }
  return undefined;
};

XmlElement.prototype.childrenNamed = function(name) {
  var matches = [];

  for (var i=0, l=this.children.length; i<l; i++)
    if (this.children[i].name === name)
      matches.push(this.children[i]);

  return matches;
};

XmlElement.prototype.childWithAttribute = function(name,value) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if ( (value && child.attr[name] === value) || (!value && child.attr[name]) )
      return child;
  }
  return undefined;
};

XmlElement.prototype.descendantWithPath = function(path) {
  var descendant = this;
  var components = path.split('.');

  for (var i=0, l=components.length; i<l; i++)
    if (descendant)
      descendant = descendant.childNamed(components[i]);
    else
      return undefined;

  return descendant;
};

XmlElement.prototype.valueWithPath = function(path) {
  var components = path.split('@');
  var descendant = this.descendantWithPath(components[0]);
  if (descendant)
    return components.length > 1 ? descendant.attr[components[1]] : descendant.val;
  else
    return undefined;
};

// String formatting (for debugging)

XmlElement.prototype.toString = function(options) {
  return this.toStringWithIndent("", options);
};

function escapeValue(str, isAttribute) {
  str = str.replace(/&/g, '&').replace(/</g, "<").replace(/>/g, ">");
  if (isAttribute) {
    str = str.replace(/"/g, '"');
  }
  return str;
}

XmlElement.prototype.toStringWithIndent = function(indent, options) {
  var s = indent + "<" + this.name;
  var linebreak = options && options.compressed ? "" : "\n";

  for (var name in this.attr) {
    if (Object.prototype.hasOwnProperty.call(this.attr, name)) {
      s += " " + name + '="' + escapeValue(this.attr[name]) + '"';
    }
  }

  var finalVal = this.val.trim();
  if (finalVal.indexOf('<') !== -1 || finalVal.indexOf('>') !== -1 || finalVal.indexOf('&') !== -1) {
    if (options && options.cdata) {
      finalVal = "<![CDATA[" + finalVal + "]]>";
    } else {
      finalVal = escapeValue(finalVal);
    }
  }

  if (options && options.trimmed && finalVal.length > 25)
    finalVal = finalVal.substring(0,25).trim() + "…";

  if (this.children.length) {
    s += ">" + linebreak;

    var childIndent = indent + (options && options.compressed ? "" : "  ");

    if (finalVal.length)
      s += childIndent + finalVal + linebreak;

    for (var i=0, l=this.children.length; i<l; i++)
      s += this.children[i].toStringWithIndent(childIndent, options) + linebreak;

    s += indent + "</" + this.name + ">";
  }
  else if (finalVal.length) {
    s += ">" + finalVal + "</" + this.name +">";
  }
  else s += "/>";

  return s;
};

/*
XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy
of XmlElements.
*/

function XmlDocument(xml) {
  xml && (xml = xml.toString().trim());

  if (!xml)
    throw new Error("No XML to parse!");

  // Expose the parser to the other delegates while the parser is running
  this.parser = sax.parser(true); // strict
  addParserEvents(this.parser);

  // We'll use the file-scoped "delegates" var to remember what elements we're currently
  // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy.
  // It's safe to use a global because JS is single-threaded.
  delegates = [this];

  this.parser.write(xml);

  // Remove the parser as it is no longer needed and should not be exposed to clients
  delete this.parser;
}

// make XmlDocument inherit XmlElement's methods
extend(XmlDocument.prototype, XmlElement.prototype);

XmlDocument.prototype._opentag = function(tag) {
  if (typeof this.children === 'undefined')
    // the first tag we encounter should be the root - we'll "become" the root XmlElement
    XmlElement.call(this,tag);
  else
    // all other tags will be the root element's children
    XmlElement.prototype._opentag.apply(this,arguments);
};

// file-scoped global stack of delegates
var delegates = null;

/*
Helper functions
*/

function addParserEvents(parser) {
  parser.onopentag = parser_opentag;
  parser.onclosetag = parser_closetag;
  parser.ontext = parser_text;
  parser.oncdata = parser_cdata;
  parser.onerror = parser_error;
}

// create these closures and cache them by keeping them file-scoped
function parser_opentag() { delegates[0]._opentag.apply(delegates[0],arguments) }
function parser_closetag() { delegates[0]._closetag.apply(delegates[0],arguments) }
function parser_text() { delegates[0]._text.apply(delegates[0],arguments) }
function parser_cdata() { delegates[0]._cdata.apply(delegates[0],arguments) }
function parser_error() { delegates[0]._error.apply(delegates[0],arguments) }

// a relatively standard extend method
function extend(destination, source) {
  for (var prop in source)
    if (source.hasOwnProperty(prop))
      destination[prop] = source[prop];
}

// Are we being used in a Node-like environment?
if (typeof module !== 'undefined' && module.exports)
    module.exports.XmlDocument = XmlDocument;
else
    this.XmlDocument = XmlDocument;

})();
<commit_msg>forgot attr escaping<commit_after>(function () {

var sax;

if (typeof module !== 'undefined' && module.exports) {
  // We're being used in a Node-like environment
  sax = require('sax');
}
else {
  // assume it's attached to the Window object in a browser
  sax = this.sax;

  if (!sax) // no sax for you!
    throw new Error("Expected sax to be defined. Make sure you're including sax.js before this file.");
}

/*
XmlElement is our basic building block. Everything is an XmlElement; even XmlDocument
behaves like an XmlElement by inheriting its attributes and functions.
*/

function XmlElement(tag) {
  // Capture the parser object off of the XmlDocument delegate
  var parser = delegates[delegates.length - 1].parser;

  this.name = tag.name;
  this.attr = tag.attributes || {};
  this.val = "";
  this.children = [];
  this.firstChild = null;
  this.lastChild = null;

  // Assign parse information
  this.line = parser.line;
  this.column = parser.column;
  this.position = parser.position;
  this.startTagPosition = parser.startTagPosition;
}

// SaxParser handlers

XmlElement.prototype._opentag = function(tag) {

  var child = new XmlElement(tag);

  // add to our children array
  this.children.push(child);

  // update first/last pointers
  if (!this.firstChild) this.firstChild = child;
  this.lastChild = child;

  delegates.unshift(child);
};

XmlElement.prototype._closetag = function() {
  delegates.shift();
};

XmlElement.prototype._text = function(text) {
  if (text) this.val += text;
};

XmlElement.prototype._cdata = function(cdata) {
  if (cdata) this.val += cdata;
};

XmlElement.prototype._error = function(err) {
  throw err;
};

// Useful functions

XmlElement.prototype.eachChild = function(iterator, context) {
  for (var i=0, l=this.children.length; i<l; i++)
    if (iterator.call(context, this.children[i], i, this.children) === false) return;
};

XmlElement.prototype.childNamed = function(name) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if (child.name === name) return child;
  }
  return undefined;
};

XmlElement.prototype.childrenNamed = function(name) {
  var matches = [];

  for (var i=0, l=this.children.length; i<l; i++)
    if (this.children[i].name === name)
      matches.push(this.children[i]);

  return matches;
};

XmlElement.prototype.childWithAttribute = function(name,value) {
  for (var i=0, l=this.children.length; i<l; i++) {
    var child = this.children[i];
    if ( (value && child.attr[name] === value) || (!value && child.attr[name]) )
      return child;
  }
  return undefined;
};

XmlElement.prototype.descendantWithPath = function(path) {
  var descendant = this;
  var components = path.split('.');

  for (var i=0, l=components.length; i<l; i++)
    if (descendant)
      descendant = descendant.childNamed(components[i]);
    else
      return undefined;

  return descendant;
};

XmlElement.prototype.valueWithPath = function(path) {
  var components = path.split('@');
  var descendant = this.descendantWithPath(components[0]);
  if (descendant)
    return components.length > 1 ? descendant.attr[components[1]] : descendant.val;
  else
    return undefined;
};

// String formatting (for debugging)

XmlElement.prototype.toString = function(options) {
  return this.toStringWithIndent("", options);
};

function escapeValue(str, isAttribute) {
  // The order of replacement is important - first process the &, than all other
  str = str.replace(/&/g, '&').replace(/</g, "<").replace(/>/g, ">");
  if (isAttribute) {
    str = str.replace(/"/g, '"');
  }
  return str;
}

XmlElement.prototype.toStringWithIndent = function(indent, options) {
  var s = indent + "<" + this.name;
  var linebreak = options && options.compressed ? "" : "\n";

  for (var name in this.attr) {
    if (Object.prototype.hasOwnProperty.call(this.attr, name)) {
      s += " " + name + '="' + escapeValue(this.attr[name], true) + '"';
    }
  }

  var finalVal = this.val.trim();
  if (finalVal.indexOf('<') !== -1 || finalVal.indexOf('>') !== -1 || finalVal.indexOf('&') !== -1) {
    if (options && options.cdata) {
      finalVal = "<![CDATA[" + finalVal + "]]>";
    } else {
      finalVal = escapeValue(finalVal);
    }
  }

  if (options && options.trimmed && finalVal.length > 25)
    finalVal = finalVal.substring(0,25).trim() + "…";

  if (this.children.length) {
    s += ">" + linebreak;

    var childIndent = indent + (options && options.compressed ? "" : "  ");

    if (finalVal.length)
      s += childIndent + finalVal + linebreak;

    for (var i=0, l=this.children.length; i<l; i++)
      s += this.children[i].toStringWithIndent(childIndent, options) + linebreak;

    s += indent + "</" + this.name + ">";
  }
  else if (finalVal.length) {
    s += ">" + finalVal + "</" + this.name +">";
  }
  else s += "/>";

  return s;
};

/*
XmlDocument is the class we expose to the user; it uses the sax parser to create a hierarchy
of XmlElements.
*/

function XmlDocument(xml) {
  xml && (xml = xml.toString().trim());

  if (!xml)
    throw new Error("No XML to parse!");

  // Expose the parser to the other delegates while the parser is running
  this.parser = sax.parser(true); // strict
  addParserEvents(this.parser);

  // We'll use the file-scoped "delegates" var to remember what elements we're currently
  // parsing; they will push and pop off the stack as we get deeper into the XML hierarchy.
  // It's safe to use a global because JS is single-threaded.
  delegates = [this];

  this.parser.write(xml);

  // Remove the parser as it is no longer needed and should not be exposed to clients
  delete this.parser;
}

// make XmlDocument inherit XmlElement's methods
extend(XmlDocument.prototype, XmlElement.prototype);

XmlDocument.prototype._opentag = function(tag) {
  if (typeof this.children === 'undefined')
    // the first tag we encounter should be the root - we'll "become" the root XmlElement
    XmlElement.call(this,tag);
  else
    // all other tags will be the root element's children
    XmlElement.prototype._opentag.apply(this,arguments);
};

// file-scoped global stack of delegates
var delegates = null;

/*
Helper functions
*/

function addParserEvents(parser) {
  parser.onopentag = parser_opentag;
  parser.onclosetag = parser_closetag;
  parser.ontext = parser_text;
  parser.oncdata = parser_cdata;
  parser.onerror = parser_error;
}

// create these closures and cache them by keeping them file-scoped
function parser_opentag() { delegates[0]._opentag.apply(delegates[0],arguments) }
function parser_closetag() { delegates[0]._closetag.apply(delegates[0],arguments) }
function parser_text() { delegates[0]._text.apply(delegates[0],arguments) }
function parser_cdata() { delegates[0]._cdata.apply(delegates[0],arguments) }
function parser_error() { delegates[0]._error.apply(delegates[0],arguments) }

// a relatively standard extend method
function extend(destination, source) {
  for (var prop in source)
    if (source.hasOwnProperty(prop))
      destination[prop] = source[prop];
}

// Are we being used in a Node-like environment?
if (typeof module !== 'undefined' && module.exports)
    module.exports.XmlDocument = XmlDocument;
else
    this.XmlDocument = XmlDocument;

})();
<|endoftext|>