<commit_before>// self.importScripts("./common.js")

self.addEventListener('fetch', function (event) {
    // console.log("sw:fetch listener event",event, event.request.url)
})

self.addEventListener('install', function (event) {
    event.waitUntil(self.skipWaiting())
})

self.addEventListener('notificationclick', function (event) {
    // console.log("notification click", event)
    // event.notification.close();

    event.waitUntil(
        clients
            .matchAll({type: "window"})
            .then(function (clientList) {
                for (var i = 0; i < clientList.length; i++) {
                    const client = clientList[i]
                    postMessage(client, event)
                    if (client.focus) {
                        return client.focus();
                    }
                }
                if (clients.openWindow) {
                    return clients
                    // .openWindow(url)
                        .openWindow("/")
                        .then(function (client) {
                            setTimeout(function () {
                                postMessage(client, event)
                            }, 2000)
                        })
                }
            })
    );

}, false);

function postMessage(client, event) {
    console.log("posting notification-clicked from event", event)
    client.postMessage({
        type: "notification-clicked",
        action: event.action,
        data: event.notification.data
    })
}

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
    'messagingSenderId': '49437522774'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a sevice worker
//   `messaging.setBackgroundMessageHandler` handler.
// messaging.onMessage(function(payload) {
//     console.log("Message received. ", payload);
//     // ...
// });

messaging.setBackgroundMessageHandler(function (payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Hurray Custom notification';
    const notificationOptions = {
        requiresInteraction: true,
        sticky: true,
        renotify: true,
        tag: payload.data.id,
        vibrate: [500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450, 110, 200, 110, 170, 40, 500],
        sound: "/alarm.ogg",
        actions: [
            {title: "Mark Done", action: "mark-done"},
            {title: "Snooze", action: "snooze"},
        ],
        body: 'setting body: received data: ' + JSON.stringify(payload.data),
        data: payload.data
    };

    return self.registration.showNotification(notificationTitle,
        notificationOptions);
});
,
<commit_msg>using bkg message to display notification<commit_after>// self.importScripts("./common.js")

self.addEventListener('fetch', function (event) {
    // console.log("sw:fetch listener event",event, event.request.url)
})

self.addEventListener('install', function (event) {
    event.waitUntil(self.skipWaiting())
})

self.addEventListener('notificationclick', function (event) {
    // console.log("notification click", event)
    // event.notification.close();

    event.waitUntil(
        clients
            .matchAll({type: "window"})
            .then(function (clientList) {
                for (var i = 0; i < clientList.length; i++) {
                    const client = clientList[i]
                    postMessage(client, event)
                    if (client.focus) {
                        return client.focus();
                    }
                }
                if (clients.openWindow) {
                    return clients
                    // .openWindow(url)
                        .openWindow("/")
                        .then(function (client) {
                            setTimeout(function () {
                                postMessage(client, event)
                            }, 2000)
                        })
                }
            })
    );

}, false);

function postMessage(client, event) {
    console.log("posting notification-clicked from event", event)
    client.postMessage({
        type: "notification-clicked",
        action: event.action,
        data: event.notification.data
    })
}

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
    'messagingSenderId': '49437522774'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a sevice worker
//   `messaging.setBackgroundMessageHandler` handler.
// messaging.onMessage(function(payload) {
//     console.log("Message received. ", payload);
//     // ...
// });

// self.addEventListener('push', function(event) {
//     console.log('[Service Worker] Push Received.');
//     console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
//
//     const title = 'Push Codelab';
//     const options = {
//         body: 'Yay it works.',
//         sound: "/alarm.ogg",
//         timestamp:0
//     };
//
//     event.waitUntil(self.registration.showNotification(title, options));
// });

messaging.setBackgroundMessageHandler(function (payload) {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    // Customize notification here
    const notificationTitle = 'Hurray Custom notification';
    const notificationOptions = {
        requiresInteraction: true,
        sticky: true,
        renotify: true,
        tag: payload.data.id,
        vibrate: [500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450, 110, 200, 110, 170, 40, 500],
        sound: "/alarm.ogg",
        actions: [
            {title: "Mark Done", action: "mark-done"},
            {title: "Snooze", action: "snooze"},
        ],
        body: 'setting body: received data: ' + JSON.stringify(payload.data),
        data: payload.data
    };

    return self.registration.showNotification(notificationTitle,
        notificationOptions);
});
<|endoftext|>


<commit_before>"use strict";

const PouchDB = require("pouchdb-browser")
const _ = require("ramda")

// PouchDB.debug.enable("*")
PouchDB.debug.disable()
PouchDB.plugin(require('pouchdb-find'))
PouchDB.plugin(require('pouchdb-upsert'))

const removeNilValuedKeys = value =>
    _.when(_.is(Object),
        _.compose(_.map(removeNilValuedKeys), _.reject(_.isNil))
    )(value)

export default async (dbName, indices = []) => {
    const db = new PouchDB(dbName)
    let syncTracker = null;

    function createIndex(index) {
        return db.createIndex(index)
        // .then(console.log)
    }

    async function deleteIndices() {
        const existingCustomIndices = _.filter(_.prop("ddoc"), (await db.getIndexes()).indexes)
        return await Promise.all(_.map(_.bind(db.deleteIndex, db), existingCustomIndices))
    }

    function bulkDocs(docs) {
        return db.bulkDocs(docs)
    }

    async function upsert(id, doc) {
        // console.log("upsert: doc", dbName, doc, id)
        //noinspection UnnecessaryLocalVariableJS
        const upsertResult = await db.upsert(id, oldDoc => {
            // console.log("upsert: oldDoc ", dbName, oldDoc, id)
            const newDoc = _.merge({_rev: oldDoc._rev}, doc)

            const areDocsSame = _.equals(
                removeNilValuedKeys(newDoc),
                removeNilValuedKeys(oldDoc),
            )

            if (areDocsSame) {
                console.log("upsert: ignoring update since docs are same: ", areDocsSame)
                return
            }
            console.log("upsert: adding new doc since docs are *not* same", areDocsSame)
            return doc
        })
        // console.log("upsert: result", upsertResult)
        return upsertResult
    }

    function startRemoteSync(remoteUrl = "http://localhost:12321", prefix = "", dbName_ = dbName) {
        const dbNameWithPrefix = `${prefix}${dbName_}`
        console.log(`starting sync for ${dbNameWithPrefix}`)
        const remoteURL = `${remoteUrl}/${dbNameWithPrefix}`;
        if (syncTracker) {
            syncTracker.cancel()
        }
        const tracker = db
            .sync(remoteURL,
                {live: true, retry: true},
                (error, result) => {
                    if (error) {
                        console.error(`PLDB: sync err ${dbName_}`, error)
                    } else {
                        console.info("PLDB: sync complete = ", result)
                    }

                })
        syncTracker = tracker
        return tracker
    }

    const find = async options => (await db.find(options)).docs

    function allDocs() {
        db.allDocs()
    }

    async function deleteAllDocs() {
        function remove(doc) {
            return db.put(_.merge(doc, {_deleted: true}))
        }

        return Promise.all(_.map(remove, await allDocs()))
    }

    // await deleteIndices();
    await Promise.all(_.map(createIndex, indices))

    function onChange(callback) {
        return db.changes({
            since: 'now',
            live: true,
            include_docs: true,
        }).on('change', function (change) {
            // console.log("change", change)
            callback(change.doc)
        }).on('complete', function (info) {
            // changes() was canceled
        }).on('error', function (err) {
            console.log(err);
        });
    }

    return {
        find,
        deleteAllDocs,
        bulkDocs,
        _db: db,
        upsert,
        deleteIndices,
        allDocs,
        startRemoteSync,
        findAll: () => find({selector: {"_id": {"$ne": null}}}),
        onChange,
        changes: _.bind(db.changes, db),
        name: dbName
    }
}

<commit_msg>[fix] firebase sync infinite upsert bug<commit_after>"use strict";

const PouchDB = require("pouchdb-browser")
const _ = require("ramda")

// PouchDB.debug.enable("*")
PouchDB.debug.disable()
PouchDB.plugin(require('pouchdb-find'))
PouchDB.plugin(require('pouchdb-upsert'))

const removeNilValuedKeys = value =>
    _.when(_.is(Object),
        _.compose(_.map(removeNilValuedKeys), _.reject(_.isNil))
    )(value)

export default async (dbName, indices = []) => {
    const db = new PouchDB(dbName)
    let syncTracker = null;

    function createIndex(index) {
        return db.createIndex(index)
        // .then(console.log)
    }

    async function deleteIndices() {
        const existingCustomIndices = _.filter(_.prop("ddoc"), (await db.getIndexes()).indexes)
        return await Promise.all(_.map(_.bind(db.deleteIndex, db), existingCustomIndices))
    }

    function bulkDocs(docs) {
        return db.bulkDocs(docs)
    }

    async function upsert(id, doc) {
        //noinspection UnnecessaryLocalVariableJS
        const upsertResult = await db.upsert(id, oldDoc => {

            const cleanNewDoc = ((doc, oldDoc) => {
                const mergeOldDocRev = _.merge(_.__, {_rev: oldDoc._rev})

                const isRevEmpty = _.propSatisfies(_.isEmpty, "_rev")

                return _
                    .compose(
                        _.when(isRevEmpty, mergeOldDocRev),
                        removeNilValuedKeys
                    )(doc)

            })(doc, oldDoc)

            const cleanOldDoc = removeNilValuedKeys(oldDoc)

            const areDocsSame = _.equals(cleanNewDoc, cleanOldDoc)

            console.log("doc diff", _.merge(cleanOldDoc, {}), _.merge(cleanNewDoc, {}))

            if (areDocsSame) {
                console.log("upsert: ignoring update since docs are same: ", areDocsSame)
                return
            }
            console.log("upsert: adding new doc since docs are *not* same", cleanNewDoc, cleanOldDoc)
            return cleanNewDoc
        })
        console.log("upsert: result", upsertResult)
        return upsertResult
    }

    function startRemoteSync(remoteUrl = "http://localhost:12321", prefix = "", dbName_ = dbName) {
        const dbNameWithPrefix = `${prefix}${dbName_}`
        console.log(`starting sync for ${dbNameWithPrefix}`)
        const remoteURL = `${remoteUrl}/${dbNameWithPrefix}`;
        if (syncTracker) {
            syncTracker.cancel()
        }
        const tracker = db
            .sync(remoteURL,
                {live: true, retry: true},
                (error, result) => {
                    if (error) {
                        console.error(`PLDB: sync err ${dbName_}`, error)
                    } else {
                        console.info("PLDB: sync complete = ", result)
                    }

                })
        syncTracker = tracker
        return tracker
    }

    const find = async options => (await db.find(options)).docs

    function allDocs() {
        db.allDocs()
    }

    async function deleteAllDocs() {
        function remove(doc) {
            return db.put(_.merge(doc, {_deleted: true}))
        }

        return Promise.all(_.map(remove, await allDocs()))
    }

    // await deleteIndices();
    await Promise.all(_.map(createIndex, indices))

    function onChange(callback) {
        return db.changes({
            since: 'now',
            live: true,
            include_docs: true,
        }).on('change', function (change) {
            // console.log("change", change)
            callback(change.doc)
        }).on('complete', function (info) {
            // changes() was canceled
        }).on('error', function (err) {
            console.log(err);
        });
    }

    return {
        find,
        deleteAllDocs,
        bulkDocs,
        _db: db,
        upsert,
        deleteIndices,
        allDocs,
        startRemoteSync,
        findAll: () => find({selector: {"_id": {"$ne": null}}}),
        onChange,
        changes: _.bind(db.changes, db),
        name: dbName
    }
}

