<commit_before>// Node.js API.
var http = require('http')

// Control-flow utilities.
var cadence = require('cadence')
var delta = require('delta')

// Route Sencha Connect middleware based on request method and URL patterns.
var dispatch = require('dispatch')

// Exceptions that you can catch by type.
var interrupt = require('interrupt').createInterrupter('reactor')

// Contextualized callbacks and event handlers.
var Operation = require('operation/variadic')

// Evented work queue.
var Turnstile = require('turnstile')
Turnstile.Queue = require('turnstile/queue')

// Catch exceptions based on a regex match of an error message or property.
var rescue = require('rescue')

// Return the first not null-like value.
var coalesce = require('extant')

// Do nothing.
var nop = require('nop')

function Constructor (object, dispatch) {
    this._object = object
    this._dispatch = dispatch
    this._use = []
    this._defaultUse = true
}

Constructor.prototype.useDefault = function () {
    this.use(require('express-auth-parser'))
    this.use(require('body-parser').urlencoded({ extended: false, limit: '64mb' }))
    this.use(require('body-parser').json({ limit: '64mb' }))
}

Constructor.prototype.use = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._defaultUse = false
    while (vargs.length) {
        var varg = vargs.shift()
        if (Array.isArray(varg)) {
            vargs.unshift(varg)
        } else {
            this._use.push(varg)
        }
    }
}

Constructor.prototype.dispatch = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._dispatch[vargs.shift()] = Operation(vargs, { object: this._object })
}

function handler (queue, before, operation) {
    return function (request, response, next) {
        var vargs = Array.prototype.slice.call(arguments, 3)
        request.entry = {
            when: {
                push: Date.now(),
                start: null,
                headers: null,
                finish: null
            },
            health: {
                push: JSON.parse(JSON.stringify(queue.turnstile.health)),
                start: null,
                finish: null
            },
            duration: {
                start: null,
                headers: null,
                finish: null,
            },
            request: {
                method: request.method,
                header: request.headers,
                url: request.url,
                vargs: vargs
            }
        }
        before(request, response, function (error) {
            queue.push({
                error: coalesce(error),
                operation: operation,
                request: request,
                response: response,
                vargs: vargs
            })
        })
    }
}

function Reactor (object, configurator) {
    var constructor = new Constructor(object, this._dispatch = {})
    configurator(constructor)
    this.turnstile = new Turnstile({
        Date: coalesce(constructor.Date, Date),
        turnstiles: coalesce(constructor.turnstiles, 24),
        timeout: coalesce(constructor.timeout)
    })
    this._queue = new Turnstile.Queue(this, '_respond', this.turnstile)
    this._logger = coalesce(constructor.logger, function (entry) {
        if (entry.error) {
            console.log(entry.error.stack)
        }
    })
    this._completed = coalesce(constructor.completed, nop)
    this._object = object
    if (constructor._defaultUse) {
        constructor.useDefault()
    }
    var before = require('connect')()
    constructor._use.forEach(function (middleware) {
        before.use(middleware)
    })
    var dispatcher = {}
    for (var pattern in this._dispatch) {
        dispatcher[pattern] = handler(this._queue, before, this._dispatch[pattern])
    }
    this.middleware = require('connect')().use(dispatch(dispatcher))
}

Reactor.prototype._timeout = cadence(function () { throw 503 })

function createProperties (properties) {
    return {
        statusCode: properties.statusCode,
        headers: coalesce(properties.headers, {}),
        description: coalesce(properties.description)
    }
}

function raise (statusCode, description, headers) {
    throw interrupt('http', createProperties({
        statusCode: statusCode,
        description: description,
        headers: headers
    }))
}

Reactor.prototype._respond = cadence(function (async, envelope) {
    var work = envelope.body
    var next = work.next

    var entry = work.request.entry

    entry.when.start = Date.now()
    entry.health.start = JSON.parse(JSON.stringify(this.turnstile.health))

    work.request.entry = entry
    work.request.raise = raise

    if (envelope.timedout) {
        work.operation = Operation([ this, '_timeout' ])
    }

    var finish

    async(function () {
        async(function () {
            async([function () {
                async(function () {
                    if (work.error) {
                        throw { cause: work.error, statusCode: coalesce(work.error.statusCode) }
                    }
                    work.operation.apply(null, [ work.request ].concat(work.vargs, async()))
                }, function () {
                    var vargs = Array.prototype.slice.call(arguments)

                    var result = vargs.shift()
                    var statusCode = (typeof vargs[0] == 'number') ? vargs.shift() : 200
                    var description = coalesce(http.STATUS_CODES[statusCode], 'unknown')
                    if (typeof vargs[0] == 'string') {
                        description = vargs.shift()
                    }
                    if (vargs[0] == null) {
                        vargs.shift()
                    }
                    var headers = coalesce(vargs.shift(), {})

                    return [ result, statusCode, description, headers ]
                })
            }, function (caught) {
                for (;;) {
                    try {
                        return rescue(/^reactor#http$/m, function (error) {
                            var statusCode = error.statusCode
                            var description = coalesce(error.description, http.STATUS_CODES[statusCode])
                            var body = coalesce(error.body, description)
                            var headers = coalesce(error.headers, {})

                            entry.error = coalesce(error.cause)

                            interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                            headers['content-type'] = 'application/json'

                            return [ description, statusCode, description, headers ]
                        })(caught)
                    } catch (error) {
                        if (
                            typeof error == 'number' &&
                            !isNaN(error) &&
                            (error | 0) === error &&
                            Math.floor(error / 100) <= 5 &&
                            Math.floor(error / 100) >= 3
                        ) {
                            error = { statusCode: error }
                        } else if (typeof error == 'string') {
                            error = { statusCode: 307, location: error }
                        }
                        if (Array.isArray(error)) {
                            error = error.slice()
                            var statusCode = typeof error[0] == 'number' ? error.shift() : 503
                            var body = error.shift() || null
                            var description = coalesce(http.STATUS_CODES[statusCode])
                            if (typeof vargs[0] == 'string') {
                                description = error.shift()
                            }
                            if (body == null) {
                                body = description
                            }
                            error = {
                                statusCode: statusCode,
                                description: description,
                                body: needBody ? description : body,
                                headers: coalesce(error.shift(), {})
                            }
                        }
                        if (
                            ! (error instanceof Error) &&
                            typeof error == 'object' &&
                            typeof error.statusCode == 'number' &&
                            !isNaN(error.statusCode) &&
                            (error.statusCode | 0) === error.statusCode &&
                            Math.floor(error.statusCode / 100) <= 5 &&
                            Math.floor(error.statusCode / 100) >= 3
                        ) {
                            var properties = createProperties(error)
                            if (error.location) {
                                properties.headers.location = error.location
                            }
                            error = interrupt('http', properties, { cause: coalesce(error.cause) })
                        } else {
                            error = { statusCode: 500, cause: error }
                        }
                        caught = error
                    }
                }
            }])
        }, function (result, statusCode, description, headers) {
            var body, f
            if (typeof result == 'function') {
                f = result
            } else {
                if (typeof result == 'string') {
                    if (!('content-type' in headers)) {
                        headers['content-type'] = 'text/plain'
                        result = new Buffer(result)
                    } else if (headers['content-type'] == 'application/json') {
                        result = new Buffer(JSON.stringify(result) + '\n')
                    }
                } else if (!('content-type' in headers)) {
                    headers['content-type'] = 'application/json'
                    result = new Buffer(JSON.stringify(result) + '\n')
                }
                f = function (response) {
                    response.end(result)
                }
            }

            work.response.writeHead(statusCode, description, headers)

            entry.when.headers = Date.now()

            entry.statusCode = statusCode
            entry.description = description
            entry.headers = headers

            var finish = delta(async()).ee(work.response).on('finish')

            async([function () {
                async(function () {
                    if (f.length == 2) {
                        f.call(work.operation.object, work.response, async())
                    } else {
                        return [ f.call(work.operation.object, work.response) ]
                    }
                }, function () {
                    return []
                })
            }, function (error) {
                entry.error = error
                work.response.end()
                finish.cancel()
            }])
        })
    }, function () {
        entry.health.done = JSON.parse(JSON.stringify(this.turnstile.health))
        entry.when.finish = Date.now()
        entry.duration = {
            start: entry.when.start - entry.when.push,
            headers: entry.when.headers - entry.when.push,
            finish: entry.when.finish - entry.when.push
        }
        this._logger.call(null, entry)
        this._completed.call(null, entry)
    })
})

Reactor.resend = function (statusCode, headers, body) {
    return Reactor.send({ statusCode: statusCode, headers: headers }, body)
}

Reactor.send = function (properties, buffer) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    delete headers['transfer-encoding']
    return [ buffer, properties.statusCode, properties.statusMessage, headers ]
}

Reactor.string = function (statusCode, contentType, text) {
    return [ new Buffer(text), statusCode, { 'content-type': contentType } ]
}

Reactor.stream = function (properties, stream) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    headers['transfer-encoding'] = 'chunked'
    return [ function (response) {
        stream.pipe(response)
    }, properties.statusCode, properties.statusMessage, headers ]
}

module.exports = Reactor
<commit_msg>Rename varaible to `noop`.<commit_after>// Node.js API.
var http = require('http')

// Control-flow utilities.
var cadence = require('cadence')
var delta = require('delta')

// Route Sencha Connect middleware based on request method and URL patterns.
var dispatch = require('dispatch')

// Exceptions that you can catch by type.
var interrupt = require('interrupt').createInterrupter('reactor')

// Contextualized callbacks and event handlers.
var Operation = require('operation/variadic')

// Evented work queue.
var Turnstile = require('turnstile')
Turnstile.Queue = require('turnstile/queue')

// Catch exceptions based on a regex match of an error message or property.
var rescue = require('rescue')

// Return the first not null-like value.
var coalesce = require('extant')

// Do nothing.
var noop = require('nop')

function Constructor (object, dispatch) {
    this._object = object
    this._dispatch = dispatch
    this._use = []
    this._defaultUse = true
}

Constructor.prototype.useDefault = function () {
    this.use(require('express-auth-parser'))
    this.use(require('body-parser').urlencoded({ extended: false, limit: '64mb' }))
    this.use(require('body-parser').json({ limit: '64mb' }))
}

Constructor.prototype.use = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._defaultUse = false
    while (vargs.length) {
        var varg = vargs.shift()
        if (Array.isArray(varg)) {
            vargs.unshift(varg)
        } else {
            this._use.push(varg)
        }
    }
}

Constructor.prototype.dispatch = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._dispatch[vargs.shift()] = Operation(vargs, { object: this._object })
}

function handler (queue, before, operation) {
    return function (request, response, next) {
        var vargs = Array.prototype.slice.call(arguments, 3)
        request.entry = {
            when: {
                push: Date.now(),
                start: null,
                headers: null,
                finish: null
            },
            health: {
                push: JSON.parse(JSON.stringify(queue.turnstile.health)),
                start: null,
                finish: null
            },
            duration: {
                start: null,
                headers: null,
                finish: null,
            },
            request: {
                method: request.method,
                header: request.headers,
                url: request.url,
                vargs: vargs
            }
        }
        before(request, response, function (error) {
            queue.push({
                error: coalesce(error),
                operation: operation,
                request: request,
                response: response,
                vargs: vargs
            })
        })
    }
}