<|endoftext|>


<commit_before>module.exports = '2017-07-09';
<commit_msg>bump version<commit_after>module.exports = '2017-07-10';
<|endoftext|>


<commit_before>/**
	A library of UI widgets designed for use alongside Enyo core in the
	development of smart TV applications.

	@namespace moon
*/
if (enyo && enyo.version) {
	enyo.version.moonstone = "2.5.3-zzz.4";
}
<commit_msg>Update version string to 2.5.3-zzz.5<commit_after>/**
	A library of UI widgets designed for use alongside Enyo core in the
	development of smart TV applications.

	@namespace moon
*/
if (enyo && enyo.version) {
	enyo.version.moonstone = "2.5.3-zzz.5";
}
<|endoftext|>


<commit_before>module.exports = '2018-10-30';
<commit_msg>Bump version<commit_after>module.exports = '2018-12-11';
<|endoftext|>


<commit_before>"use strict";
exports.osmbc_version = "1.8.1";

<commit_msg>Version.<commit_after>"use strict";
exports.osmbc_version = "1.8.1a";

<|endoftext|>


<commit_before>"use strict";

const _ = require("ramda")

const cryptoRandomString = require('crypto-random-string');

require("./pcss/main.pcss")

const sound = require("./sound")
const $ = require("jquery")
window.jQuery = $
require("./jquery.trap")
require("jquery-ui/ui/position")
const Notifications = require("./notifications")
import Fire from "./fire"
import DB from "./pouchdb-wrapper"

const developmentMode = IS_DEVELOPMENT_ENV
const pkg = packageJSON

window.addEventListener('WebComponentsReady', () => {
    boot().catch(console.error)
});

//noinspection JSUnresolvedVariable
const env = ENV

const version = env["npm_version"]

async function boot() {
    const deviceId = getOrCreateDeviceId()
    const $elm = $("#elm-app-container")
    $elm.trap();

    $elm.on("keydown", `.entity-list`, e => {
        // console.log(e.keyCode, e.key, e.target, e);

        if (e.target.tagName !== "PAPER-INPUT") {
            // prevent document scrolling
            if (e.key === " "/*space: 32*/ && e.target.tagName !== "PAPER-INPUT") {
                e.preventDefault()
            }
            else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
                e.preventDefault()
            }
        }

    })

    $elm.get(0).addEventListener("keydown", e => {
        const $closest = $(e.target).closest("[data-prevent-default-keys]")
        if ($closest.length === 0)return
        const preventDefaultKeys =
            $closest.data("prevent-default-keys").split(",")
        // console.log(e.keyCode, e.key, e, preventDefaultKey);

        if (_.contains(e.key)(preventDefaultKeys)) {
            e.preventDefault()
        }
    }, true)

    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }
    const allDocsMap = _.map(db => db.findAll())(dbMap)

    const flags = {
        now: Date.now(),
        encodedTodoList: await allDocsMap["todo-db"],
        encodedProjectList: await allDocsMap["project-db"],
        encodedContextList: await allDocsMap["context-db"],
        // encodedTodoList: [],
        // encodedProjectList: [],
        // encodedContextList: [],
        pouchDBRemoteSyncURI: localStorage.getItem("pouchdb.remote-sync-uri") || "",
        developmentMode: developmentMode,
        appVersion: pkg.version,
        deviceId
    }
    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("elm-app-container"), flags)

    Fire.setup(app, _.values(dbMap))

    _.mapObjIndexed((db, name) => db.onChange(
        (doc) => {
            // console.log(`app.ports["pouchDBChanges"]:`,name, ":", doc.name || doc.text)
            return app.ports["pouchDBChanges"].send([name, doc])
        }
    ))(dbMap)

    app.ports["syncWithRemotePouch"].subscribe(async (uri) => {
        localStorage.setItem("pouchdb.remote-sync-uri", uri)
        _.map(db => db.startRemoteSync(uri, "sgtd2-"))(dbMap)
    })

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        dbMap[dbName].upsert(id, doc).catch(console.error)
    });

    Notifications.setup(app).catch(console.error)

    app.ports["focusSelector"].subscribe((selector) => {
        // setTimeout(() => {
        requestAnimationFrame(() => {
            // note - we blur here so that view scrolls to element if it already had focus
            $(selector).blur().focus()
        })
        // }, 0)
    })

    app.ports["focusSelectorIfNoFocus"].subscribe((selector) => {
        const $focus = $(":focus, [focused]")
        // console.log($focus, $focus.length)
        if ($focus.length === 0) {
            $(selector).focus()
        }
    })

    app.ports["positionDropdown"].subscribe(([myId, ofId]) => {
        requestAnimationFrame(() => {
            $("#" + myId).position({
                my: "right top",
                at: "right top",
                of: "#" + ofId,
                within: "#main-view",
                collision: "flipfit"
            }).find(`[tabindex="0"]`).focus()
        })
    })

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    // console.log(toFocus.inputElement, toFocus.$.input)
                    toFocus.inputElement.focus()
                    // toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

function getOrCreateDeviceId() {
    let deviceId = localStorage.getItem("device-id")
    if (!deviceId) {
        deviceId = cryptoRandomString(64)
        localStorage.setItem("device-id", deviceId)
    }
    return deviceId
}
<commit_msg>update run scripts<commit_after>"use strict";

const _ = require("ramda")

const cryptoRandomString = require('crypto-random-string');

require("./pcss/main.pcss")

const sound = require("./sound")
const $ = require("jquery")
window.jQuery = $
require("./jquery.trap")
require("jquery-ui/ui/position")
const Notifications = require("./notifications")
import Fire from "./fire"
import DB from "./pouchdb-wrapper"

const developmentMode = IS_DEVELOPMENT_ENV

window.addEventListener('WebComponentsReady', () => {
    boot().catch(console.error)
});

//noinspection JSUnresolvedVariable
const env = ENV
console.log(env)

const npmPackageVersion = env["npm_package_version"]

async function boot() {
    const deviceId = getOrCreateDeviceId()
    const $elm = $("#elm-app-container")
    $elm.trap();

    $elm.on("keydown", `.entity-list`, e => {
        // console.log(e.keyCode, e.key, e.target, e);

        if (e.target.tagName !== "PAPER-INPUT") {
            // prevent document scrolling
            if (e.key === " "/*space: 32*/ && e.target.tagName !== "PAPER-INPUT") {
                e.preventDefault()
            }
            else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
                e.preventDefault()
            }
        }

    })

    $elm.get(0).addEventListener("keydown", e => {
        const $closest = $(e.target).closest("[data-prevent-default-keys]")
        if ($closest.length === 0)return
        const preventDefaultKeys =
            $closest.data("prevent-default-keys").split(",")
        // console.log(e.keyCode, e.key, e, preventDefaultKey);

        if (_.contains(e.key)(preventDefaultKeys)) {
            e.preventDefault()
        }
    }, true)

    const dbMap = {
        "todo-db": await DB("todo-db"),
        "project-db": await DB("project-db"),
        "context-db": await DB("context-db")
    }
    const allDocsMap = _.map(db => db.findAll())(dbMap)

    const flags = {
        now: Date.now(),
        encodedTodoList: await allDocsMap["todo-db"],
        encodedProjectList: await allDocsMap["project-db"],
        encodedContextList: await allDocsMap["context-db"],
        // encodedTodoList: [],
        // encodedProjectList: [],
        // encodedContextList: [],
        pouchDBRemoteSyncURI: localStorage.getItem("pouchdb.remote-sync-uri") || "",
        developmentMode: developmentMode,
        appVersion: npmPackageVersion,
        deviceId
    }
    const Elm = require("elm/Main.elm")
    const app = Elm["Main"]
        .embed(document.getElementById("elm-app-container"), flags)

    Fire.setup(app, _.values(dbMap))

    _.mapObjIndexed((db, name) => db.onChange(
        (doc) => {
            // console.log(`app.ports["pouchDBChanges"]:`,name, ":", doc.name || doc.text)
            return app.ports["pouchDBChanges"].send([name, doc])
        }
    ))(dbMap)

    app.ports["syncWithRemotePouch"].subscribe(async (uri) => {
        localStorage.setItem("pouchdb.remote-sync-uri", uri)
        _.map(db => db.startRemoteSync(uri, "sgtd2-"))(dbMap)
    })

    app.ports["pouchDBUpsert"].subscribe(async ([dbName, id, doc]) => {
        dbMap[dbName].upsert(id, doc).catch(console.error)
    });

    Notifications.setup(app).catch(console.error)

    app.ports["focusSelector"].subscribe((selector) => {
        // setTimeout(() => {
        requestAnimationFrame(() => {
            // note - we blur here so that view scrolls to element if it already had focus
            $(selector).blur().focus()
        })
        // }, 0)
    })

    app.ports["focusSelectorIfNoFocus"].subscribe((selector) => {
        const $focus = $(":focus, [focused]")
        // console.log($focus, $focus.length)
        if ($focus.length === 0) {
            $(selector).focus()
        }
    })

    app.ports["positionDropdown"].subscribe(([myId, ofId]) => {
        requestAnimationFrame(() => {
            $("#" + myId).position({
                my: "right top",
                at: "right top",
                of: "#" + ofId,
                within: "#main-view",
                collision: "flipfit"
            }).find(`[tabindex="0"]`).focus()
        })
    })

    app.ports["focusPaperInput"].subscribe((selector) => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const toFocus = document.querySelector(selector)
                // console.log("toFocus", toFocus, document.activeElement)
                if (toFocus && document.activeElement !== toFocus) {
                    toFocus.focus()
                } else {
                    // console.log("not focusing")
                }
                if (toFocus) {
                    // console.log(toFocus.inputElement, toFocus.$.input)
                    toFocus.inputElement.focus()
                    // toFocus.$.input.focus()
                }
            })
        }, 0)
    })

    app.ports["startAlarm"].subscribe(() => {
        sound.start()
    })

    app.ports["stopAlarm"].subscribe(() => {
        sound.stop()
    })
}

function getOrCreateDeviceId() {
    let deviceId = localStorage.getItem("device-id")
    if (!deviceId) {
        deviceId = cryptoRandomString(64)
        localStorage.setItem("device-id", deviceId)
    }
    return deviceId
}
<|endoftext|>


<commit_before>'use strict';

WaveSurfer.WebAudio = {
    init: function (params) {
        this.ac = params.AudioContext ||
            new (window.AudioContext || window.webkitAudioContext);

        this.createScriptNode();
        this.createVolumeNode();
    },

    createScriptNode: function () {
        var my = this;
        this.scriptNode = this.ac.createJavaScriptNode(256);
        this.scriptNode.connect(this.ac.destination);
        this.scriptNode.onaudioprocess = function () {
            if (my.source && !my.isPaused()) {
                my.fireEvent('audioprocess', my.getPlayedPercents());
            }
        };
    },

    /**
     * Create the gain node needed to control the playback volume.
     */
    createVolumeNode: function () {
        // Create gain node using the AudioContext
        this.gainNode = this.ac.createGainNode();

        // Add the gain node to the graph
        this.gainNode.connect(this.ac.destination);
    },

    refreshBufferSource: function () {
        this.source && this.source.disconnect();
        this.source = this.ac.createBufferSource();
        this.source.buffer = this.buffer;
        this.source.connect(this.scriptNode);
        this.source.connect(this.ac.destination);
        // Wiring up the voume node
        this.source.connect(this.gainNode);
    },

    setBuffer: function (buffer) {
        this.lastPause = 0;
        this.lastStart = 0;
        this.startTime = 0;
        this.buffer = buffer;
        this.refreshBufferSource();
    },

    /**
     * Decodes binary data and creates buffer source.
     *
     * @param {ArrayBuffer} arraybuffer Audio data.
     */
    loadBuffer: function (arraybuffer, cb, errb) {
        var my = this;

        if (this.source) {
            this.pause();
        }

        this.ac.decodeAudioData(
            arraybuffer,
            function (buffer) {
                my.setBuffer(buffer);
                cb && cb(buffer);
            },
            function () {
                //console.error('Error decoding audio buffer');
                errb && errb();
            }
        );
    },

    isPaused: function () {
        return this.source.PLAYING_STATE != this.source.playbackState;
    },

    getDuration: function () {
        return this.buffer.duration;
    },

    /**
     * Plays the loaded audio region.
     *
     * @param {Number} start Start offset in seconds,
     * relative to the beginning of the track.
     *
     * @param {Number} end End offset in seconds,
     * relative to the beginning of the track.
     */
    play: function (start, end) {
        this.refreshBufferSource();

        if (null == start) { start = this.getCurrentTime(); }
        if (null == end) {
            end = this.getDuration();
        } else {
            this.lastPause = end;
        }

        this.lastStart = start;
        this.startTime = this.ac.currentTime;

        this.source.noteGrainOn(0, start, end - start);
    },

    /**
     * Pauses the loaded audio.
     */
    pause: function () {
        this.lastPause = this.lastStart + (this.ac.currentTime - this.startTime);
        this.source.noteOff(0);
    },

    /**
     * @returns {Float32Array} Array of peaks.
     */
    getPeaks: function (length, sampleStep) {
        sampleStep = sampleStep || 128;
        var buffer = this.buffer;
        var k = buffer.length / length;
        var peaks = new Float32Array(length);

        for (var c = 0; c < buffer.numberOfChannels; c++) {
            var chan = buffer.getChannelData(c);

            for (var i = 0; i < length; i++) {
                var peak = -Infinity;
                var start = ~~(i * k);
                var end = (i + 1) * k;
                for (var j = start; j < end; j += sampleStep) {
                    var val = chan[j];
                    if (val > peak) {
                        peak = val;
                    } else if (-val > peak) {
                        peak = -val;
                    }
                }

                if (c > 0) {
                    peaks[i] += peak;
                } else {
                    peaks[i] = peak;
                }
            }
        }

        return peaks;
    },

    getMaxPeak: function () {
        /* Peaks are sums of absolute peak values from each channel. */
        return this.buffer.numberOfChannels * 1.0;
    },

    getPlayedPercents: function () {
        var duration = this.getDuration();
        return duration > 0 ? this.getCurrentTime() / duration : 0;
    },

    getCurrentTime: function () {
        if (this.isPaused()) {
            return this.lastPause;
        }
        return this.lastStart + (this.ac.currentTime - this.startTime);
    }
};

WaveSurfer.util.extend(WaveSurfer.WebAudio, Observer);
<commit_msg>Added method for setting the gain on the WebAudio object. Takes a value between -1 and 1.<commit_after>'use strict';

WaveSurfer.WebAudio = {
    init: function (params) {
        this.ac = params.AudioContext ||
            new (window.AudioContext || window.webkitAudioContext);

        this.createScriptNode();
        this.createVolumeNode();
    },

    createScriptNode: function () {
        var my = this;
        this.scriptNode = this.ac.createJavaScriptNode(256);
        this.scriptNode.connect(this.ac.destination);
        this.scriptNode.onaudioprocess = function () {
            if (my.source && !my.isPaused()) {
                my.fireEvent('audioprocess', my.getPlayedPercents());
            }
        };
    },

    /**
     * Create the gain node needed to control the playback volume.
     */
    createVolumeNode: function () {
        // Create gain node using the AudioContext
        this.gainNode = this.ac.createGainNode();

        // Add the gain node to the graph
        this.gainNode.connect(this.ac.destination);
    },

    /**
     * Set the gain to a new value.
     *
     * @param  newGain  The new gain, a value between -1 and 1. -1 being no gain and 1 being maxium gain.
     */
    setVolume: function(newGain) {
        this.gainNode.gain.value = newGain;
    },

    refreshBufferSource: function () {
        this.source && this.source.disconnect();
        this.source = this.ac.createBufferSource();
        this.source.buffer = this.buffer;
        this.source.connect(this.scriptNode);
        this.source.connect(this.ac.destination);
        // Wiring up the voume node
        this.source.connect(this.gainNode);
    },

    setBuffer: function (buffer) {
        this.lastPause = 0;
        this.lastStart = 0;
        this.startTime = 0;
        this.buffer = buffer;
        this.refreshBufferSource();
    },

    /**
     * Decodes binary data and creates buffer source.
     *
     * @param {ArrayBuffer} arraybuffer Audio data.
     */
    loadBuffer: function (arraybuffer, cb, errb) {
        var my = this;

        if (this.source) {
            this.pause();
        }

        this.ac.decodeAudioData(
            arraybuffer,
            function (buffer) {
                my.setBuffer(buffer);
                cb && cb(buffer);
            },
            function () {
                //console.error('Error decoding audio buffer');
                errb && errb();
            }
        );
    },

    isPaused: function () {
        return this.source.PLAYING_STATE != this.source.playbackState;
    },

    getDuration: function () {
        return this.buffer.duration;
    },

    /**
     * Plays the loaded audio region.
     *
     * @param {Number} start Start offset in seconds,
     * relative to the beginning of the track.
     *
     * @param {Number} end End offset in seconds,
     * relative to the beginning of the track.
     */
    play: function (start, end) {
        this.refreshBufferSource();

        if (null == start) { start = this.getCurrentTime(); }
        if (null == end) {
            end = this.getDuration();
        } else {
            this.lastPause = end;
        }

        this.lastStart = start;
        this.startTime = this.ac.currentTime;

        this.source.noteGrainOn(0, start, end - start);
    },

    /**
     * Pauses the loaded audio.
     */
    pause: function () {
        this.lastPause = this.lastStart + (this.ac.currentTime - this.startTime);
        this.source.noteOff(0);
    },

    /**
     * @returns {Float32Array} Array of peaks.
     */
    getPeaks: function (length, sampleStep) {
        sampleStep = sampleStep || 128;
        var buffer = this.buffer;
        var k = buffer.length / length;
        var peaks = new Float32Array(length);

        for (var c = 0; c < buffer.numberOfChannels; c++) {
            var chan = buffer.getChannelData(c);

            for (var i = 0; i < length; i++) {
                var peak = -Infinity;
                var start = ~~(i * k);
                var end = (i + 1) * k;
                for (var j = start; j < end; j += sampleStep) {
                    var val = chan[j];
                    if (val > peak) {
                        peak = val;
                    } else if (-val > peak) {
                        peak = -val;
                    }
                }

                if (c > 0) {
                    peaks[i] += peak;
                } else {
                    peaks[i] = peak;
                }
            }
        }

        return peaks;
    },

    getMaxPeak: function () {
        /* Peaks are sums of absolute peak values from each channel. */
        return this.buffer.numberOfChannels * 1.0;
    },

    getPlayedPercents: function () {
        var duration = this.getDuration();
        return duration > 0 ? this.getCurrentTime() / duration : 0;
    },

    getCurrentTime: function () {
        if (this.isPaused()) {
            return this.lastPause;
        }
        return this.lastStart + (this.ac.currentTime - this.startTime);
    }
};