function Reactor (object, configurator) {
    var constructor = new Constructor(object, this._dispatch = {})
    configurator(constructor)
    this.turnstile = new Turnstile({
        Date: coalesce(constructor.Date, Date),
        turnstiles: coalesce(constructor.turnstiles, 24),
        timeout: coalesce(constructor.timeout)
    })
    this._queue = new Turnstile.Queue(this, '_respond', this.turnstile)
    this._logger = coalesce(constructor.logger, function (entry) {
        if (entry.error) {
            console.log(entry.error.stack)
        }
    })
    this._completed = coalesce(constructor.completed, noop)
    this._object = object
    if (constructor._defaultUse) {
        constructor.useDefault()
    }
    var before = require('connect')()
    constructor._use.forEach(function (middleware) {
        before.use(middleware)
    })
    var dispatcher = {}
    for (var pattern in this._dispatch) {
        dispatcher[pattern] = handler(this._queue, before, this._dispatch[pattern])
    }
    this.middleware = require('connect')().use(dispatch(dispatcher))
}

Reactor.prototype._timeout = cadence(function () { throw 503 })

function createProperties (properties) {
    return {
        statusCode: properties.statusCode,
        headers: coalesce(properties.headers, {}),
        description: coalesce(properties.description)
    }
}

function raise (statusCode, description, headers) {
    throw interrupt('http', createProperties({
        statusCode: statusCode,
        description: description,
        headers: headers
    }))
}

Reactor.prototype._respond = cadence(function (async, envelope) {
    var work = envelope.body
    var next = work.next

    var entry = work.request.entry

    entry.when.start = Date.now()
    entry.health.start = JSON.parse(JSON.stringify(this.turnstile.health))

    work.request.entry = entry
    work.request.raise = raise

    if (envelope.timedout) {
        work.operation = Operation([ this, '_timeout' ])
    }

    var finish

    async(function () {
        async(function () {
            async([function () {
                async(function () {
                    if (work.error) {
                        throw { cause: work.error, statusCode: coalesce(work.error.statusCode) }
                    }
                    work.operation.apply(null, [ work.request ].concat(work.vargs, async()))
                }, function () {
                    var vargs = Array.prototype.slice.call(arguments)

                    var result = vargs.shift()
                    var statusCode = (typeof vargs[0] == 'number') ? vargs.shift() : 200
                    var description = coalesce(http.STATUS_CODES[statusCode], 'unknown')
                    if (typeof vargs[0] == 'string') {
                        description = vargs.shift()
                    }
                    if (vargs[0] == null) {
                        vargs.shift()
                    }
                    var headers = coalesce(vargs.shift(), {})

                    return [ result, statusCode, description, headers ]
                })
            }, function (caught) {
                for (;;) {
                    try {
                        return rescue(/^reactor#http$/m, function (error) {
                            var statusCode = error.statusCode
                            var description = coalesce(error.description, http.STATUS_CODES[statusCode])
                            var body = coalesce(error.body, description)
                            var headers = coalesce(error.headers, {})

                            entry.error = coalesce(error.cause)

                            interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                            headers['content-type'] = 'application/json'

                            return [ description, statusCode, description, headers ]
                        })(caught)
                    } catch (error) {
                        if (
                            typeof error == 'number' &&
                            !isNaN(error) &&
                            (error | 0) === error &&
                            Math.floor(error / 100) <= 5 &&
                            Math.floor(error / 100) >= 3
                        ) {
                            error = { statusCode: error }
                        } else if (typeof error == 'string') {
                            error = { statusCode: 307, location: error }
                        }
                        if (Array.isArray(error)) {
                            error = error.slice()
                            var statusCode = typeof error[0] == 'number' ? error.shift() : 503
                            var body = error.shift() || null
                            var description = coalesce(http.STATUS_CODES[statusCode])
                            if (typeof vargs[0] == 'string') {
                                description = error.shift()
                            }
                            if (body == null) {
                                body = description
                            }
                            error = {
                                statusCode: statusCode,
                                description: description,
                                body: needBody ? description : body,
                                headers: coalesce(error.shift(), {})
                            }
                        }
                        if (
                            ! (error instanceof Error) &&
                            typeof error == 'object' &&
                            typeof error.statusCode == 'number' &&
                            !isNaN(error.statusCode) &&
                            (error.statusCode | 0) === error.statusCode &&
                            Math.floor(error.statusCode / 100) <= 5 &&
                            Math.floor(error.statusCode / 100) >= 3
                        ) {
                            var properties = createProperties(error)
                            if (error.location) {
                                properties.headers.location = error.location
                            }
                            error = interrupt('http', properties, { cause: coalesce(error.cause) })
                        } else {
                            error = { statusCode: 500, cause: error }
                        }
                        caught = error
                    }
                }
            }])
        }, function (result, statusCode, description, headers) {
            var body, f
            if (typeof result == 'function') {
                f = result
            } else {
                if (typeof result == 'string') {
                    if (!('content-type' in headers)) {
                        headers['content-type'] = 'text/plain'
                        result = new Buffer(result)
                    } else if (headers['content-type'] == 'application/json') {
                        result = new Buffer(JSON.stringify(result) + '\n')
                    }
                } else if (!('content-type' in headers)) {
                    headers['content-type'] = 'application/json'
                    result = new Buffer(JSON.stringify(result) + '\n')
                }
                f = function (response) {
                    response.end(result)
                }
            }

            work.response.writeHead(statusCode, description, headers)

            entry.when.headers = Date.now()

            entry.statusCode = statusCode
            entry.description = description
            entry.headers = headers

            var finish = delta(async()).ee(work.response).on('finish')

            async([function () {
                async(function () {
                    if (f.length == 2) {
                        f.call(work.operation.object, work.response, async())
                    } else {
                        return [ f.call(work.operation.object, work.response) ]
                    }
                }, function () {
                    return []
                })
            }, function (error) {
                entry.error = error
                work.response.end()
                finish.cancel()
            }])
        })
    }, function () {
        entry.health.done = JSON.parse(JSON.stringify(this.turnstile.health))
        entry.when.finish = Date.now()
        entry.duration = {
            start: entry.when.start - entry.when.push,
            headers: entry.when.headers - entry.when.push,
            finish: entry.when.finish - entry.when.push
        }
        this._logger.call(null, entry)
        this._completed.call(null, entry)
    })
})

Reactor.resend = function (statusCode, headers, body) {
    return Reactor.send({ statusCode: statusCode, headers: headers }, body)
}

Reactor.send = function (properties, buffer) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    delete headers['transfer-encoding']
    return [ buffer, properties.statusCode, properties.statusMessage, headers ]
}

Reactor.string = function (statusCode, contentType, text) {
    return [ new Buffer(text), statusCode, { 'content-type': contentType } ]
}

Reactor.stream = function (properties, stream) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    headers['transfer-encoding'] = 'chunked'
    return [ function (response) {
        stream.pipe(response)
    }, properties.statusCode, properties.statusMessage, headers ]
}

module.exports = Reactor
<|endoftext|>


<commit_before>moduleAid.VERSION = '2.0.15';

// this module catches the popup event and tells which nodes (triggers) the slimChrome script should check for

this.holdPanelUI = function(e) {
	e.detail = 'PanelUI-button';
	e.stopPropagation();
};

this.holdNavBarOverflow = function(e) {
	e.detail = 'nav-bar-overflow-button';
	e.stopPropagation();
};

this.holdPopupAutoComplete = function(e) {
	if(isAncestor(document.commandDispatcher.focusedElement, $('searchbar'))) {
		e.detail = 'searchbar';
		e.stopPropagation();
	}
};

this.holdPopupAutoCompleteRichResult = function(e) {
	e.detail = 'urlbar';
	e.stopPropagation();
};

this.holdIdentityPopup = function(e) {
	e.detail = 'identity-box';
	e.stopPropagation();
};

this.holdNotificationPopup = function(e) {
	e.detail = 'notification-popup-box';
	e.stopPropagation();
};

this.setupHoldDownloadsPanel = function(e) {
	if(e.target.id == 'downloadsPanel') {
		listenerAid.remove(window, 'popupshowing', setupHoldDownloadsPanel);
		listenerAid.add(e.target, 'AskingForNodeOwner', holdDownloadsPanel);
	}
};

this.holdDownloadsPanel = function(e) {
	e.detail = 'downloads-button';
	e.stopPropagation();
};

this.setupHoldBookmarkPanel = function(e) {
	if(e.target.id == 'editBookmarkPanel') {
		listenerAid.remove(window, 'popupshowing', setupHoldBookmarkPanel);
		listenerAid.add(e.target, 'AskingForNodeOwner', holdBookmarkPanel);
	}
};

this.holdBookmarkPanel = function(e) {
	e.detail = 'bookmarks-menu-button';
	e.stopPropagation();
};

// Keep chrome visible when opening menus within it
this.blockPopups = ['identity-popup', 'notification-popup'];
this.blockedPopup = false;
this.holdPopupNodes = [];
this.holdPopupMenu = function(e) {
	// don't do anything on tooltips! the UI might collapse altogether
	if(!e.target || e.target.nodeName == 'window' || e.target.nodeName == 'tooltip') { return; }

	var trigger = e.originalTarget.triggerNode;

	// check if the trigger node is present in our toolbars;
	// there's no need to check the overflow panel here, as it will likely be open already in these cases
	var hold = isAncestor(trigger, slimChromeContainer);

	// try to use the anchor specified when opening the popup, if any; ditto from above for overflow panel nodes
	if(!hold && e.target.anchorNode) {
		hold = isAncestor(e.target.anchorNode, slimChromeContainer);
	}

	if(!hold && !trigger) {
		// CUI panel doesn't carry a triggerNode, we have to find it ourselves
		if(e.target.id == 'customizationui-widget-panel') {
			hold_loop: for(var t=0; t<slimChromeToolbars.childNodes.length; t++) {
				if(slimChromeToolbars.childNodes[t].localName != 'toolbar' || !CustomizableUI.getAreaType(slimChromeToolbars.childNodes[t].id)) { continue; }

				var widgets = CustomizableUI.getWidgetsInArea(slimChromeToolbars.childNodes[t].id);
				for(var w=0; w<widgets.length; w++) {
					var widget = widgets[w].forWindow(window);
					if(!widget || !widget.node || !widget.node.open) { continue; }

					hold = true;
					break hold_loop;
				}
			}
		}

		// let's just assume all panels that are children from these toolbars are opening from them
		else if(isAncestor(e.target, slimChromeContainer)) {
			hold = true;
		}
	}

	// nothing "native" is opening this popup, so let's see if someone claims it
	if(!hold) {
		trigger = askForOwner(e.target);
		if(trigger && typeof(trigger) == 'string') {
			trigger = $(trigger);
			// trigger could be either in the toolbars themselves or in the overflow panel
			hold = isAncestor(trigger, slimChromeContainer) || isAncestor(trigger, overflowList);
		}
	}

	if(hold) {
		// if we're opening the chrome now, the anchor may move, so we need to reposition the popup when it does
		holdPopupNodes.push(e.target);

		// sometimes when opening the menu panel, it will be nearly collapsed, I have no idea what is setting these values
		if(e.target.id == 'PanelUI-popup') {
			removeAttribute(e.target, 'width');
			removeAttribute(e.target, 'height');
		}

		// if opening a panel from the urlbar, we should keep the mini state, instead of expanding to full chrome
		if(trueAttribute(slimChromeContainer, 'mini') && slimChromeContainer.hovers == 0 && blockPopups.indexOf(e.target.id) > -1) {
			setMini(true);
			blockedPopup = true;
		} else {
			if(!trueAttribute(slimChromeContainer, 'fullWidth')) {
				hideIt(e.target);
				timerAid.init('ensureHoldPopupShows', popupsFinishedWidth, 200);
			}

			setHover(true, true);
		}

		var selfRemover = function(ee) {
			if(ee.originalTarget != e.originalTarget) { return; } //submenus
			listenerAid.remove(e.target, 'popuphidden', selfRemover);

			// making sure we don't collapse it permanently
			hideIt(e.target, true);

			if(typeof(setHover) != 'undefined') {
				if(trueAttribute(slimChromeContainer, 'mini') && blockPopups.indexOf(e.target.id) > -1) {
					if(blockedPopup) {
						hideMiniInABit();
						blockedPopup = false;
					}
				} else {
					setHover(false);
				}
			}

			aSync(function() {
				if(typeof(holdPopupNodes) != 'undefined' && holdPopupNodes.indexOf(e.target) > -1) {
					holdPopupNodes.splice(holdPopupNodes.indexOf(e.target), 1);
				}
			}, 150);
		}
		listenerAid.add(e.target, 'popuphidden', selfRemover);
	}
};