WaveSurfer.util.extend(WaveSurfer.WebAudio, Observer);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Pane from '../../Pane';
import IfPermission from '../../IfPermission';
import NotesForm from './NotesForm';
import NoteRenderer from './NoteRenderer';
import css from './Notes.css';

class Notes extends React.Component {
  static propTypes = {
    stripes: PropTypes.object.isRequired,
    onToggle: PropTypes.func.isRequired,
    onSubmit: PropTypes.func.isRequired,
    onDelete: PropTypes.func.isRequired,
    onUpdate: PropTypes.func.isRequired,
    link: PropTypes.string.isRequired,
    notes: PropTypes.arrayOf(PropTypes.object).isRequired,
  }

  render() {
    const notes = this.props.notes;
    const notesPaneTitle = (

        Notes

          {notes.length} Note{notes.length === 1 ? '' : 's'}

    );

    const noteList = notes.map((note, i) => (<NoteRenderer stripes={this.props.stripes} note={note} noteKey={note.id} onDelete={this.props.onDelete} onUpdate={this.props.onUpdate} key={`noteRender-${i}`} />));

    return (
      <Pane
        defaultWidth="20%"
        paneTitle={notesPaneTitle}
        dismissible
        onClose={this.props.onToggle}
      >
        <IfPermission perm="notes.collection.get">

            {noteList}

        </IfPermission>
        <IfPermission perm="notes.item.post">
          <NotesForm
            id="userform-addnote"
            initialValues={{ link: this.props.link }}
            form="newNote"
            onSubmit={this.props.onSubmit}
          />
        </IfPermission>
      </Pane>
    );
  }
}

export default Notes;
<commit_msg>lint: refactor as pure component<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Pane from '../../Pane';
import IfPermission from '../../IfPermission';
import NotesForm from './NotesForm';
import NoteRenderer from './NoteRenderer';
import css from './Notes.css';

const Notes = (props) => {
  const notes = props.notes;
  const notesPaneTitle = (

      Notes

        {notes.length} Note{notes.length === 1 ? '' : 's'}

  );

  const noteList = notes.map((note, i) => (<NoteRenderer stripes={props.stripes} note={note} noteKey={note.id} onDelete={props.onDelete} onUpdate={props.onUpdate} key={`noteRender-${i}`} />));

  return (
    <Pane
      defaultWidth="20%"
      paneTitle={notesPaneTitle}
      dismissible
      onClose={props.onToggle}
    >
      <IfPermission perm="notes.collection.get">

          {noteList}

      </IfPermission>
      <IfPermission perm="notes.item.post">
        <NotesForm
          id="userform-addnote"
          initialValues={{ link: props.link }}
          form="newNote"
          onSubmit={props.onSubmit}
        />
      </IfPermission>
    </Pane>
  );
};

Notes.propTypes = {
  stripes: PropTypes.object.isRequired,
  onToggle: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired,
  onDelete: PropTypes.func.isRequired,
  onUpdate: PropTypes.func.isRequired,
  link: PropTypes.string.isRequired,
  notes: PropTypes.arrayOf(PropTypes.object).isRequired,
};

export default Notes;
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn,
    http = require('http'),
    urlmod = require('url'),
    fs = require('fs');
var logLevel = 1;
var vidProc,
    player,
    vidProcLog = '',
    files = [],
    currFile = 0,
    playing = false;
var localAddress = '',
    remoteAddress = '',
    port = 3000,
    req,
    res;

// INIT ////////////////////////////////////

// standardized exit function
var exitFunction = function (code) {
    console.log('exiting vidcomm');
    if (code) console.log('exited with code '+code);
    if (vidProcLog.length) console.log(vidProcLog);
    // show cursor
    console.log('\033[?12l\033[?25h');
}

var echo = function (msg) {
    if (logLevel > 0) console.log(msg);
}

// HTTP SERVER /////////////////////////////

// find local ip address
var findLocalAddress = function () {
    echo('find local address');
    require('child_process').exec('ifconfig eth0 | grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'', function (error, stdout, stderr) {
        if (stdout.search(/192\.168\.1\.\d+/) !== -1) {
            localAddress = stdout;
            echo(localAddress);
            startServer();
        } else {
            echo('couldn\'t find local ip address. letting server down.');
            playNextVideo();
        }
    });
}

// start local http server
var startServer = function () {
    echo('start server');
    http.createServer(function (req_, res_) {
        req = req_;
        res = res_;
        if (req.method === 'GET') {
            req.on('close', function() { echo('connection closed'); });
            req.on('data', function(data) { /* void */ });
            req.on('end', function() { parseRequest(); });
        } else {
            echo('error: no accepted HTTP method');
            respond('no');
        }
    }).listen(port, localAddress);
    echo('server running at http://'+localAddress+':'+port);
    // query remote server for status
    queryRemote('playing');
}

// parse incoming http requests
var parseRequest = function () {
    echo('parse request');
    var url = urlmod.parse(req.url);
    if (url.href) {
        var cmd = url.href.slice(1);
        echo(cmd);
        if (cmd === 'playing') respond(isPlaying());
        else if (cmd === 'ended') playNextVideo();
        else respond('bad command');
    } else {
        echo('bad url '+url);
        respond('error: bad url');
    }
}

// respond to remote http requests
var respond = function (data) {
    echo('respond '+ data);
    var headers = {
        'Content-Length': Buffer.byteLength(data),
        'Content-Type': 'text/plain; charset=utf-8',
        'Expires': (new Date(Date.now)).toUTCString()
    }
    res.writeHead(200, headers);
    res.end(data);
}

// REMOTE QUERIES //////////////////////////

// query remote server
var queryRemote = function (query) {
    echo('query remote '+ query);
    var url = 'http://'+remoteAddress+':'+port+'/'+query;
    http.get(url, function(res_) {
        res_.on('data', function (data) { parseServerResponse(data) });
    }).on('error', function(e) {
        echo(e.code);
    });
}

// parse remote server's response to query
var parseServerResponse = function (data) {
    echo('parse server response '+ data);
    if (data == 'no') {
        // remote is not playing, play local file
        echo('remote is not playing, play local file');
        playNextVideo();
    } else {
        echo('remote is playing, wait for remote message, so, do nothing');
        // wait for remote message, so, do nothing
    }
}

// VIDEO ///////////////////////////////////

// establish video playing status
var isPlaying = function () {
    if (playing) return 'yes';
    else return 'no';
}

// play video files
var playNextVideo = function () {
    // next file to play
    var filename = files[currFile++];
    // if it doesn't exist, reset filename to the first one
    if (!filename) filename = files[0];
    // play only if the file exists
    if (!filename) {
        console.log('vidcomm playback ended');
        exitFunction();
        process.exit(0);
    } else {
        playing = true;
        echo('play video '+filename);
        vidProc = (player === 'omxplayer') ? spawn('omxplayer', ['-o', 'local', filename]) : spawn('mplayer', ['-vm', filename]);
        vidProc.stdout.on('data', function (data) { vidProcLog += data; });
        vidProc.stderr.on('data', function (data) { vidProcLog += data; });
        vidProc.on('exit', function (code) {
            playing = false;
            echo(player+' exited with code '+code);
            queryRemote('ended');
        });
    }
}

// PROCESS /////////////////////////////////

// parse process' incoming arguments
var parseArgv = function () {
    echo('parse argv');
    var conf;
    files = [];

    // parse arguments
    if (process.argv.length < 3) {
        // no arguments case
        conf = 'vidcomm.conf';
    } else {
        process.argv.forEach(function (val, idx, arr) {
            if (val.search(/^\.*[^\.]+\.conf$/) !== -1) {
                // configuration file case
                conf = val;
                // readConf();
            } else if (val.search(/^\.*[^\.]+\.(mp4|m4v|mov)$/) !== -1) {
                // video filename case
                files.push(val);
            } else if (val.search(/192\.168\.1\.\d+/) !== -1) {
                // remote address case
                remoteAddress = val;
            }
        });
    }

    // if a configuration file was given
    if (conf) {
        var data = fs.readFileSync(conf, {encoding:'utf-8'});
        if (data.length) {
            data.split('\n').forEach(function (val, idx, arr) {
                if (val.search(/^\.*[^\.]+\.(mp4|m4v|mov)$/) !== -1) {
                    // video filename case
                    files.push(val);
                } else if (val.search(/192\.168\.1\.\d+/) !== -1) {
                    // remote address case
                    remoteAddress = val;
                }
            });
        }
    }

    // if video files were given
    if (files.length) {
        // clear terminal, move cursor to top left and hide it
        // console.log('\033[2J\033\033[H\033[?25l');

        // if a remote server address was given
        if (remoteAddress.length) {
            findLocalAddress();
        } else {
            playNextVideo();
        }
    }

    // failsafe
    if (!conf && !files.length) {
        exitFunction();
        process.exit(1);
    }
}

// check whether other vidcomm process is running on the system
var checkForDuplicates = function () {
    echo('check for duplicates');
    require('child_process').exec('ps aux | grep '+player+' | grep -v grep', function (error, stdout, stderr) {
        if (stdout.length) {
            console.log('a video player is already running on this machine.');
            console.log(stdout);
            exitFunction();
            process.exit(1);
        } else {
            echo('vidcomm starting.');
            parseArgv();
        }
    });
}

// handle ctrl-C gracefully
process.on('SIGINT', function () {
    console.log(' ');
    exitFunction();
    process.exit(1);
});