this.popupsWillSetMini = function(e) {
	// e.detail is if setting or unsetting mini state
	if(e.detail) { blockedPopup = false; }
};

this.popupsFinishedWidth = function() {
	timerAid.cancel('ensureHoldPopupShows');
	if(holdPopupNodes.length > 0) {
		for(var i=0; i<holdPopupNodes.length; i++) {
			// don't bother if the popup was never hidden to begin with,
			// it's not needed (the chrome was already expanded when it opened), so the popup is already properly placed,
			// also this prevents some issues, for example the context menu jumping to the top left corner
			if(!holdPopupNodes[i].collapsed) { continue; }

			// obviously we won't need to move it if it isn't open
			if(holdPopupNodes[i].open || holdPopupNodes[i].state == 'open') {
				holdPopupNodes[i].moveTo(-1,-1);
				hideIt(holdPopupNodes[i], true);
			}
		}

		// in case opening the popup triggered the chrome to show, and the mouse just so happens to be in that area, we need to make sure the mouse leaving
		// won't hide the chrome with the popup still shown
		if(slimChromeContainer.hovers === 1 && $$('#'+objName+'-slimChrome-container:hover')[0]) {
			setHover(true);
		}
	}
};

this.loadSlimChromePopups = function() {
	// if a menu or a panel is opened from the toolbox, keep it shown
	listenerAid.add(window, 'popupshown', holdPopupMenu);
	listenerAid.add(slimChromeContainer, 'willSetMiniChrome', popupsWillSetMini);
	listenerAid.add(slimChromeContainer, 'FinishedSlimChromeWidth', popupsFinishedWidth);
};

this.unloadSlimChromePopups = function() {
	timerAid.cancel('ensureHoldPopupShows');

	listenerAid.remove(window, 'popupshown', holdPopupMenu);
	listenerAid.remove(slimChromeContainer, 'willSetMiniChrome', popupsWillSetMini);
	listenerAid.remove(slimChromeContainer, 'FinishedSlimChromeWidth', popupsFinishedWidth);
};

moduleAid.LOADMODULE = function() {
	listenerAid.add(window, 'LoadedSlimChrome', loadSlimChromePopups);
	listenerAid.add(window, 'UnloadingSlimChrome', unloadSlimChromePopups);

	// in case slimChrome loads before popups
	if(typeof(slimChromeContainer) != 'undefined' && slimChromeContainer) {
		loadSlimChromePopups();
	}

	// make sure we know about all these panels so we can hold the chrome open with them
	listenerAid.add($('PanelUI-popup'), 'AskingForNodeOwner', holdPanelUI);
	listenerAid.add($('widget-overflow'), 'AskingForNodeOwner', holdNavBarOverflow);
	listenerAid.add($('PopupAutoComplete'), 'AskingForNodeOwner', holdPopupAutoComplete);
	listenerAid.add($('PopupAutoCompleteRichResult'), 'AskingForNodeOwner', holdPopupAutoCompleteRichResult);
	listenerAid.add($('identity-popup'), 'AskingForNodeOwner', holdIdentityPopup);
	listenerAid.add($('notification-popup'), 'AskingForNodeOwner', holdNotificationPopup);

	// the downloadsPanel is only created when first called
	if($('downloadsPanel')) {
		listenerAid.add($('downloadsPanel'), 'AskingForNodeOwner', holdDownloadsPanel);
	} else {
		listenerAid.add(window, 'popupshowing', setupHoldDownloadsPanel);
	}

	// ditto for the editBookmarkPanel
	if($('editBookmarkPanel')) {
		listenerAid.add($('editBookmarkPanel'), 'AskingForNodeOwner', holdBookmarkPanel);
	} else {
		listenerAid.add(window, 'popupshowing', setupHoldBookmarkPanel);
	}
};

moduleAid.UNLOADMODULE = function() {
	listenerAid.remove($('PanelUI-popup'), 'AskingForNodeOwner', holdPanelUI);
	listenerAid.remove($('widget-overflow'), 'AskingForNodeOwner', holdNavBarOverflow);
	listenerAid.remove($('PopupAutoComplete'), 'AskingForNodeOwner', holdPopupAutoComplete);
	listenerAid.remove($('PopupAutoCompleteRichResult'), 'AskingForNodeOwner', holdPopupAutoCompleteRichResult);
	listenerAid.remove($('identity-popup'), 'AskingForNodeOwner', holdIdentityPopup);
	listenerAid.remove($('notification-popup'), 'AskingForNodeOwner', holdNotificationPopup);
	listenerAid.remove($('downloadsPanel'), 'AskingForNodeOwner', holdDownloadsPanel);
	listenerAid.remove($('editBookmarkPanel'), 'AskingForNodeOwner', holdBookmarkPanel);
	listenerAid.remove(window, 'popupshowing', setupHoldDownloadsPanel);
	listenerAid.remove(window, 'popupshowing', setupHoldBookmarkPanel);

	listenerAid.remove(window, 'LoadedSlimChrome', loadSlimChromePopups);
	listenerAid.remove(window, 'UnloadingSlimChrome', unloadSlimChromePopups);

	if(typeof(slimChromeContainer) != 'undefined' && slimChromeContainer) {
		unloadSlimChromePopups();
	}
};
<commit_msg>#10 search engine selection menu should keep chrome visible<commit_after>moduleAid.VERSION = '2.0.16';

// this module catches the popup event and tells which nodes (triggers) the slimChrome script should check for

this.holdPanelUI = function(e) {
	e.detail = 'PanelUI-button';
	e.stopPropagation();
};

this.holdNavBarOverflow = function(e) {
	e.detail = 'nav-bar-overflow-button';
	e.stopPropagation();
};

this.holdPopupAutoComplete = function(e) {
	if(isAncestor(document.commandDispatcher.focusedElement, $('searchbar'))) {
		e.detail = 'searchbar';
		e.stopPropagation();
	}
};

this.holdPopupAutoCompleteRichResult = function(e) {
	e.detail = 'urlbar';
	e.stopPropagation();
};

this.holdIdentityPopup = function(e) {
	e.detail = 'identity-box';
	e.stopPropagation();
};

this.holdNotificationPopup = function(e) {
	e.detail = 'notification-popup-box';
	e.stopPropagation();
};

this.setupHoldDownloadsPanel = function(e) {
	if(e.target.id == 'downloadsPanel') {
		listenerAid.remove(window, 'popupshowing', setupHoldDownloadsPanel);
		listenerAid.add(e.target, 'AskingForNodeOwner', holdDownloadsPanel);
	}
};

this.holdDownloadsPanel = function(e) {
	e.detail = 'downloads-button';
	e.stopPropagation();
};

this.setupHoldBookmarkPanel = function(e) {
	if(e.target.id == 'editBookmarkPanel') {
		listenerAid.remove(window, 'popupshowing', setupHoldBookmarkPanel);
		listenerAid.add(e.target, 'AskingForNodeOwner', holdBookmarkPanel);
	}
};

this.holdBookmarkPanel = function(e) {
	e.detail = 'bookmarks-menu-button';
	e.stopPropagation();
};

// Keep chrome visible when opening menus within it
this.blockPopups = ['identity-popup', 'notification-popup'];
this.blockedPopup = false;
this.holdPopupNodes = [];
this.holdPopupMenu = function(e) {
	// don't do anything on tooltips! the UI might collapse altogether
	if(!e.target || e.target.nodeName == 'window' || e.target.nodeName == 'tooltip') { return; }

	var trigger = e.originalTarget.triggerNode;
	var target = e.target;

	// check if the trigger node is present in our toolbars;
	// there's no need to check the overflow panel here, as it will likely be open already in these cases
	var hold = isAncestor(trigger, slimChromeContainer);

	// try to use the anchor specified when opening the popup, if any; ditto from above for overflow panel nodes
	if(!hold && target.anchorNode) {
		hold = isAncestor(target.anchorNode, slimChromeContainer);
	}

	if(!hold && !trigger) {
		// CUI panel doesn't carry a triggerNode, we have to find it ourselves
		if(target.id == 'customizationui-widget-panel') {
			hold_loop: for(var t=0; t<slimChromeToolbars.childNodes.length; t++) {
				if(slimChromeToolbars.childNodes[t].localName != 'toolbar' || !CustomizableUI.getAreaType(slimChromeToolbars.childNodes[t].id)) { continue; }

				var widgets = CustomizableUI.getWidgetsInArea(slimChromeToolbars.childNodes[t].id);
				for(var w=0; w<widgets.length; w++) {
					var widget = widgets[w].forWindow(window);
					if(!widget || !widget.node || !widget.node.open) { continue; }

					hold = true;
					break hold_loop;
				}
			}
		}

		// let's just assume all panels that are children from these toolbars are opening from them
		else if(isAncestor(target, slimChromeContainer)) {
			hold = true;

			// the search engine selection menu is an anonymous child of the searchbar; e.target == $('searchbar'), so we need to explicitely get the actual menu to use
			if(target.id == 'searchbar') {
				target = document.getAnonymousElementByAttribute(target, 'anonid', 'searchbar-popup');
			}
		}
	}

	// nothing "native" is opening this popup, so let's see if someone claims it
	if(!hold) {
		trigger = askForOwner(target);
		if(trigger && typeof(trigger) == 'string') {
			trigger = $(trigger);
			// trigger could be either in the toolbars themselves or in the overflow panel
			hold = isAncestor(trigger, slimChromeContainer) || isAncestor(trigger, overflowList);
		}
	}

	if(hold) {
		// if we're opening the chrome now, the anchor may move, so we need to reposition the popup when it does
		holdPopupNodes.push(target);

		// sometimes when opening the menu panel, it will be nearly collapsed, I have no idea what is setting these values
		if(target.id == 'PanelUI-popup') {
			removeAttribute(target, 'width');
			removeAttribute(target, 'height');
		}

		// if opening a panel from the urlbar, we should keep the mini state, instead of expanding to full chrome
		if(trueAttribute(slimChromeContainer, 'mini') && slimChromeContainer.hovers == 0 && blockPopups.indexOf(target.id) > -1) {
			setMini(true);
			blockedPopup = true;
		} else {
			if(!trueAttribute(slimChromeContainer, 'fullWidth')) {
				hideIt(target);
				timerAid.init('ensureHoldPopupShows', popupsFinishedWidth, 200);
			}

			setHover(true, true);
		}

		var selfRemover = function(ee) {
			if(ee.originalTarget != e.originalTarget) { return; } //submenus
			listenerAid.remove(target, 'popuphidden', selfRemover);

			// making sure we don't collapse it permanently
			hideIt(target, true);

			if(typeof(setHover) != 'undefined') {
				if(trueAttribute(slimChromeContainer, 'mini') && blockPopups.indexOf(target.id) > -1) {
					if(blockedPopup) {
						hideMiniInABit();
						blockedPopup = false;
					}
				} else {
					setHover(false);
				}
			}

			aSync(function() {
				if(typeof(holdPopupNodes) != 'undefined' && holdPopupNodes.indexOf(target) > -1) {
					holdPopupNodes.splice(holdPopupNodes.indexOf(target), 1);
				}
			}, 150);
		}
		listenerAid.add(target, 'popuphidden', selfRemover);
	}
};