// START ///////////////////////////////////

// check which player is available on the system
require('child_process').exec('which omxplayer', function (error, stdout, stderr) {
    if (stdout[0] !== '/') {
        require('child_process').exec('which mplayer', function (error, stdout, stderr) {
            if (stdout[0] !== '/') {
                console.log('no video player available.');
                exitFunction();
                process.exit(1);
            } else {
                player = 'mplayer';
                echo('player is '+player);
                checkForDuplicates();
            }
        });
    } else {
        player = 'omxplayer';
        echo('player is '+player);
        checkForDuplicates();
    }
});<commit_msg>testing network error status<commit_after>var spawn = require('child_process').spawn,
    http = require('http'),
    urlmod = require('url'),
    fs = require('fs');
var logLevel = 1;
var vidProc,
    player,
    vidProcLog = '',
    files = [],
    currFile = 0,
    playing = false;
var localAddress = '',
    remoteAddress = '',
    port = 3000,
    req,
    res;

// INIT ////////////////////////////////////

// standardized exit function
var exitFunction = function (code) {
    console.log('exiting vidcomm');
    if (code) console.log('exited with code '+code);
    if (vidProcLog.length) console.log(vidProcLog);
    // show cursor
    console.log('\033[?12l\033[?25h');
}

var echo = function (msg) {
    if (logLevel > 0) console.log(msg);
}

// HTTP SERVER /////////////////////////////

// find local ip address
var findLocalAddress = function () {
    echo('find local address');
    require('child_process').exec('ifconfig eth0 | grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'', function (error, stdout, stderr) {
        if (stdout.search(/192\.168\.1\.\d+/) !== -1) {
            localAddress = stdout;
            echo(localAddress);
            startServer();
        } else {
            echo('couldn\'t find local ip address. letting server down.');
            playNextVideo();
        }
    });
}

// start local http server
var startServer = function () {
    echo('start server');
    http.createServer(function (req_, res_) {
        req = req_;
        res = res_;
        if (req.method === 'GET') {
            req.on('close', function() { echo('connection closed'); });
            req.on('data', function(data) { /* void */ });
            req.on('end', function() { parseRequest(); });
        } else {
            echo('error: no accepted HTTP method');
            respond('no');
        }
    }).listen(port, localAddress);
    echo('server running at http://'+localAddress+':'+port);
    // query remote server for status
    queryRemote('playing');
}

// parse incoming http requests
var parseRequest = function () {
    echo('parse request');
    var url = urlmod.parse(req.url);
    if (url.href) {
        var cmd = url.href.slice(1);
        echo(cmd);
        if (cmd === 'playing') respond(isPlaying());
        else if (cmd === 'ended') playNextVideo();
        else respond('bad command');
    } else {
        echo('bad url '+url);
        respond('error: bad url');
    }
}

// respond to remote http requests
var respond = function (data) {
    echo('respond '+ data);
    var headers = {
        'Content-Length': Buffer.byteLength(data),
        'Content-Type': 'text/plain; charset=utf-8',
        'Expires': (new Date(Date.now)).toUTCString()
    }
    res.writeHead(200, headers);
    res.end(data);
}

// REMOTE QUERIES //////////////////////////

// query remote server
var queryRemote = function (query) {
    echo('query remote '+ query);
    var url = 'http://'+remoteAddress+':'+port+'/'+query;
    http.get(url, function(res_) {
        res_.on('data', function (data) { parseServerResponse(data) });
    }).on('error', function(e) {
        echo(e.code === 'ECONNREFUSED');
    });
}

// parse remote server's response to query
var parseServerResponse = function (data) {
    echo('parse server response '+ data);
    if (data == 'no') {
        // remote is not playing, play local file
        echo('remote is not playing, play local file');
        playNextVideo();
    } else {
        echo('remote is playing, wait for remote message, so, do nothing');
        // wait for remote message, so, do nothing
    }
}

// VIDEO ///////////////////////////////////

// establish video playing status
var isPlaying = function () {
    if (playing) return 'yes';
    else return 'no';
}

// play video files
var playNextVideo = function () {
    // next file to play
    var filename = files[currFile++];
    // if it doesn't exist, reset filename to the first one
    if (!filename) filename = files[0];
    // play only if the file exists
    if (!filename) {
        console.log('vidcomm playback ended');
        exitFunction();
        process.exit(0);
    } else {
        playing = true;
        echo('play video '+filename);
        vidProc = (player === 'omxplayer') ? spawn('omxplayer', ['-o', 'local', filename]) : spawn('mplayer', ['-vm', filename]);
        vidProc.stdout.on('data', function (data) { vidProcLog += data; });
        vidProc.stderr.on('data', function (data) { vidProcLog += data; });
        vidProc.on('exit', function (code) {
            playing = false;
            echo(player+' exited with code '+code);
            queryRemote('ended');
        });
    }
}

// PROCESS /////////////////////////////////

// parse process' incoming arguments
var parseArgv = function () {
    echo('parse argv');
    var conf;
    files = [];

    // parse arguments
    if (process.argv.length < 3) {
        // no arguments case
        conf = 'vidcomm.conf';
    } else {
        process.argv.forEach(function (val, idx, arr) {
            if (val.search(/^\.*[^\.]+\.conf$/) !== -1) {
                // configuration file case
                conf = val;
                // readConf();
            } else if (val.search(/^\.*[^\.]+\.(mp4|m4v|mov)$/) !== -1) {
                // video filename case
                files.push(val);
            } else if (val.search(/192\.168\.1\.\d+/) !== -1) {
                // remote address case
                remoteAddress = val;
            }
        });
    }

    // if a configuration file was given
    if (conf) {
        var data = fs.readFileSync(conf, {encoding:'utf-8'});
        if (data.length) {
            data.split('\n').forEach(function (val, idx, arr) {
                if (val.search(/^\.*[^\.]+\.(mp4|m4v|mov)$/) !== -1) {
                    // video filename case
                    files.push(val);
                } else if (val.search(/192\.168\.1\.\d+/) !== -1) {
                    // remote address case
                    remoteAddress = val;
                }
            });
        }
    }

    // if video files were given
    if (files.length) {
        // clear terminal, move cursor to top left and hide it
        // console.log('\033[2J\033\033[H\033[?25l');

        // if a remote server address was given
        if (remoteAddress.length) {
            findLocalAddress();
        } else {
            playNextVideo();
        }
    }

    // failsafe
    if (!conf && !files.length) {
        exitFunction();
        process.exit(1);
    }
}

// check whether other vidcomm process is running on the system
var checkForDuplicates = function () {
    echo('check for duplicates');
    require('child_process').exec('ps aux | grep '+player+' | grep -v grep', function (error, stdout, stderr) {
        if (stdout.length) {
            console.log('a video player is already running on this machine.');
            console.log(stdout);
            exitFunction();
            process.exit(1);
        } else {
            echo('vidcomm starting.');
            parseArgv();
        }
    });
}

// handle ctrl-C gracefully
process.on('SIGINT', function () {
    console.log(' ');
    exitFunction();
    process.exit(1);
});

// START ///////////////////////////////////

// check which player is available on the system
require('child_process').exec('which omxplayer', function (error, stdout, stderr) {
    if (stdout[0] !== '/') {
        require('child_process').exec('which mplayer', function (error, stdout, stderr) {
            if (stdout[0] !== '/') {
                console.log('no video player available.');
                exitFunction();
                process.exit(1);
            } else {
                player = 'mplayer';
                echo('player is '+player);
                checkForDuplicates();
            }
        });
    } else {
        player = 'omxplayer';
        echo('player is '+player);
        checkForDuplicates();
    }
});<|endoftext|>


<commit_before>/**
 * Markup and styles.
 */
const template = document.createElement('template');
template.innerHTML = `

    <slot id="slidesSlot">
No content available
</slot>

`;

/**
 * XSlider definition.
 */
class XSlider extends HTMLElement {
  static get observedAttributes() {
    return ['selected'];
  }

  /**
   * Runs anytime a new instance is created (in HTML or JS).
   * The construtor is a good place to create shadow DOM, though you should
   * avoid touching any attributes or light DOM children as they may not
   * be available yet.
   * @constructor
   */
  constructor() {
    super();

    this.attachShadow({mode: 'open'});
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  /**
   * Fires when the element is inserted into the DOM.
   * It's a good place to set the initial `role`, `tabindex`, internal state,
   * and install event listeners.
   */
  connectedCallback() {
    // Grab references to the DOM.
    this._slidesWrapper = this.shadowRoot.querySelector('#slidesWrapper');
    this._slidesSlot = this.shadowRoot.querySelector('#slidesSlot');
    this._paginationWrapper = this.shadowRoot.querySelector('#pagination');
    this._slides = this._getSlides();

    // Setup the component.
    this.selected = this.getAttribute('initial-slide') || 0;
    this.selected = this.selected || 0;

    this._updatePagination();

    // Enable transitions only after the initial setup.
    // Double rAF is necessary to wait for 'selected' to take effect.
    requestAnimationFrame(_ => {
      requestAnimationFrame(_ => {
        this.setAttribute('transitioning', '');
      });
    });

    // Add event listeners.
    this._slidesSlot.addEventListener('slotchange', this);
  }

  /**
   * Defining handleEvent allows to pass `this` as the callback to every
   * `addEventListener` and `removeEventListener`. This avoids the need of
   * binding every function. See
   * https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
   *
   * @param {Event} e
   */
  handleEvent(e) {
    if (e.target.name === 'x-slider-pagination') {
      this._onPaginationChange(e);
    } else if (e.target === this._slidesSlot) {
      this._onSlotChange();
    }
  }

  /**
   * Fires when the element is removed from the DOM.
   * It's a good place to do clean up work like releasing references and
   * removing event listeners.
   */
  disconnectedCallback() {
    this._slidesSlot.removeEventListener('slotchange', this);
  }

  /**
   * Used for upgrading properties in case this element is upgraded lazily.
   * See web/fundamentals/architecture/building-components/best-practices#lazy-properties
   * @param {any} prop
   */
  _upgradeProperty(prop) {
    if (this.hasOwnProperty(prop)) {
      let value = this[prop];
      delete this[prop];
      this[prop] = value;
    }
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    this[attrName] = newVal;
  }

  /**
   * Sets the selected slide and moves it into view.
   * @param {number} i The 0-based index of the selected slide.
   */
  set selected(i) {
    const parsed = parseInt(i, 10);

    if (!Number.isFinite(parsed) ||
        parsed >= this._slides.length ||
        parsed < 0) {
      console.warn(`Can not set selected slide to number ${i}`);
      return;
    }

    if (this._selected === parsed) return;

    this._selected = parsed;
    this.setAttribute('selected', parsed);

    /* Update the DOM as necessary */
    requestAnimationFrame(_ => {
      this._slideTo(this._selected)
      this._updatePagination();
    });
  }

  /**
   * Gets the index of the selected slide.
   * @return {number} The 0-based index of the selected slide.
   */
  get selected() {
    return this._selected;
  }

  /**
   * Called when the content of #slidesSlot changes.
   * Updates the slider to the new number of slides.
   */
  _onSlotChange() {
    this._slides = this._getSlides();

    if (this.selected >= this._slides.length) {
      this.selected = this._slides.length - 1;
    }

    this._updatePagination();
  }

  /**
   * Updates the pagination to relect the current number of slides,
   * and highlights the bullet point correponsing to the selected slide.
   */
  _updatePagination() {
    // Update the number of bullet points
    if (this._paginationWrapper.childElementCount !== this._slides.length) {
      // Remove bullet points (TODO: optimise).
      while (this._paginationWrapper.firstChild) {
        this._paginationWrapper.firstChild.removeEventListener('change', this);
        this._paginationWrapper.removeChild(this._paginationWrapper.firstChild);
      }
      // Add bullet points.
      const frag = document.createDocumentFragment();
      this._slides.forEach((s, i) => {
        const radio = document.createElement('input');
        radio.setAttribute('type', 'radio');
        radio.setAttribute('name', 'x-slider-pagination');
        radio.setAttribute('aria-label', `To slide ${i + 1}`);
        radio.setAttribute('data-slide', i);
        radio.checked = i === this._selected;
        radio.addEventListener('change', this);

        frag.appendChild(radio);
      });
      this._paginationWrapper.appendChild(frag);
    } else {
      Array.from(this._paginationWrapper.childNodes).forEach((radio, i) => {
        radio.checked = i === this._selected;
      });
    }
  }

  _onPaginationChange(e) {
    this.selected = e.target.getAttribute('data-slide');
  }

  /**
   * Gets the elements in the light DOM (assignedNodes() of #slidesSlot).
   * @returns {Array<HTMLElement>} The elements found in #slidesSlot.
   */
  _getSlides() {
    return this._slidesSlot.assignedNodes()
        .filter(n => n.nodeType === Node.ELEMENT_NODE);
  }

  /**
   * Translates the slider to show the target slide.
   * @param {number} targetSlide The slide to slide to.
   */
  _slideTo(targetSlide) {
    this._slidesWrapper.style.transform = `translateX(${- targetSlide * 100}%)`;
  }
}

window.customElements.define('x-slider', XSlider);
<commit_msg>Added previous and next buttons<commit_after>/**
 * Markup and styles.
 */
const template = document.createElement('template');
template.innerHTML = `

    <slot id="slidesSlot">
No content available
</slot>

  <button id="previous" aria-label="To previous slide"><</button>
  <button id="next" aria-label="To next slide">></button>

`;

/**
 * XSlider definition.
 */
class XSlider extends HTMLElement {
  static get observedAttributes() {
    return ['selected'];
  }

  /**
   * Runs anytime a new instance is created (in HTML or JS).
   * The construtor is a good place to create shadow DOM, though you should
   * avoid touching any attributes or light DOM children as they may not
   * be available yet.
   * @constructor
   */
  constructor() {
    super();

    this.attachShadow({mode: 'open'});
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }

  /**
   * Fires when the element is inserted into the DOM.
   * It's a good place to set the initial `role`, `tabindex`, internal state,
   * and install event listeners.
   */
  connectedCallback() {
    // Grab references to the DOM.
    this._slidesWrapper = this.shadowRoot.querySelector('#slidesWrapper');
    this._slidesSlot = this.shadowRoot.querySelector('#slidesSlot');
    this._paginationWrapper = this.shadowRoot.querySelector('#pagination');
    this._slides = this._getSlides();
    this._prevButton = this.shadowRoot.querySelector('#previous');
    this._nextButton = this.shadowRoot.querySelector('#next');

    // Setup the component.
    this.selected = this.getAttribute('initial-slide') || 0;
    this.selected = this.selected || 0;

    this._updatePagination();

    // Enable transitions only after the initial setup.
    // Double rAF is necessary to wait for 'selected' to take effect.
    requestAnimationFrame(_ => {
      requestAnimationFrame(_ => {
        this.setAttribute('transitioning', '');
      });
    });

    // Add event listeners.
    this._slidesSlot.addEventListener('slotchange', this);
    this._prevButton.addEventListener('click', this);
    this._nextButton.addEventListener('click', this);
  }

  /**
   * Defining handleEvent allows to pass `this` as the callback to every
   * `addEventListener` and `removeEventListener`. This avoids the need of
   * binding every function. See
   * https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
   *
   * @param {Event} e Any event.
   */
  handleEvent(e) {
    if (e.target.name === 'x-slider-pagination') {
      this._onPaginationChange(e);
    } else if (e.target === this._slidesSlot) {
      this._onSlotChange();
    } else if (e.target === this._prevButton) {
      this.previous();
    } else if (e.target === this._nextButton) {
      this.next();
    }
  }

  /**
   * Fires when the element is removed from the DOM.
   * It's a good place to do clean up work like releasing references and
   * removing event listeners.
   */
  disconnectedCallback() {
    this._slidesSlot.removeEventListener('slotchange', this);
  }

  /**
   * Used for upgrading properties in case this element is upgraded lazily.
   * See web/fundamentals/architecture/building-components/best-practices#lazy-properties
   * @param {any} prop
   */
  _upgradeProperty(prop) {
    if (this.hasOwnProperty(prop)) {
      let value = this[prop];
      delete this[prop];
      this[prop] = value;
    }
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    this[attrName] = newVal;
  }

  /**
   * Sets the selected slide and moves it into view.
   * @param {number} i The 0-based index of the selected slide.
   */
  set selected(i) {
    const parsed = parseInt(i, 10);

    // Accept only numbers between `0` and `this._slides.length - 1`.
    if (!Number.isFinite(parsed) ||
        parsed >= this._slides.length ||
        parsed < 0) {
      console.warn(`Can not set selected slide to number ${i}`);
      return;
    }

    // Return if property's value hasn't changed.
    if (this._selected === parsed) return;

    // Update internal state.
    this._selected = parsed;
    // Reflect to attribute.
    this.setAttribute('selected', parsed);

    // Enable / Disable navigation buttons.
    if (!this.loop && this.selected === 0) {
      this._prevButton.setAttribute('disabled', '');
    } else {
      this._prevButton.removeAttribute('disabled');
    }
    if (!this.loop && this.selected === this._slides.length - 1) {
      this._nextButton.setAttribute('disabled', '');
    } else {
      this._nextButton.removeAttribute('disabled');
    }

    // Show the new selected slide and update pagination.
    requestAnimationFrame(_ => {
      this._slideTo(this._selected)
      this._updatePagination();
    });
  }

  /**
   * Gets the index of the selected slide.
   * @return {number} The 0-based index of the selected slide.
   */
  get selected() {
    return this._selected;
  }

  /**
   * Called when the content of #slidesSlot changes.
   * Updates the slider to the new number of slides.
   */
  _onSlotChange() {
    this._slides = this._getSlides();

    if (this.selected >= this._slides.length) {
      this.selected = this._slides.length - 1;
    }

    this._updatePagination();
  }

  /**
   * Updates the pagination to relect the current number of slides,
   * and highlights the bullet point correponsing to the selected slide.
   */
  _updatePagination() {
    // Update the number of bullet points
    if (this._paginationWrapper.childElementCount !== this._slides.length) {
      // Remove bullet points (TODO: optimise).
      while (this._paginationWrapper.firstChild) {
        this._paginationWrapper.firstChild.removeEventListener('change', this);
        this._paginationWrapper.removeChild(this._paginationWrapper.firstChild);
      }
      // Add bullet points.
      const frag = document.createDocumentFragment();
      this._slides.forEach((s, i) => {
        const radio = document.createElement('input');
        radio.setAttribute('type', 'radio');
        radio.setAttribute('name', 'x-slider-pagination');
        radio.setAttribute('value', i);
        radio.setAttribute('aria-label', `To slide ${i + 1}`);
        radio.checked = i === this._selected;
        radio.addEventListener('change', this);

        frag.appendChild(radio);
      });
      this._paginationWrapper.appendChild(frag);
    } else {
      Array.from(this._paginationWrapper.childNodes).forEach((radio, i) => {
        radio.checked = i === this._selected;
      });
    }
  }

  /**
   * Called when any pagination bullet point is selected.
   * @param {Event} e The 'change' event fired by the radio input.
   * @memberof XSlider
   */
  _onPaginationChange(e) {
    this.selected = e.target.getAttribute('value');
  }

  /**
   * Gets the elements in the light DOM (assignedNodes() of #slidesSlot).
   * @returns {Array<HTMLElement>} The elements found in #slidesSlot.
   */
  _getSlides() {
    return this._slidesSlot.assignedNodes()
        .filter(n => n.nodeType === Node.ELEMENT_NODE);
  }

  /**
   * Selects the slide preceding the currently selected one.
   * If the currently selected slide is the first slide and the loop
   * functionality is disabled, nothing happens.
   */
  previous() {
    if (this.selected > 0) {
      this.selected -= 1;
    } else if (this.loop) {
      this.selected = this._slides.length - 1;
    }
  }