this.popupsWillSetMini = function(e) {
	// e.detail is if setting or unsetting mini state
	if(e.detail) { blockedPopup = false; }
};

this.popupsFinishedWidth = function() {
	timerAid.cancel('ensureHoldPopupShows');
	if(holdPopupNodes.length > 0) {
		for(var i=0; i<holdPopupNodes.length; i++) {
			// don't bother if the popup was never hidden to begin with,
			// it's not needed (the chrome was already expanded when it opened), so the popup is already properly placed,
			// also this prevents some issues, for example the context menu jumping to the top left corner
			if(!holdPopupNodes[i].collapsed) { continue; }

			// obviously we won't need to move it if it isn't open
			if(holdPopupNodes[i].open || holdPopupNodes[i].state == 'open') {
				holdPopupNodes[i].moveTo(-1,-1);
				hideIt(holdPopupNodes[i], true);
			}
		}

		// in case opening the popup triggered the chrome to show, and the mouse just so happens to be in that area, we need to make sure the mouse leaving
		// won't hide the chrome with the popup still shown
		if(slimChromeContainer.hovers === 1 && $$('#'+objName+'-slimChrome-container:hover')[0]) {
			setHover(true);
		}
	}
};

this.loadSlimChromePopups = function() {
	// if a menu or a panel is opened from the toolbox, keep it shown
	listenerAid.add(window, 'popupshown', holdPopupMenu);
	listenerAid.add(slimChromeContainer, 'willSetMiniChrome', popupsWillSetMini);
	listenerAid.add(slimChromeContainer, 'FinishedSlimChromeWidth', popupsFinishedWidth);
};

this.unloadSlimChromePopups = function() {
	timerAid.cancel('ensureHoldPopupShows');

	listenerAid.remove(window, 'popupshown', holdPopupMenu);
	listenerAid.remove(slimChromeContainer, 'willSetMiniChrome', popupsWillSetMini);
	listenerAid.remove(slimChromeContainer, 'FinishedSlimChromeWidth', popupsFinishedWidth);
};

moduleAid.LOADMODULE = function() {
	listenerAid.add(window, 'LoadedSlimChrome', loadSlimChromePopups);
	listenerAid.add(window, 'UnloadingSlimChrome', unloadSlimChromePopups);

	// in case slimChrome loads before popups
	if(typeof(slimChromeContainer) != 'undefined' && slimChromeContainer) {
		loadSlimChromePopups();
	}

	// make sure we know about all these panels so we can hold the chrome open with them
	listenerAid.add($('PanelUI-popup'), 'AskingForNodeOwner', holdPanelUI);
	listenerAid.add($('widget-overflow'), 'AskingForNodeOwner', holdNavBarOverflow);
	listenerAid.add($('PopupAutoComplete'), 'AskingForNodeOwner', holdPopupAutoComplete);
	listenerAid.add($('PopupAutoCompleteRichResult'), 'AskingForNodeOwner', holdPopupAutoCompleteRichResult);
	listenerAid.add($('identity-popup'), 'AskingForNodeOwner', holdIdentityPopup);
	listenerAid.add($('notification-popup'), 'AskingForNodeOwner', holdNotificationPopup);

	// the downloadsPanel is only created when first called
	if($('downloadsPanel')) {
		listenerAid.add($('downloadsPanel'), 'AskingForNodeOwner', holdDownloadsPanel);
	} else {
		listenerAid.add(window, 'popupshowing', setupHoldDownloadsPanel);
	}

	// ditto for the editBookmarkPanel
	if($('editBookmarkPanel')) {
		listenerAid.add($('editBookmarkPanel'), 'AskingForNodeOwner', holdBookmarkPanel);
	} else {
		listenerAid.add(window, 'popupshowing', setupHoldBookmarkPanel);
	}
};

moduleAid.UNLOADMODULE = function() {
	listenerAid.remove($('PanelUI-popup'), 'AskingForNodeOwner', holdPanelUI);
	listenerAid.remove($('widget-overflow'), 'AskingForNodeOwner', holdNavBarOverflow);
	listenerAid.remove($('PopupAutoComplete'), 'AskingForNodeOwner', holdPopupAutoComplete);
	listenerAid.remove($('PopupAutoCompleteRichResult'), 'AskingForNodeOwner', holdPopupAutoCompleteRichResult);
	listenerAid.remove($('identity-popup'), 'AskingForNodeOwner', holdIdentityPopup);
	listenerAid.remove($('notification-popup'), 'AskingForNodeOwner', holdNotificationPopup);
	listenerAid.remove($('downloadsPanel'), 'AskingForNodeOwner', holdDownloadsPanel);
	listenerAid.remove($('editBookmarkPanel'), 'AskingForNodeOwner', holdBookmarkPanel);
	listenerAid.remove(window, 'popupshowing', setupHoldDownloadsPanel);
	listenerAid.remove(window, 'popupshowing', setupHoldBookmarkPanel);

	listenerAid.remove(window, 'LoadedSlimChrome', loadSlimChromePopups);
	listenerAid.remove(window, 'UnloadingSlimChrome', unloadSlimChromePopups);

	if(typeof(slimChromeContainer) != 'undefined' && slimChromeContainer) {
		unloadSlimChromePopups();
	}
};
<|endoftext|>


<commit_before>/**
 * test.js - Histone template engine.
 * Copyright 2012 MegaFon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

load('Histone.js');

const Java = JavaImporter(
	java.io.File,
	java.net.URI,
	java.io.FileWriter,
	java.io.PrintWriter,
	java.io.FileInputStream,
	java.io.FileOutputStream,
	org.w3c.dom.NodeList,
	javax.xml.parsers.DocumentBuilderFactory,
	javax.xml.xpath.XPathFactory,
	javax.xml.xpath.XPathConstants
);

function printColor() {
	var args = Array.prototype.slice.call(arguments);
	var color = args.shift();
	switch (color) {
		case 'red':
			color = '\033[01;31m';
			break;
		case 'white':
			color = '\033[01;37m';
			break;
		case 'green':
			color = '\033[01;32m';
			break;
		default:
			color = '';
	}
	print.apply(this, [color].concat(args));
}

function readDir(path, callback) {
	var result = [];
	if (!callback) callback = function() {};
	var dirObj = new java.io.File(path);
	if (dirObj.isDirectory()) {
		var files = dirObj.list();
		var absolutePath = (dirObj.getAbsolutePath() + '');
		for (var fileDescr = {}, c = 0; c < files.length; c++) {
			var filePath = absolutePath.concat('/', String(files[c]));
			var fileObj = new java.io.File(filePath);
			var fileName = String(fileObj.getName());
			if (fileObj.isDirectory()) fileDescr = {
				'type': 'folder', 'name': fileName, 'path': filePath
			}; else if (fileObj.isFile()) fileDescr = {
				'type': 'file', 'name': fileName, 'path': filePath
			};
			if (callback(fileDescr) === false) continue;
			if (fileObj.isDirectory()) {
				fileDescr.files = readDir(filePath, callback);
				if (callback(fileDescr)) result.push(fileDescr);
			} else result.push(fileDescr);
		}
	}
	return result;
};

var testResult = 0;

readDir('histone-acceptance-tests/src/main/acceptance/parser', function(file) {
	var fileType = file.name.split('.').pop();
	if (fileType !== 'json') return;

	//
	// if (file.name === 'functions.json') {
	// 	var testSuites = readFile(file.path);
	// 	testSuites = JSON.parse(testSuites);
	// 	for (var i = 0; i < testSuites.length; i++) {
	// 		var testSuite = testSuites[i];
	// 		print('TESTING:', testSuite.name);
	// 		for (var j = 0; j < testSuite.cases.length; j++) {
	// 			var testCase = testSuite.cases[j];
	// 			if (!testCase.expected) continue;
	// 			testCase.expected = JSON.parse(testCase.expected);
	// 		}
	// 	}
	// 	print(JSON.stringify(testSuites));
	// }

	var testSuites = readFile(file.path);
	testSuites = JSON.parse(testSuites);

	for (var i = 0; i < testSuites.length; i++) {
		var testSuite = testSuites[i];
		printColor('green', '\n[TESTING "' + testSuite.name + '"]\n');
		for (var j = 0; j < testSuite.cases.length; j++) {
			var testCase = testSuite.cases[j];
			var input = testCase.input;

			if (testCase.hasOwnProperty('expected')) {
				var expected = testCase.expected;
				expected = JSON.stringify(expected);
				var result = Histone(input);
				result = JSON.stringify(result.getAST());
				if (expected === result) {
					printColor('white', '	[SUCCESS]', input);
				} else {
					printColor('red', '	[FAILING]', input);
					testResult = 1;
				}

			}

			else if (testCase.hasOwnProperty('exception')) {
				// var exception = testCase.exception;
				// try {
				// 	Histone(input);
				// } catch (error) {
				// 	for (var key in exception) {

				// 	}
				// 	// print(JSON.stringify(result));
				// }
			}

		}
	}

});

quit(testResult);<commit_msg>playing with colors<commit_after>/**
 * test.js - Histone template engine.
 * Copyright 2012 MegaFon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

load('Histone.js');

const Java = JavaImporter(
	java.io.File,
	java.net.URI,
	java.io.FileWriter,
	java.io.PrintWriter,
	java.io.FileInputStream,
	java.io.FileOutputStream,
	org.w3c.dom.NodeList,
	javax.xml.parsers.DocumentBuilderFactory,
	javax.xml.xpath.XPathFactory,
	javax.xml.xpath.XPathConstants
);

function printColor() {
	var args = Array.prototype.slice.call(arguments);
	var color = args.shift();
	switch (color) {
		case 'red':
			color = '\033[01;31m';
			break;
		case 'white':
			color = '\033[01;37m';
			break;
		case 'green':
			color = '\033[01;32m';
			break;
		default:
			color = '';
	}
	print.apply(this, [color].concat(args));
}

function readDir(path, callback) {
	var result = [];
	if (!callback) callback = function() {};
	var dirObj = new java.io.File(path);
	if (dirObj.isDirectory()) {
		var files = dirObj.list();
		var absolutePath = (dirObj.getAbsolutePath() + '');
		for (var fileDescr = {}, c = 0; c < files.length; c++) {
			var filePath = absolutePath.concat('/', String(files[c]));
			var fileObj = new java.io.File(filePath);
			var fileName = String(fileObj.getName());
			if (fileObj.isDirectory()) fileDescr = {
				'type': 'folder', 'name': fileName, 'path': filePath
			}; else if (fileObj.isFile()) fileDescr = {
				'type': 'file', 'name': fileName, 'path': filePath
			};
			if (callback(fileDescr) === false) continue;
			if (fileObj.isDirectory()) {
				fileDescr.files = readDir(filePath, callback);
				if (callback(fileDescr)) result.push(fileDescr);
			} else result.push(fileDescr);
		}
	}
	return result;
};

var testResult = 0;

readDir('histone-acceptance-tests/src/main/acceptance/parser', function(file) {
	var fileType = file.name.split('.').pop();
	if (fileType !== 'json') return;

	//
	// if (file.name === 'functions.json') {
	// 	var testSuites = readFile(file.path);
	// 	testSuites = JSON.parse(testSuites);
	// 	for (var i = 0; i < testSuites.length; i++) {
	// 		var testSuite = testSuites[i];
	// 		print('TESTING:', testSuite.name);
	// 		for (var j = 0; j < testSuite.cases.length; j++) {
	// 			var testCase = testSuite.cases[j];
	// 			if (!testCase.expected) continue;
	// 			testCase.expected = JSON.parse(testCase.expected);
	// 		}
	// 	}
	//
	// 	print(JSON.stringify(testSuites));
	// }

	var testSuites = readFile(file.path);
	testSuites = JSON.parse(testSuites);

	for (var i = 0; i < testSuites.length; i++) {
		var testSuite = testSuites[i];
		printColor('green', '\n[TESTING "' + testSuite.name + '"]\n');
		for (var j = 0; j < testSuite.cases.length; j++) {
			var testCase = testSuite.cases[j];
			var input = testCase.input;

			if (testCase.hasOwnProperty('expected')) {
				var expected = testCase.expected;
				expected = JSON.stringify(expected);
				var result = Histone(input);
				result = JSON.stringify(result.getAST());
				if (expected === result) {
					printColor('white', '	[SUCCESS]', input);
				} else {
					printColor('red', '	[FAILING]', input);
					testResult = 1;
				}

			}

			else if (testCase.hasOwnProperty('exception')) {
				// var exception = testCase.exception;
				// try {
				// 	Histone(input);
				// } catch (error) {
				// 	for (var key in exception) {

				// 	}
				// 	// print(JSON.stringify(result));
				// }
			}

		}
	}

});

quit(testResult);<|endoftext|>


<commit_before>/*
 * queue.js
 */
var util = require('util');
var MongoClient = require('mongodb').MongoClient;

var logger, client;

function MongoInserter(collection){
  this._coll = collection;
}
MongoInserter.prototype.start = function(items, callback){
  this._items = items;
  this._callback = callback;
  this._insertNext();
  this._insertedCount = 0;
}
MongoInserter.prototype._insertNext = function(){
  var item = this._items.shift();
  if (item == undefined) {
    this._callback(null, this._insertedCount);
    return;
  }

  if (!("key" in item)) {
    logger.warn("Ignored item without 'key'.");
    this._insertNext();
    return;
  }

  if (!("priority" in item)) {
    item.priority = 10;
  }

  logger.debug("db." + this._coll.collectionName + ".upsert: " + util.inspect(item));

  var me = this;

  var updateSet = {
      $inc : { priority: item.priority }
    };

  if ("value" in item) {
    updateSet["$set"] = { value: item.value };
  }

  me._coll.update({ "key" : item.key }, updateSet,
      { safe: true, upsert:true },
      function(err) {
        if (err) {
          me._callback(err, me._insertedCount);
          return;
        }

        me._insertedCount++;
        me._insertNext();
      });
}

exports.initialize = function(dbUri, param, mylogger, callback){
  logger = mylogger;
  callback = callback || (function(){});

  var params = param || {};
  if (!("auto_reconnect" in params))
     params["auto_reconnect"] = true;

  suffix = params.suffix || "";
  delete params.suffix;

  MongoClient.connect(dbUri, params, function(err, db){
      if (err) {
        callback(err);
        return;
      }
      client = db;

      callback(null);
    });
};

exports.terminate = function(){
  if (client) {
    client.close();
  }
}

exports.dataIn = function(target, items, callback){
  client.collection(target + suffix, function(err, coll) {
      if (err) {
        callback(err);
        return;
      }

      new MongoInserter(coll).start(items, callback);
    });
};

exports.dataOut = function(target, callback){
  var cb = function(err, result){
      result = result || null;
      return callback(err, result);
  };

  client.collection(target + suffix, function(err, coll) {
      if (err) {
        cb(err);
        return;
      }

      var sort = [ ["priority", -1], ["_id", 1] ];

      coll.find({}, {'limit': 1, 'sort': sort },
        function(err, cursor){
          if (err) {
            cb(err);
            return;
          }

          cursor.toArray(function(err, docs){
              if (!docs || docs.length == 0) {
                cb(null, null);
                return;
              }

              var doc = docs[0];
              var docId = doc._id;
              logger.debug("Data-out: " + util.inspect(doc));

              delete doc["_id"];

              if ( cb(null, doc) ) {
                coll.remove({ "_id": docId }, function(err, result){
                  logger.debug("Removed document _id:" + docId);
                });
              } else {
                logger.error("Client not received document _id:" + docId);
              }
            });
        });
    });
};

exports.clear = function(target, callback){
  client.collection(target + suffix, function(err, coll) {
      if (err) {
        callback(err);
        return;
      }

      coll.remove({}, function(err, result){
          callback(err);
        });
    });
};

<commit_msg>Fix to create mongodb client<commit_after>/*
 * queue.js
 */
const util = require('util')
const MongoClient = require('mongodb').MongoClient

let logger, client, db;

function MongoInserter(collection){
  this._coll = collection;
}
MongoInserter.prototype.start = function(items, callback){
  this._items = items;
  this._callback = callback;
  this._insertNext();
  this._insertedCount = 0;
}
MongoInserter.prototype._insertNext = function(){
  var item = this._items.shift();
  if (item == undefined) {
    this._callback(null, this._insertedCount);
    return;
  }

  if (!("key" in item)) {
    logger.warn("Ignored item without 'key'.");
    this._insertNext();
    return;
  }

  if (!("priority" in item)) {
    item.priority = 10;
  }

  logger.debug("db." + this._coll.collectionName + ".upsert: " + util.inspect(item));

  var me = this;

  var updateSet = {
      $inc : { priority: item.priority }
    };

  if ("value" in item) {
    updateSet["$set"] = { value: item.value };
  }

  me._coll.update({ "key" : item.key }, updateSet,
      { safe: true, upsert:true },
      function(err) {
        if (err) {
          me._callback(err, me._insertedCount);
          return;
        }

        me._insertedCount++;
        me._insertNext();
      });
}

exports.initialize = (dbUri, param, mylogger, callback) => {
  logger = mylogger;
  callback = callback || (function(){});

  const params = param || {};
  if (!("auto_reconnect" in params))
     params["auto_reconnect"] = true;

  const { suffix: suffix_, ...otherParams } = params
  suffix = suffix_ || ''

  const lastSlashPost = dbUri.lastIndexOf('/')
  const dbEndpoint = dbUri.substr(0, lastSlashPost)
  const dbName = dbUri.substr(lastSlashPost + 1)

  MongoClient.connect(dbEndpoint, otherParams, (err, client_) => {
    if (err) {
      callback(err)
      return
    }
    client = client_
    db = client_.db(dbName)
    callback(null)
  })
}

exports.terminate = function(){
  if (client) {
    client.close();
  }
}

exports.dataIn = function(target, items, callback){
  db.collection(target + suffix, function(err, coll) {
      if (err) {
        callback(err);
        return;
      }

      new MongoInserter(coll).start(items, callback);
    });
};

exports.dataOut = function(target, callback){
  var cb = function(err, result){
      result = result || null;
      return callback(err, result);
  };

  db.collection(target + suffix, function(err, coll) {
      if (err) {
        cb(err);
        return;
      }

      var sort = [ ["priority", -1], ["_id", 1] ];

      coll.find({}, {'limit': 1, 'sort': sort },
        function(err, cursor){
          if (err) {
            cb(err);
            return;
          }

          cursor.toArray(function(err, docs){
              if (!docs || docs.length == 0) {
                cb(null, null);
                return;
              }

              var doc = docs[0];
              var docId = doc._id;
              logger.debug("Data-out: " + util.inspect(doc));

              delete doc["_id"];

              if ( cb(null, doc) ) {
                coll.remove({ "_id": docId }, function(err, result){
                  logger.debug("Removed document _id:" + docId);
                });
              } else {
                logger.error("Client not received document _id:" + docId);
              }
            });
        });
    });
};

exports.clear = function(target, callback){
  db.collection(target + suffix, function(err, coll) {
      if (err) {
        callback(err);
        return;
      }

      coll.remove({}, function(err, result){
          callback(err);
        });
    });
};

<|endoftext|>


<commit_before>import UserPage from 'flarum/components/UserPage';
import PrivateDiscussionList from './PrivateDiscussionList';
import Button from 'flarum/components/Button';
import Dropdown from 'flarum/components/Dropdown';
import ItemList from 'flarum/utils/ItemList';
import listItems from 'flarum/helpers/listItems';
import PrivateDiscussionComposer from './PrivateDiscussionComposer';
import LogInModal from 'flarum/components/LogInModal';

export default class PrivateDiscussionsUserPage extends UserPage {
    init() {
        super.init();

        this.changeSort('latest');
    }

    show(user) {
        // We can not create the list in init because the user will not be available if it has to be loaded asynchronously
        this.list = new PrivateDiscussionList({
            params: {
                q: `byobu:${user.username()} is:private`,
                sort: this.sort,
            },
        });

        this.list.refresh();

        // We call the parent method after creating the list, this way the this.list property
        // is set before content() is called for the first time
        super.show(user);
    }

    handleChangeSort(sort, e) {
        e.preventDefault();

        this.changeSort(sort);
    }

    changeSort(sort) {
        this.sort = sort;
        this.loadUser(m.route.param('username'));
    }