  /**
   * Selects the slide following the currently selected one.
   * If the currently selected slide is the last slide and the loop
   * functionality is disabled, nothing happens.
   */
  next() {
    if (this.selected < this._slides.length - 1) {
      this.selected += 1;
    } else if (this.loop) {
      this.selected = 0;
    }
  }

  /**
   * Translates the slider to show the target slide.
   * @param {number} targetSlide The slide to slide to.
   */
  _slideTo(targetSlide) {
    this._slidesWrapper.style.transform = `translateX(${- targetSlide * 100}%)`;
  }
}

window.customElements.define('x-slider', XSlider);
<|endoftext|>


<commit_before>(function() {
  "use strict";

  // Save a reference to the global object
  var root = this,

      // Allow use of jQuery, Zepto or ender with Xively.js in the style of Backbone
      $ = root.jQuery || root.Zepto || root.ender || root.$,

      protocol = function() {
        return (document.location.protocol === "https:" ? "https:" : "http:");
      },

      // Wraps all socket connection and message sending logic
      SocketTransport = function(apiHost) {
        var self = this,
            socket = false,
            socketReady = false,
            queue = [],

            execute = function ( arr ) {
              if ( typeof arr === "function" ) {
                arr.apply( this, Array.prototype.slice.call( arguments, 1 ));
              }
              else if ( Object.prototype.toString.apply(arr) === '[object Array]' ) {
                var x = arr.length;
                while (x--) {
                  arr[x].apply( this, Array.prototype.slice.call( arguments, 1 ));
                }
              }
            };

        this.connect = function(callback) {
          var SocketProvider = (window.SockJS || window.MozWebSocket || window.WebSocket),
              socketEndpoint,
              socketPort;

          if ( !socket && SocketProvider ) {
            if ( window.SockJS ) {
              socketPort = (protocol() === "https:" ? 8093 : 8082); // TODO: double-check these ports are right
              socketEndpoint = protocol() + "//" + apiHost + ":" + socketPort + "/sockjs";
            } else {
              socketPort = (protocol() === "https:" ? 8094 : 8080); // TODO: double-check these ports are right
              socketEndpoint = (protocol() === "https:" ? "wss:" : "ws:") + "//" + apiHost + ":" + socketPort;
            }

            socket = new SocketProvider(socketEndpoint);

            socket.onerror = function( e ) {
              if ( self.error ) { self.error( e, this ); }
              self.connect();
            };

            socket.onclose = function( e ) {
              if ( self.close ) { self.close( e, this ); }
              self.connect();
            };

            socket.onopen = function( e ) {
              socketReady = true;
              if ( self.open )         { self.open( e, this ); }
              if ( queue.length )  { execute( queue ); }
              if ( callback )         { callback( this ); }
            };

            socket.onmessage = function( e ) {
              var data      = e.data,
                  response  = JSON.parse( data );
              if ( response.body ) {
                $('body').trigger( "xively."+ response.resource, response.body );
              }
            };
          }
        };

        this.send = function(message) {
          if (!socketReady) {
            this.connect();
            queue.push(function() {
              socket.send(message);
            });
          } else {
            socket.send(message);
          }
        };
      };

  // Main Xively client object
  var XivelyClient = function(apiHost) {
    // set the default api host
    apiHost = (apiHost || "api.xively.com");

    // private stuff
    var self = this,
        version = "1.0.4-alpha",

        apiEndpoint = protocol() + "//" + apiHost + "/v2",

        cacheRequest = false,

        apiKey,

        // var which will hold the public API we are going to expose
        methods,

        // log helper method that doesn't break in environments without console.log
        log = function(msg) {
          if (window.console && window.console.log) {
            window.console.log( msg );
          }
        },

        // Ajax request
        request = function(options) {
          var settings = $.extend({
                type      : 'get'
              }, options);

          if ( !apiKey ) {
            return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
          }

          if ( !settings.url ) { return; }
          settings.type = settings.type.toUpperCase();

          if ( settings.type === "PUT" || settings.type === "POST" ) {
            if ( !settings.data || typeof settings.data !== 'object' ) {
              return;
            }
            else {
              settings.data = JSON.stringify(settings.data);
            }
          }

          $.ajax({
            url         : settings.url,
            type        : settings.type,
            headers     : {
              "X-ApiKey" : apiKey,
              "Content-Type": "application/json"
            },
            data        : settings.data,
            crossDomain : true,
            dataType    : 'json',
            cache       : cacheRequest
          })
          .done(settings.done)
          .fail(settings.fail)
          .always(settings.always);
        },

        resources = [];

    // disable caching
    $.ajaxSetup ({
      cache: cacheRequest
    });

    this.socket = function() {
      if (this._ws) {
        return this._ws;
      }

      return (this._ws = new SocketTransport(apiHost));
    };

    this.version = function() {
      return version;
    };

    // ---------------------------------
    // Set API Key
    //
    this.setKey = function(newKey) {
      apiKey = newKey;
    };

    // ---------------------------------
    // Expose api endpoint
    //
    this.apiEndpoint = apiEndpoint;

    // ---------------------------------
    // General API interaction functions
    //
    this.request = request;

    // this.subscribe = ws.subscribe.bind(ws);
    this.subscribe = function(resource, callback) {
      var request  = '{"headers":{"X-ApiKey":"' + apiKey + '"}, "method":"subscribe", "resource":"'+ resource +'"}';

      if ( !apiKey ) {
        return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
      }

      if ( resources.indexOf(resource) < 0 ) {
        resources.push( resource );

        this.socket().send( request );
      }

      if ( callback && typeof callback === "function" ) {
        $( document ).on( "xively."+ resource, callback );
      }
    };

    this.unsubscribe = function(resource) {
      if ( !apiKey ) {
        return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
      }

      var index = resources.indexOf(resource);

      if (index >= 0) {
        resources.splice(index, 1);
        this.socket().send('{"headers":{"X-ApiKey":"' + apiKey + '"}, "method":"unsubscribe", "resource":"'+ resource +'"}');
      }
    };

    this.live = function (selector, resource) {
      var callback = function ( event, data ) {
        var response = event.current_value ? event : data;
        if ( response.current_value ) {
          $( selector ).each(function() {
            $( this ).html( response.current_value ).attr( 'data-xively-resource', resource );
          });
        }
      };
      request({
        url    : apiEndpoint + resource,
        always : callback
      });
      this.subscribe( resource, callback );
    };

    this.stop = function ( selector ) {
      this.unsubscribe( $( selector ).first().attr( 'data-xively-resource' ) );
    };

    // ---------------------------------
    // Feed module
    //
    this.feed = {
      get: function(id, callback) {
        request({
          url: apiEndpoint + "/feeds/" + id,
          always: callback
        });
      },

      update: function(id, data, callback) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ id +".json",
          data    : data,
          always  : callback
        });
      },

      'new': function(data, callback) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( id, callback ) {
        request({
          type    : "delete",
          url     : apiEndpoint +"/feeds/"+ id,
          always  : callback
        });
      },

      history : function ( id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ id +".json",
          data    : options,
          always  : callback
        });
      },

      list : function ( options, callback ) {
        request({
          url     : apiEndpoint +"/feeds",
          data    : options,
          always  : callback
        });
      },

      subscribe: function(id, callback) {
        if (id) {
          self.subscribe("/feeds/" + id, callback);
        }
      },

      unsubscribe : function ( id, callback ) {
        if ( id ) {
          self.unsubscribe( "/feeds/"+ id );
        }
      }
    };

    // ---------------------------------
    // Datastream module
    //
    this.datastream = {
      get : function ( feed_id, datastream_id, callback ) {
        request({
          url    : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          always : callback
        });
      },

      update : function ( feed_id, datastream_id, data, callback ) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : data,
          always  : callback
        });
      },

      'new' : function ( feed_id, data, callback ) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( feed_id, datastream_id, callback ) {
        request({
          type    : "delete",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id,
          always  : callback
        });
      },

      history : function ( feed_id, datastream_id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : options,
          always  : callback
        });
      },

      list : function ( feed_id, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +".json",
          always  : function ( data ) {
            callback.call( this, data.datastreams );
          }
        });
      },

      subscribe : function ( feed_id, datastream_id, callback ) {
        if ( feed_id && datastream_id ) {
          self.subscribe( "/feeds/"+ feed_id +"/datastreams/"+ datastream_id, callback );
        }
      },

      unsubscribe : function ( feed_id, datastream_id, callback ) {
        if ( feed_id && datastream_id ) {
          self.unsubscribe( "/feeds/"+ feed_id +"/datastreams/"+ datastream_id );
        }
      },

      live : function ( element, feed_id, datastream_id ) {
        if ( element && feed_id && datastream_id ) {
          self.live( element, "/feeds/"+ feed_id +"/datastreams/"+ datastream_id );
        }
      },

      stop : function ( element ) {
        if ( element ) {
          self.stop( element );
        }
      }
    };

    // ---------------------------------
    // Datapoint module
    //
    this.datapoint = {
      get : function ( feed_id, datastream_id, timestamp, callback ) {
        request({
          url    : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp,
          always : callback
        });
      },

      update : function ( feed_id, datastream_id, timestamp, value, callback ) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp,
          data    : {
            "value": value
          },
          always  : callback
        });
      },

      'new' : function ( feed_id, datastream_id, data, callback ) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( feed_id, datastream_id, timestamp, callback ) {
        var req_options = {
          type   : "delete",
          always : callback
        };

        if ( typeof timestamp === "object" ) {
          req_options.url  = apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints";
          req_options.data = timestamp;
        }
        else {
          req_options.url = apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp;
        }

        request( req_options );
      },

      history : function ( feed_id, datastream_id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : options,
          always  : function ( data ) {
            callback.call( this, data.datapoints );
          }
        });
      }
    };

    this._settings = function() {
      return {
        apiKey: apiKey,
        apiHost: apiHost,
        cacheRequest: cacheRequest
      };
    };

    return this;
  };

  root.XivelyClient = XivelyClient;
  root.xively = root.Xively = new XivelyClient();

}).call(this);
<commit_msg>Remove an 'alpha' from the internal version string.<commit_after>(function() {
  "use strict";

  // Save a reference to the global object
  var root = this,

      // Allow use of jQuery, Zepto or ender with Xively.js in the style of Backbone
      $ = root.jQuery || root.Zepto || root.ender || root.$,

      protocol = function() {
        return (document.location.protocol === "https:" ? "https:" : "http:");
      },

      // Wraps all socket connection and message sending logic
      SocketTransport = function(apiHost) {
        var self = this,
            socket = false,
            socketReady = false,
            queue = [],

            execute = function ( arr ) {
              if ( typeof arr === "function" ) {
                arr.apply( this, Array.prototype.slice.call( arguments, 1 ));
              }
              else if ( Object.prototype.toString.apply(arr) === '[object Array]' ) {
                var x = arr.length;
                while (x--) {
                  arr[x].apply( this, Array.prototype.slice.call( arguments, 1 ));
                }
              }
            };

        this.connect = function(callback) {
          var SocketProvider = (window.SockJS || window.MozWebSocket || window.WebSocket),
              socketEndpoint,
              socketPort;

          if ( !socket && SocketProvider ) {
            if ( window.SockJS ) {
              socketPort = (protocol() === "https:" ? 8093 : 8082); // TODO: double-check these ports are right
              socketEndpoint = protocol() + "//" + apiHost + ":" + socketPort + "/sockjs";
            } else {
              socketPort = (protocol() === "https:" ? 8094 : 8080); // TODO: double-check these ports are right
              socketEndpoint = (protocol() === "https:" ? "wss:" : "ws:") + "//" + apiHost + ":" + socketPort;
            }

            socket = new SocketProvider(socketEndpoint);

            socket.onerror = function( e ) {
              if ( self.error ) { self.error( e, this ); }
              self.connect();
            };

            socket.onclose = function( e ) {
              if ( self.close ) { self.close( e, this ); }
              self.connect();
            };

            socket.onopen = function( e ) {
              socketReady = true;
              if ( self.open )         { self.open( e, this ); }
              if ( queue.length )  { execute( queue ); }
              if ( callback )         { callback( this ); }
            };

            socket.onmessage = function( e ) {
              var data      = e.data,
                  response  = JSON.parse( data );
              if ( response.body ) {
                $('body').trigger( "xively."+ response.resource, response.body );
              }
            };
          }
        };

        this.send = function(message) {
          if (!socketReady) {
            this.connect();
            queue.push(function() {
              socket.send(message);
            });
          } else {
            socket.send(message);
          }
        };
      };

  // Main Xively client object
  var XivelyClient = function(apiHost) {
    // set the default api host
    apiHost = (apiHost || "api.xively.com");

    // private stuff
    var self = this,
        version = "1.0.4",

        apiEndpoint = protocol() + "//" + apiHost + "/v2",

        cacheRequest = false,

        apiKey,

        // var which will hold the public API we are going to expose
        methods,

        // log helper method that doesn't break in environments without console.log
        log = function(msg) {
          if (window.console && window.console.log) {
            window.console.log( msg );
          }
        },

        // Ajax request
        request = function(options) {
          var settings = $.extend({
                type      : 'get'
              }, options);

          if ( !apiKey ) {
            return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
          }

          if ( !settings.url ) { return; }
          settings.type = settings.type.toUpperCase();

          if ( settings.type === "PUT" || settings.type === "POST" ) {
            if ( !settings.data || typeof settings.data !== 'object' ) {
              return;
            }
            else {
              settings.data = JSON.stringify(settings.data);
            }
          }

          $.ajax({
            url         : settings.url,
            type        : settings.type,
            headers     : {
              "X-ApiKey" : apiKey,
              "Content-Type": "application/json"
            },
            data        : settings.data,
            crossDomain : true,
            dataType    : 'json',
            cache       : cacheRequest
          })
          .done(settings.done)
          .fail(settings.fail)
          .always(settings.always);
        },

        resources = [];

    // disable caching
    $.ajaxSetup ({
      cache: cacheRequest
    });

    this.socket = function() {
      if (this._ws) {
        return this._ws;
      }

      return (this._ws = new SocketTransport(apiHost));
    };

    this.version = function() {
      return version;
    };

    // ---------------------------------
    // Set API Key
    //
    this.setKey = function(newKey) {
      apiKey = newKey;
    };

    // ---------------------------------
    // Expose api endpoint
    //
    this.apiEndpoint = apiEndpoint;

    // ---------------------------------
    // General API interaction functions
    //
    this.request = request;

    // this.subscribe = ws.subscribe.bind(ws);
    this.subscribe = function(resource, callback) {
      var request  = '{"headers":{"X-ApiKey":"' + apiKey + '"}, "method":"subscribe", "resource":"'+ resource +'"}';

      if ( !apiKey ) {
        return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
      }

      if ( resources.indexOf(resource) < 0 ) {
        resources.push( resource );

        this.socket().send( request );
      }

      if ( callback && typeof callback === "function" ) {
        $( document ).on( "xively."+ resource, callback );
      }
    };

    this.unsubscribe = function(resource) {
      if ( !apiKey ) {
        return log( "(xivelyJS) ::: No API key ::: Set your API key first with xively.setKey( YOUR_API_KEY ) before using any methods. Check docs for more info." );
      }

      var index = resources.indexOf(resource);

      if (index >= 0) {
        resources.splice(index, 1);
        this.socket().send('{"headers":{"X-ApiKey":"' + apiKey + '"}, "method":"unsubscribe", "resource":"'+ resource +'"}');
      }
    };

    this.live = function (selector, resource) {
      var callback = function ( event, data ) {
        var response = event.current_value ? event : data;
        if ( response.current_value ) {
          $( selector ).each(function() {
            $( this ).html( response.current_value ).attr( 'data-xively-resource', resource );
          });
        }
      };
      request({
        url    : apiEndpoint + resource,
        always : callback
      });
      this.subscribe( resource, callback );
    };

    this.stop = function ( selector ) {
      this.unsubscribe( $( selector ).first().attr( 'data-xively-resource' ) );
    };

    // ---------------------------------
    // Feed module
    //
    this.feed = {
      get: function(id, callback) {
        request({
          url: apiEndpoint + "/feeds/" + id,
          always: callback
        });
      },

      update: function(id, data, callback) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ id +".json",
          data    : data,
          always  : callback
        });
      },

      'new': function(data, callback) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( id, callback ) {
        request({
          type    : "delete",
          url     : apiEndpoint +"/feeds/"+ id,
          always  : callback
        });
      },

      history : function ( id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ id +".json",
          data    : options,
          always  : callback
        });
      },

      list : function ( options, callback ) {
        request({
          url     : apiEndpoint +"/feeds",
          data    : options,
          always  : callback
        });
      },

      subscribe: function(id, callback) {
        if (id) {
          self.subscribe("/feeds/" + id, callback);
        }
      },

      unsubscribe : function ( id, callback ) {
        if ( id ) {
          self.unsubscribe( "/feeds/"+ id );
        }
      }
    };

    // ---------------------------------
    // Datastream module
    //
    this.datastream = {
      get : function ( feed_id, datastream_id, callback ) {
        request({
          url    : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          always : callback
        });
      },

      update : function ( feed_id, datastream_id, data, callback ) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : data,
          always  : callback
        });
      },

      'new' : function ( feed_id, data, callback ) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( feed_id, datastream_id, callback ) {
        request({
          type    : "delete",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id,
          always  : callback
        });
      },

      history : function ( feed_id, datastream_id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : options,
          always  : callback
        });
      },

      list : function ( feed_id, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +".json",
          always  : function ( data ) {
            callback.call( this, data.datastreams );
          }
        });
      },

      subscribe : function ( feed_id, datastream_id, callback ) {
        if ( feed_id && datastream_id ) {
          self.subscribe( "/feeds/"+ feed_id +"/datastreams/"+ datastream_id, callback );
        }
      },

      unsubscribe : function ( feed_id, datastream_id, callback ) {
        if ( feed_id && datastream_id ) {
          self.unsubscribe( "/feeds/"+ feed_id +"/datastreams/"+ datastream_id );
        }
      },

      live : function ( element, feed_id, datastream_id ) {
        if ( element && feed_id && datastream_id ) {
          self.live( element, "/feeds/"+ feed_id +"/datastreams/"+ datastream_id );
        }
      },

      stop : function ( element ) {
        if ( element ) {
          self.stop( element );
        }
      }
    };

    // ---------------------------------
    // Datapoint module
    //
    this.datapoint = {
      get : function ( feed_id, datastream_id, timestamp, callback ) {
        request({
          url    : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp,
          always : callback
        });
      },

      update : function ( feed_id, datastream_id, timestamp, value, callback ) {
        request({
          type    : "put",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp,
          data    : {
            "value": value
          },
          always  : callback
        });
      },

      'new' : function ( feed_id, datastream_id, data, callback ) {
        request({
          type    : "post",
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints",
          data    : data,
          always  : callback
        });
      },

      'delete' : function ( feed_id, datastream_id, timestamp, callback ) {
        var req_options = {
          type   : "delete",
          always : callback
        };

        if ( typeof timestamp === "object" ) {
          req_options.url  = apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints";
          req_options.data = timestamp;
        }
        else {
          req_options.url = apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +"/datapoints/"+ timestamp;
        }

        request( req_options );
      },

      history : function ( feed_id, datastream_id, options, callback ) {
        request({
          url     : apiEndpoint +"/feeds/"+ feed_id +"/datastreams/"+ datastream_id +".json",
          data    : options,
          always  : function ( data ) {
            callback.call( this, data.datapoints );
          }
        });
      }
    };

    this._settings = function() {
      return {
        apiKey: apiKey,
        apiHost: apiHost,
        cacheRequest: cacheRequest
      };
    };

    return this;
  };

  root.XivelyClient = XivelyClient;
  root.xively = root.Xively = new XivelyClient();

}).call(this);
<|endoftext|>