    newDiscussionAction(e) {
        e.preventDefault();

        const deferred = m.deferred();

        if (app.session.user) {
            let recipients = new ItemList();
            recipients.add('users:' + app.session.user.id(), app.session.user);

            if (this.user !== null && app.session.user.id() !== this.user.id()) {
                recipients.add('users:' + this.user.id(), this.user);
            }

            PrivateDiscussionComposer.prototype.recipients = recipients;

            const component = new PrivateDiscussionComposer({
                user: app.session.user,
                recipients: recipients,
                recipientUsers: recipients,
                titlePlaceholder: app.translator.trans('fof-byobu.forum.composer_private_discussion.title_placeholder'),
                submitLabel: app.translator.trans('fof-byobu.forum.composer_private_discussion.submit_button'),
            });

            app.composer.load(component);

            deferred.resolve(component);
        } else {
            deferred.reject();

            app.modal.show(new LogInModal());
        }

        return deferred.promise;
    }

    content() {
        return (

{listItems(this.actionItems().toArray())}

{listItems(this.viewItems().toArray())}

                {this.list.render()}

        );
    }

    actionItems() {
        const items = new ItemList();
        const canStartDiscussion = app.forum.attribute('canStartDiscussion') || !app.session.user;

        if (app.session.user && app.forum.attribute('canStartPrivateDiscussion')) {
            items.add(
                'start_private',
                Button.component({
                    children: app.translator.trans(
                        canStartDiscussion ? 'fof-byobu.forum.nav.start_button' : 'core.forum.index.cannot_start_discussion_button'
                    ),
                    className: 'Button Button--primary IndexPage-newDiscussion',
                    itemClassName: 'App-primaryControl',
                    onclick: this.newDiscussionAction.bind(this),
                    disabled: !canStartDiscussion,
                })
            );
        }

        return items;
    }

    viewItems() {
        const items = new ItemList();
        const sortMap = this.list.sortMap();

        const sortOptions = {};
        for (const i in sortMap) {
            sortOptions[i] = app.translator.trans('core.forum.index_sort.' + i + '_button');
        }

        items.add(
            'sort',
            Dropdown.component({
                buttonClassName: 'Button',
                label: sortOptions[this.sort] || Object.keys(sortMap).map(key => sortOptions[key])[0],
                children: Object.keys(sortOptions).map(value => {
                    const label = sortOptions[value];
                    const active = (this.sort || Object.keys(sortMap)[0]) === value;

                    return Button.component({
                        children: label,
                        icon: active ? 'fas fa-check' : true,
                        onclick: this.handleChangeSort.bind(this, value),
                        active: active,
                    });
                }),
            })
        );

        return items;
    }
}
<commit_msg>Prevent collapse of new PD button on mobile (#116) (#118)<commit_after>import UserPage from 'flarum/components/UserPage';
import PrivateDiscussionList from './PrivateDiscussionList';
import Button from 'flarum/components/Button';
import Dropdown from 'flarum/components/Dropdown';
import ItemList from 'flarum/utils/ItemList';
import listItems from 'flarum/helpers/listItems';
import PrivateDiscussionComposer from './PrivateDiscussionComposer';
import LogInModal from 'flarum/components/LogInModal';

export default class PrivateDiscussionsUserPage extends UserPage {
    init() {
        super.init();

        this.changeSort('latest');
    }

    show(user) {
        // We can not create the list in init because the user will not be available if it has to be loaded asynchronously
        this.list = new PrivateDiscussionList({
            params: {
                q: `byobu:${user.username()} is:private`,
                sort: this.sort,
            },
        });

        this.list.refresh();

        // We call the parent method after creating the list, this way the this.list property
        // is set before content() is called for the first time
        super.show(user);
    }

    handleChangeSort(sort, e) {
        e.preventDefault();

        this.changeSort(sort);
    }

    changeSort(sort) {
        this.sort = sort;
        this.loadUser(m.route.param('username'));
    }

    newDiscussionAction(e) {
        e.preventDefault();

        const deferred = m.deferred();

        if (app.session.user) {
            let recipients = new ItemList();
            recipients.add('users:' + app.session.user.id(), app.session.user);

            if (this.user !== null && app.session.user.id() !== this.user.id()) {
                recipients.add('users:' + this.user.id(), this.user);
            }

            PrivateDiscussionComposer.prototype.recipients = recipients;

            const component = new PrivateDiscussionComposer({
                user: app.session.user,
                recipients: recipients,
                recipientUsers: recipients,
                titlePlaceholder: app.translator.trans('fof-byobu.forum.composer_private_discussion.title_placeholder'),
                submitLabel: app.translator.trans('fof-byobu.forum.composer_private_discussion.submit_button'),
            });

            app.composer.load(component);

            deferred.resolve(component);
        } else {
            deferred.reject();

            app.modal.show(new LogInModal());
        }

        return deferred.promise;
    }

    content() {
        return (

{listItems(this.actionItems().toArray())}

{listItems(this.viewItems().toArray())}

                {this.list.render()}

        );
    }

    actionItems() {
        const items = new ItemList();
        const canStartDiscussion = app.forum.attribute('canStartDiscussion') || !app.session.user;

        if (app.session.user && app.forum.attribute('canStartPrivateDiscussion')) {
            items.add(
                'start_private',
                Button.component({
                    children: app.translator.trans(
                        canStartDiscussion ? 'fof-byobu.forum.nav.start_button' : 'core.forum.index.cannot_start_discussion_button'
                    ),
                    className: 'Button Button--primary IndexPage-newDiscussion',
                    itemClassName: 'fof-byobu_primaryControl',
                    onclick: this.newDiscussionAction.bind(this),
                    disabled: !canStartDiscussion,
                })
            );
        }

        return items;
    }

    viewItems() {
        const items = new ItemList();
        const sortMap = this.list.sortMap();

        const sortOptions = {};
        for (const i in sortMap) {
            sortOptions[i] = app.translator.trans('core.forum.index_sort.' + i + '_button');
        }

        items.add(
            'sort',
            Dropdown.component({
                buttonClassName: 'Button',
                label: sortOptions[this.sort] || Object.keys(sortMap).map(key => sortOptions[key])[0],
                children: Object.keys(sortOptions).map(value => {
                    const label = sortOptions[value];
                    const active = (this.sort || Object.keys(sortMap)[0]) === value;

                    return Button.component({
                        children: label,
                        icon: active ? 'fas fa-check' : true,
                        onclick: this.handleChangeSort.bind(this, value),
                        active: active,
                    });
                }),
            })
        );

        return items;
    }
}
<|endoftext|>


<commit_before>Parse.initialize("ONeIYsbSCtIJg6bo0M823t6Z8ZqmqEM4Zfgh2U5a", "A1lJ2mF6YV48qcO5KBkQ1XCEC2BDF3mAt1MlqqlB");

function updateText(postslist) {
  var arr = [];
  for(var i = 0; i < 4; i++) {
    arr[i] = postslist[Math.floor((Math.random() * postslist.length))];
  }
  var chosen = Math.floor((Math.random() * 4));
  var message = arr[chosen].message + " answer:" + arr[chosen].name;
  $('#statusPost').text(message);
  $('#pic1').append('');
  $('#name1').text(arr[0].name);
  $('#pic1').append('');
  $('#name2').text(arr[1].name);
  $('#pic1').append('');
  $('#name3').text(arr[2].name);
  $('#pic1').append('');
  $('#name4').text(arr[3].name);
}

function test(){
  var TestObject = Parse.Object.extend("TestObject");

  var testObject = new TestObject();
  testObject.save({foo: "bar"},
    {
      success: function(object) {
        alert("success");
      },
      error: function(model, error) {
        alert("error");
      }
    }
  );
}

<commit_msg>fixing bug where images are on same row<commit_after>Parse.initialize("ONeIYsbSCtIJg6bo0M823t6Z8ZqmqEM4Zfgh2U5a", "A1lJ2mF6YV48qcO5KBkQ1XCEC2BDF3mAt1MlqqlB");

function updateText(postslist) {
  var arr = [];
  for(var i = 0; i < 4; i++) {
    arr[i] = postslist[Math.floor((Math.random() * postslist.length))];
  }
  var chosen = Math.floor((Math.random() * 4));
  var message = arr[chosen].message + " answer:" + arr[chosen].name;
  $('#statusPost').text(message);
  $('#pic1').append('');
  $('#name1').text(arr[0].name);
  $('#pic2').append('');
  $('#name2').text(arr[1].name);
  $('#pic3').append('');
  $('#name3').text(arr[2].name);
  $('#pic4').append('');
  $('#name4').text(arr[3].name);
}

function test(){
  var TestObject = Parse.Object.extend("TestObject");

  var testObject = new TestObject();
  testObject.save({foo: "bar"},
    {
      success: function(object) {
        alert("success");
      },
      error: function(model, error) {
        alert("error");
      }
    }
  );
}

<|endoftext|>


<commit_before>var util = require('util');
var connection = require(__dirname + "/connection.js").connection;
var pluginRunner = require(__dirname + "/pluginRunner.js");

var queue = function(options, jobs, callback){
  var self = this;
  if(typeof jobs == 'function' && callback == null){
    callback = jobs;
    jobs = {};
  }
  self.options = options;
  self.jobs = jobs;
  self.queueObject = self; // to keep plugins consistant

  self.runPlugin  = pluginRunner.runPlugin
  self.runPlugins = pluginRunner.runPlugins

  self.connection = new connection(options.connection);
  self.connection.connect(function(err){
    if(typeof callback == 'function'){ callback(); }
  });
}

queue.prototype.end = function(callback){
  var self = this;
  self.connection.redis.quit();
  process.nextTick(function(){
    callback();
  });
}

queue.prototype.encode = function(q, func, args){
  return JSON.stringify({
    "class": func,
    queue: q,
    args: args || []
  });
}

queue.prototype.enqueue = function(q, func, args, callback){
  var self = this;
  var job = self.jobs[func];
  self.runPlugins('before_enqueue', func, q, job, args, function(err, toRun){
    if(toRun == false){
      if(typeof callback == "function"){ callback(err, toRun); }
    }else{
      self.connection.ensureConnected(callback, function(){
        self.connection.redis.sadd(self.connection.key('queues'), q, function(){
          self.connection.redis.rpush(self.connection.key('queue', q), self.encode(q, func, args), function(){
            self.runPlugins('after_enqueue', func, q, job, args, function(){
              if(typeof callback == "function"){ callback(err, toRun); }
            });
          });
        });
      });
    }
  });
}

queue.prototype.enqueueAt = function(timestamp, q, func, args, callback){
  // Don't run plugins here, they should be run by scheduler at the enqueue step
  var self = this;
  self.connection.ensureConnected(callback, function(){
    var item = self.encode(q, func, args);
    var rTimestamp = Math.round(timestamp / 1000); // assume timestamp is in ms
    // enqueue the encoded job into a list per timestmp to be popped and workered later
    self.connection.redis.rpush(self.connection.key("delayed:" + rTimestamp), item, function(){
      // save the job + args into a set so that it can be checked by plugins
      self.connection.redis.sadd(self.connection.key("timestamps:" + item), self.connection.key("delayed:" + rTimestamp), function(){
        // and the timestamp in question to a zset to the scheduler will know which timestamps have data to work
        self.connection.redis.zadd(self.connection.key('delayed_queue_schedule'), rTimestamp, rTimestamp, function(){
          if(typeof callback == "function"){ callback(); }
        });
      });
    });
  });
};

queue.prototype.enqueueIn = function(time, q, func, args, callback){
  var self = this;
  var timestamp = (new Date().getTime()) + time;
  self.enqueueAt(timestamp, q, func, args, function(){
    if(typeof callback == "function"){ callback(); }
  });
}

queue.prototype.queues = function(callback){
  var self = this;
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.smembers(self.connection.key('queues'), function(err, queues){
      callback(err, queues);
    });
  });
}

queue.prototype.length = function(q, callback){
  var self = this;
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.llen(self.connection.key('queue', q), function(err, length){
      callback(err, length);
    });
  });
}

queue.prototype.del = function(q, func, args, count, callback){
  var self = this;
  if(typeof count == "function" && callback == null){
    callback = count;
    count = 0; // remove first enqueued items that match
  }
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.lrem(self.connection.key('queue', q), count, self.encode(q, func, args), function(err, count){
      if(typeof callback == "function"){ callback(err, count); }
    });
  });
}

queue.prototype.delDelayed = function(q, func, args, callback){
  var self = this;
  var search = self.encode(q, func, args);
  self.connection.ensureConnected(callback, function(){
    var timestamps = self.connection.redis.smembers(self.connection.key("timestamps:" + search), function(err, members){
      if(members.length == 0 ){ if(typeof callback == "function"){ callback(err, []); } }
      else{
        var started = 0;
        var timestamps = [];
        members.forEach(function(key){
          started++;
          self.connection.redis.lrem(key, 0, search, function(){
            self.connection.redis.srem(self.connection.key("timestamps:" + search), key, function(){
              timestamps.push(key.split(":")[key.split(":").length - 1]);
              started--;
              if(started == 0){
                if(typeof callback == "function"){ callback(err, timestamps); }
              }
            })
          })
        });
      }
    });
  });
}

queue.prototype.scheduledAt = function(q, func, args, callback){
  var self = this;
  var search = self.encode(q, func, args);
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.smembers(self.connection.key("timestamps:" + search), function(err, members){
      var timestamps = [];
      if(members != null){
        members.forEach(function(key){
          timestamps.push(key.split(":")[key.split(":").length - 1]);
        })
      }
      if(typeof callback == "function"){ callback(err, timestamps); }
    });
  });
}

exports.queue = queue;<commit_msg>optional callback on queue.end<commit_after>var util = require('util');
var connection = require(__dirname + "/connection.js").connection;
var pluginRunner = require(__dirname + "/pluginRunner.js");

var queue = function(options, jobs, callback){
  var self = this;
  if(typeof jobs == 'function' && callback == null){
    callback = jobs;
    jobs = {};
  }
  self.options = options;
  self.jobs = jobs;
  self.queueObject = self; // to keep plugins consistant

  self.runPlugin  = pluginRunner.runPlugin
  self.runPlugins = pluginRunner.runPlugins

  self.connection = new connection(options.connection);
  self.connection.connect(function(err){
    if(typeof callback == 'function'){ callback(); }
  });
}

queue.prototype.end = function(callback){
  var self = this;
  self.connection.redis.quit();
  process.nextTick(function(){
    if(typeof callback === "function"){ callback(); }
  });
}

queue.prototype.encode = function(q, func, args){
  return JSON.stringify({
    "class": func,
    queue: q,
    args: args || []
  });
}

queue.prototype.enqueue = function(q, func, args, callback){
  var self = this;
  var job = self.jobs[func];
  self.runPlugins('before_enqueue', func, q, job, args, function(err, toRun){
    if(toRun == false){
      if(typeof callback == "function"){ callback(err, toRun); }
    }else{
      self.connection.ensureConnected(callback, function(){
        self.connection.redis.sadd(self.connection.key('queues'), q, function(){
          self.connection.redis.rpush(self.connection.key('queue', q), self.encode(q, func, args), function(){
            self.runPlugins('after_enqueue', func, q, job, args, function(){
              if(typeof callback == "function"){ callback(err, toRun); }
            });
          });
        });
      });
    }
  });
}

queue.prototype.enqueueAt = function(timestamp, q, func, args, callback){
  // Don't run plugins here, they should be run by scheduler at the enqueue step
  var self = this;
  self.connection.ensureConnected(callback, function(){
    var item = self.encode(q, func, args);
    var rTimestamp = Math.round(timestamp / 1000); // assume timestamp is in ms
    // enqueue the encoded job into a list per timestmp to be popped and workered later
    self.connection.redis.rpush(self.connection.key("delayed:" + rTimestamp), item, function(){
      // save the job + args into a set so that it can be checked by plugins
      self.connection.redis.sadd(self.connection.key("timestamps:" + item), self.connection.key("delayed:" + rTimestamp), function(){
        // and the timestamp in question to a zset to the scheduler will know which timestamps have data to work
        self.connection.redis.zadd(self.connection.key('delayed_queue_schedule'), rTimestamp, rTimestamp, function(){
          if(typeof callback == "function"){ callback(); }
        });
      });
    });
  });
};

queue.prototype.enqueueIn = function(time, q, func, args, callback){
  var self = this;
  var timestamp = (new Date().getTime()) + time;
  self.enqueueAt(timestamp, q, func, args, function(){
    if(typeof callback == "function"){ callback(); }
  });
}

queue.prototype.queues = function(callback){
  var self = this;
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.smembers(self.connection.key('queues'), function(err, queues){
      callback(err, queues);
    });
  });
}

queue.prototype.length = function(q, callback){
  var self = this;
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.llen(self.connection.key('queue', q), function(err, length){
      callback(err, length);
    });
  });
}

queue.prototype.del = function(q, func, args, count, callback){
  var self = this;
  if(typeof count == "function" && callback == null){
    callback = count;
    count = 0; // remove first enqueued items that match
  }
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.lrem(self.connection.key('queue', q), count, self.encode(q, func, args), function(err, count){
      if(typeof callback == "function"){ callback(err, count); }
    });
  });
}

queue.prototype.delDelayed = function(q, func, args, callback){
  var self = this;
  var search = self.encode(q, func, args);
  self.connection.ensureConnected(callback, function(){
    var timestamps = self.connection.redis.smembers(self.connection.key("timestamps:" + search), function(err, members){
      if(members.length == 0 ){ if(typeof callback == "function"){ callback(err, []); } }
      else{
        var started = 0;
        var timestamps = [];
        members.forEach(function(key){
          started++;
          self.connection.redis.lrem(key, 0, search, function(){
            self.connection.redis.srem(self.connection.key("timestamps:" + search), key, function(){
              timestamps.push(key.split(":")[key.split(":").length - 1]);
              started--;
              if(started == 0){
                if(typeof callback == "function"){ callback(err, timestamps); }
              }
            })
          })
        });
      }
    });
  });
}

queue.prototype.scheduledAt = function(q, func, args, callback){
  var self = this;
  var search = self.encode(q, func, args);
  self.connection.ensureConnected(callback, function(){
    self.connection.redis.smembers(self.connection.key("timestamps:" + search), function(err, members){
      var timestamps = [];
      if(members != null){
        members.forEach(function(key){
          timestamps.push(key.split(":")[key.split(":").length - 1]);
        })
      }
      if(typeof callback == "function"){ callback(err, timestamps); }
    });
  });
}

exports.queue = queue;<|endoftext|>


<commit_before>var nad = nad || {};

nad.state = {
  // Initial amplifier state
  amp: {
    power: false,
    speakerA: true,
    speakerB: false,
    mute: false,
    source: 'CD',
    model: ''
  },
  message: {},
  error: {},
  helpVisible: false,
  power: function() {
    nad.send('power', !nad.state.amp.power);
  },
  mute: function() {
    nad.send('mute', !nad.state.amp.mute);
  },
  volumeUp: function() {
    nad.send('volume', '+');
  },
  volumeDown: function() {
    nad.send('volume', '-');
  },
  source: function(value) {
    nad.send('source', value);
  },
  ampModel: function() {
    nad.get('model', function (data) {
      nad.state.message = {request: {variable: 'model', value: '?'},
                           reply: data};
    });
  },
  speakerA: function() {
    nad.send('speakerA', !nad.state.amp.speakerA);
  },
  reload: function() {
    nad.get('power');
    nad.get('mute');
    nad.get('source');
    nad.get('speakerA');
  },
  toggleHelp: function() {
    nad.state.helpVisible = !nad.state.helpVisible;
    m.redraw();
  }
};

nad.bindKeys = function() {
  nad.keyBindings.forEach(function (kb) {
    if (typeof nad.state[kb.callback] !== 'function') {
      throw 'Invalid callback "' + kb.callback + '" for keybinding "' + kb.key + '"';
    }
    Mousetrap.bind(kb.key, nad.state[kb.callback]);
  });
};

nad.keyBindings = [
  {key: 'p', callback: 'power', description: 'Toggle power'},
  {key: 'm', callback: 'mute', description: 'Toggle mute'},
  {key: 's', callback: 'speakerA', description: 'Toggle headphones'},
  {key: 'i', callback: 'ampModel', description: 'Get amplifier model'},
  {key: 'r', callback: 'reload', description: 'Reload state from amplifier'},
  {key: '+', callback: 'volumeUp', description: 'Increase volume'},
  {key: '-', callback: 'volumeDown', description: 'Decrease volume'},
  {key: 'h', callback: 'toggleHelp',
   description: 'Togge list of keyboard shortcuts'}
];

nad.fmtCmd = function(variable, value) {
  var operator = '=';
  switch (value) {
  case '?':
  case '-':
  case '+':
    operator = value;
    value = '';
    break;
  case true:
    value = 'On';
    break;
  case false:
    value = 'Off';
    break;
  }
  switch (variable) {
  case 'power':
    variable = 'Power';
    break;
  case 'mute':
    variable = 'Mute';
    break;
  case 'speakerA':
    variable = 'SpeakerA';
    break;
  case 'model':
    variable = 'Model';
    break;
  case 'volume':
    variable = 'Volume';
    break;
  }
  return 'Main.' + [variable, value].join(operator);
};

nad.toValue = function(v) {
  if (v === true || v === false) {
    return v ? 'On' : 'Off';
  }
  return v;
};

nad.get = function(variable, callback) {
  m.request({method: 'GET', url: '/api/v1/state/' + variable})
    .then(function (data) {
      nad.state.amp[variable] = data[variable];
      return data;
    }, function (data) {
      nad.state.error = data;
    })
    .then(callback);
};

nad.send = function(variable, value) {
  var request = {value: nad.toValue(value)};
  m.request({method: 'PATCH', url: '/api/v1/state/' + variable, data: request})
    .then(function (data) {
      Object.assign(nad.state.amp, data);
      nad.state.error = {};
      nad.state.message = {request: {variable: variable, value: value},
                           reply: data};
    }, function (data) {
      nad.state.error = data;
    });
};

nad.console = function() {
  var text;
  if (Object.keys(nad.state.message).length === 0) {
    text = ['These go to eleven!'];
  } else {
    var variable = nad.state.message.request.variable;
    text = ['sent:     ' + nad.fmtCmd(nad.state.message.request.variable, nad.state.message.request.value),
            'received: ' + nad.fmtCmd(nad.state.message.request.variable, nad.state.message.reply[variable])];
  }
  return m('pre.console', text.join('\n'));
};

nad.onoff = function(options) {
  var amp = nad.state.amp;
  if (!amp.hasOwnProperty(options.type)) {
    throw 'Unknown type: ' + options.type;
  }
  var isOn = amp[options.type];
  var active = options.invert ? !isOn : isOn;
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg' + (active ? ' active' : ''),
    onclick: options.onclick
  }, options.icon);
};

nad.volume = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg',
    onclick: options.onclick
  }, options.icon);
};

nad.source = function() {
  var sources = ['CD', 'Tuner', 'Video', 'Disc/MDC', 'Tape2', 'Aux'];
  var amp = nad.state.amp;
  return m('select.form-control', {
    onchange: m.withAttr('value', nad.state.source)
  }, sources.map(function(src) {
    var val = src.toUpperCase();
    var selected = amp.source === val ? 'selected' : '';
    return m('option', {value: val, selected: selected}, src);
  }));
};

nad.reloadState = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default',
    onclick: nad.state.reload
  }, options.icon);
};

nad.amp = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg',
    onclick: nad.state.ampModel
  }, options.icon);
};

nad.error = function() {
  var e = nad.state.error;
  var isError = Object.keys(e).length !== 0;
  var text = isError ? e.message + ' (' + e.status + ')' : '';
  var cls = 'alert-danger' + (isError ? '' : ' hidden');
  return m('div.alert', {class: cls, role: 'alert'}, [
    m('strong', 'Error! '), text
  ]);
};

nad.help = function() {
  if (!nad.state.helpVisible) {
    return m('p.text-muted', 'Tip: Press ', m('code', 'h'),
             ' to display keyboard shortcuts');
  }
  var rows = nad.keyBindings.map(function (kb) {
    return m('tr', [
      m('td', m('center', m('code', kb.key))),
      m('td', kb.description)
    ]);
  });
  return m('table.table',
           m('thead', m('tr', [
             m('th', 'Key binding'),
             m('th', 'Description')
           ])),
           m('tbody', rows)
          );
};

nad.oninit = nad.bindKeys;

nad.view = function() {
  return m('div.container', [
    m('div.row', [
      m('div.col-md-4', m('h1', [
        m('span', {class: 'glyphicon glyphicon-signal'}), ' amp remote'
      ]))
    ]),
    m('div.row', [
      m('div.col-md-4', nad.error())
    ]),
    m('div.row', [
      m('div.col-md-4', [
        nad.console()
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.power,
          type: 'power',
          icon: m('span', {class: 'glyphicon glyphicon-off'})
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.mute,
          type: 'mute',
          icon: m('span', {class: 'glyphicon glyphicon-volume-off'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.volume({
          onclick: nad.state.volumeUp,
          icon: m('span', {class: 'glyphicon glyphicon-volume-up'})
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.volume({
          onclick: nad.state.volumeDown,
          icon: m('span', {class: 'glyphicon glyphicon-volume-down'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.speakerA,
          type: 'speakerA',
          icon: m('span', {class: 'glyphicon glyphicon-headphones'}),
          invert: true
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.amp({
          icon: m('span', {class: 'glyphicon glyphicon-info-sign'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, nad.source()),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.reloadState({
          icon: m('span', {class: 'glyphicon glyphicon-refresh'})
        })
      ])
    ]),
    m('div.row', m('div.col-md-4', {class: 'top-spacing'}, nad.help()))
  ]);
};

m.mount(document.getElementById('nad-remote'), nad);
<commit_msg>Send on/off as boolean<commit_after>var nad = nad || {};

nad.state = {
  // Initial amplifier state
  amp: {
    power: false,
    speakerA: true,
    speakerB: false,
    mute: false,
    source: 'CD',
    model: ''
  },
  message: {},
  error: {},
  helpVisible: false,
  power: function() {
    nad.send('power', !nad.state.amp.power);
  },
  mute: function() {
    nad.send('mute', !nad.state.amp.mute);
  },
  volumeUp: function() {
    nad.send('volume', '+');
  },
  volumeDown: function() {
    nad.send('volume', '-');
  },
  source: function(value) {
    nad.send('source', value);
  },
  ampModel: function() {
    nad.get('model', function (data) {
      nad.state.message = {request: {variable: 'model', value: '?'},
                           reply: data};
    });
  },
  speakerA: function() {
    nad.send('speakerA', !nad.state.amp.speakerA);
  },
  reload: function() {
    nad.get('power');
    nad.get('mute');
    nad.get('source');
    nad.get('speakerA');
  },
  toggleHelp: function() {
    nad.state.helpVisible = !nad.state.helpVisible;
    m.redraw();
  }
};

nad.bindKeys = function() {
  nad.keyBindings.forEach(function (kb) {
    if (typeof nad.state[kb.callback] !== 'function') {
      throw 'Invalid callback "' + kb.callback + '" for keybinding "' + kb.key + '"';
    }
    Mousetrap.bind(kb.key, nad.state[kb.callback]);
  });
};

nad.keyBindings = [
  {key: 'p', callback: 'power', description: 'Toggle power'},
  {key: 'm', callback: 'mute', description: 'Toggle mute'},
  {key: 's', callback: 'speakerA', description: 'Toggle headphones'},
  {key: 'i', callback: 'ampModel', description: 'Get amplifier model'},
  {key: 'r', callback: 'reload', description: 'Reload state from amplifier'},
  {key: '+', callback: 'volumeUp', description: 'Increase volume'},
  {key: '-', callback: 'volumeDown', description: 'Decrease volume'},
  {key: 'h', callback: 'toggleHelp',
   description: 'Togge list of keyboard shortcuts'}
];

nad.fmtCmd = function(variable, value) {
  var operator = '=';
  switch (value) {
  case '?':
  case '-':
  case '+':
    operator = value;
    value = '';
    break;
  case true:
    value = 'On';
    break;
  case false:
    value = 'Off';
    break;
  }
  switch (variable) {
  case 'speakerA':
    variable = 'SpeakerA';
    break;
  default:
    variable = variable.charAt(0).toUpperCase() + variable.slice(1);
    break;
  }
  return 'Main.' + [variable, value].join(operator);
};

nad.get = function(variable, callback) {
  m.request({method: 'GET', url: '/api/v1/state/' + variable})
    .then(function (data) {
      nad.state.amp[variable] = data[variable];
      return data;
    }, function (data) {
      nad.state.error = data;
    })
    .then(callback);
};

nad.send = function(variable, value) {
  m.request({method: 'PATCH', url: '/api/v1/state/' + variable, data: {value: value}})
    .then(function (data) {
      Object.assign(nad.state.amp, data);
      nad.state.error = {};
      nad.state.message = {request: {variable: variable, value: value},
                           reply: data};
    }, function (data) {
      nad.state.error = data;
    });
};

nad.console = function() {
  var text;
  if (Object.keys(nad.state.message).length === 0) {
    text = ['These go to eleven!'];
  } else {
    var variable = nad.state.message.request.variable;
    text = ['sent:     ' + nad.fmtCmd(nad.state.message.request.variable, nad.state.message.request.value),
            'received: ' + nad.fmtCmd(nad.state.message.request.variable, nad.state.message.reply[variable])];
  }
  return m('pre.console', text.join('\n'));
};

nad.onoff = function(options) {
  var amp = nad.state.amp;
  if (!amp.hasOwnProperty(options.type)) {
    throw 'Unknown type: ' + options.type;
  }
  var isOn = amp[options.type];
  var active = options.invert ? !isOn : isOn;
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg' + (active ? ' active' : ''),
    onclick: options.onclick
  }, options.icon);
};

nad.volume = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg',
    onclick: options.onclick
  }, options.icon);
};

nad.source = function() {
  var sources = ['CD', 'Tuner', 'Video', 'Disc/MDC', 'Tape2', 'Aux'];
  var amp = nad.state.amp;
  return m('select.form-control', {
    onchange: m.withAttr('value', nad.state.source)
  }, sources.map(function(src) {
    var val = src.toUpperCase();
    var selected = amp.source === val ? 'selected' : '';
    return m('option', {value: val, selected: selected}, src);
  }));
};

nad.reloadState = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default',
    onclick: nad.state.reload
  }, options.icon);
};

nad.amp = function(options) {
  return m('button[type=button]', {
    class: 'btn btn-default btn-lg',
    onclick: nad.state.ampModel
  }, options.icon);
};

nad.error = function() {
  var e = nad.state.error;
  var isError = Object.keys(e).length !== 0;
  var text = isError ? e.message + ' (' + e.status + ')' : '';
  var cls = 'alert-danger' + (isError ? '' : ' hidden');
  return m('div.alert', {class: cls, role: 'alert'}, [
    m('strong', 'Error! '), text
  ]);
};

nad.help = function() {
  if (!nad.state.helpVisible) {
    return m('p.text-muted', 'Tip: Press ', m('code', 'h'),
             ' to display keyboard shortcuts');
  }
  var rows = nad.keyBindings.map(function (kb) {
    return m('tr', [
      m('td', m('center', m('code', kb.key))),
      m('td', kb.description)
    ]);
  });
  return m('table.table',
           m('thead', m('tr', [
             m('th', 'Key binding'),
             m('th', 'Description')
           ])),
           m('tbody', rows)
          );
};

nad.oninit = nad.bindKeys;

nad.view = function() {
  return m('div.container', [
    m('div.row', [
      m('div.col-md-4', m('h1', [
        m('span', {class: 'glyphicon glyphicon-signal'}), ' amp remote'
      ]))
    ]),
    m('div.row', [
      m('div.col-md-4', nad.error())
    ]),
    m('div.row', [
      m('div.col-md-4', [
        nad.console()
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.power,
          type: 'power',
          icon: m('span', {class: 'glyphicon glyphicon-off'})
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.mute,
          type: 'mute',
          icon: m('span', {class: 'glyphicon glyphicon-volume-off'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.volume({
          onclick: nad.state.volumeUp,
          icon: m('span', {class: 'glyphicon glyphicon-volume-up'})
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.volume({
          onclick: nad.state.volumeDown,
          icon: m('span', {class: 'glyphicon glyphicon-volume-down'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.onoff({
          onclick: nad.state.speakerA,
          type: 'speakerA',
          icon: m('span', {class: 'glyphicon glyphicon-headphones'}),
          invert: true
        })
      ]),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.amp({
          icon: m('span', {class: 'glyphicon glyphicon-info-sign'})
        })
      ])
    ]),
    m('div.row', [
      m('div.col-md-2', {class: 'top-spacing'}, nad.source()),
      m('div.col-md-2', {class: 'top-spacing'}, [
        nad.reloadState({
          icon: m('span', {class: 'glyphicon glyphicon-refresh'})
        })
      ])
    ]),
    m('div.row', m('div.col-md-4', {class: 'top-spacing'}, nad.help()))
  ]);
};

m.mount(document.getElementById('nad-remote'), nad);
<|endoftext|>