<commit_before>const React = require('react');
const { Button, Col, Container, Row, Spinner, Table } = require('elemental');
const DemoBox = require('../components/DemoBox');
const ExampleSource = require('../components/ExampleSource');

var Buttons = React.createClass({
	displayName: 'VIEW_Spinner',
	render () {
		return (
			<Container maxWidth={800} className="demo-container">

Spinner

Common use cases

Page Element

						<Row>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" type="primary" />
								</DemoBox>
							</Col>
							<Col>
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }} inverted >
									<Spinner size="md" type="inverted" />
								</DemoBox>
							</Col>
						</Row>

					<ExampleSource>
						{`
						<Spinner size="md" />
						<Spinner size="md" type="primary" />
						<Spinner size="md" type="inverted" />
						`}
					</ExampleSource>

Inside Buttons

							<Button>
								<Spinner />
							</Button>

							<Button disabled>
								<Spinner type="primary" />
								Saving
							</Button>

							<Button type="primary">
								<Spinner type="inverted" />
								Submitting
							</Button>

					<ExampleSource>
						{`
						<Button><Spinner /></Button>
						<Button disabled><Spinner type="primary" />Saving</Button>
						<Button type="primary"><Spinner type="inverted" />Submitting</Button>
						`}
					</ExampleSource>

Full Page Load

							<Spinner size="lg" />

					<ExampleSource>
						{`
						<Spinner size="lg" />
						`}
					</ExampleSource>

Usage

								Prop
								Type
								Default
								Description

								size
								enum
								sm

Declare the size of the dots in the spinner. Possible options:

									sm md lg

									Spinners automatically become small when inside of buttons

								type
								enum
								default

									Declare the colour of the dots in the spinner. Possible options:

									default primary inverted

			</Container>
		);
	}
});

module.exports = Buttons;
<commit_msg>website: fix spinner layout<commit_after>const React = require('react');
const { Button, Col, Container, Row, Spinner, Table } = require('elemental');
const DemoBox = require('../components/DemoBox');
const ExampleSource = require('../components/ExampleSource');

var Buttons = React.createClass({
	displayName: 'VIEW_Spinner',
	render () {
		return (
			<Container maxWidth={800} className="demo-container">

Spinner

Common use cases

Page Element

						<Row>
							<Col sm="1/3">
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" />
								</DemoBox>
							</Col>
							<Col sm="1/3">
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }}>
									<Spinner size="md" type="primary" />
								</DemoBox>
							</Col>
							<Col sm="1/3">
								<DemoBox style={{ paddingBottom: 20, paddingTop: 20 }} inverted >
									<Spinner size="md" type="inverted" />
								</DemoBox>
							</Col>
						</Row>

					<ExampleSource>
						{`
						<Spinner size="md" />
						<Spinner size="md" type="primary" />
						<Spinner size="md" type="inverted" />
						`}
					</ExampleSource>

Inside Buttons

							<Button>
								<Spinner />
							</Button>

							<Button disabled>
								<Spinner type="primary" />
								Saving
							</Button>

							<Button type="primary">
								<Spinner type="inverted" />
								Submitting
							</Button>

					<ExampleSource>
						{`
						<Button><Spinner /></Button>
						<Button disabled><Spinner type="primary" />Saving</Button>
						<Button type="primary"><Spinner type="inverted" />Submitting</Button>
						`}
					</ExampleSource>

Full Page Load

							<Spinner size="lg" />

					<ExampleSource>
						{`
						<Spinner size="lg" />
						`}
					</ExampleSource>

Usage

								Prop
								Type
								Default
								Description

								size
								enum
								sm

Declare the size of the dots in the spinner. Possible options:

									sm md lg

									Spinners automatically become small when inside of buttons

								type
								enum
								default

									Declare the colour of the dots in the spinner. Possible options:

									default primary inverted

			</Container>
		);
	}
});

module.exports = Buttons;
<|endoftext|>


<commit_before>/**
 * Crée une instance de Webos.Stylesheet, representant une feuille de style CSS.
 * @param {String} path Le chemin vers la feuille de style.
 * @param {String} [container] L'element auquel sera applique le style.
 * @since 1.0 alpha 1
 * @constructor
 */
Webos.Stylesheet = function WStylesheet(path, container) {
	if (!/^(\/|~\/)/.test(path)) {
		path = '/'+path;
	}

	if (Webos.Stylesheet._cache[path]) {
		Webos.Stylesheet.insertCss(Webos.Stylesheet._cache[path], container);
		return;
	}

	new Webos.ServerCall({
		'class': 'FileController',
		method: 'getContents',
		arguments: {
			file: path
		},
		async: false
	}).load(function(response) {
		var css = response.getStandardChannel();
		if (css) {
			Webos.Stylesheet._cache[path] = css;
			Webos.Stylesheet.insertCss(css, container);
		}
	});
};

/**
 * Cache des feuilles de style.
 * @private
 * @static
 */
Webos.Stylesheet._cache = {};

/**
 * Appliquer une feuille de style CSS.
 * @param {String} css La feuille de style CSS.
 * @param {String} [container] L'element auquel sera applique le style.
 * @static
 */
Webos.Stylesheet.insertCss = function insertCss(css, container) {
	if (container) {
		css = css
			.replace(/\/\*([\s\S]*?)\*\//g, '') //On enleve les commentaires
			.replace(/([\s\S]+?)\{([\s\S]*?)\}/g, function(str, p1, p2) {
				var result = '';

				p1 = p1.replace(/\s+/g, ' ');

				if (/^\s*@/.test(p1)) {
					if (/^\s*(@.+;)+/.test(p1)) {
						result += /@.+;/g.exec(p1).join('');
					} else {
						return p1+'{'+p2+'}';
					}
				}

				var selectors = p1.split(',');

				result += container+' '+selectors.join(', '+container+' ')+'{'+p2+'}';
				result = result.replace(/\s+/g, ' ');
				return result;
			});
	}

	var cssTag = document.createElement('style');
	cssTag.setAttribute('type', 'text/css');
	var cssText = document.createTextNode(css);
	cssTag.appendChild(cssText);
	$('head').append(cssTag);
};<commit_msg>Added comments<commit_after>/**
 * A CSS stylesheet.
 * @param {String} path The path to the CSS file.
 * @param {String} [container] The element on which CSS rules will be applied.
 * @since 1.0 alpha 1
 * @constructor
 */
Webos.Stylesheet = function WStylesheet(path, container) {
	if (!/^(\/|~\/)/.test(path)) { //Old path notation support - deprecated
		path = '/'+path;
	}

	if (Webos.Stylesheet._cache[path]) { //Is the file in cache ?
		Webos.Stylesheet.insertCss(Webos.Stylesheet._cache[path], container);
		return;
	}

	new Webos.ServerCall({
		'class': 'FileController',
		method: 'getContents',
		arguments: {
			file: path
		},
		async: false
	}).load(function(response) {
		var css = response.getStandardChannel();
		if (css) { //If there is some CSS
			Webos.Stylesheet._cache[path] = css;
			Webos.Stylesheet.insertCss(css, container); //Insert CSS in the page
		}
	});
};

/**
 * Cache for CSS stylesheets.
 * @private
 * @static
 */
Webos.Stylesheet._cache = {};

/**
 * Apply some CSS rules .
 * @param {String} css CSS rules.
 * @param {String} [container] The element on which CSS rules will be applied. If ommited, CSS rules will be applied to the whole page.
 * @static
 */
Webos.Stylesheet.insertCss = function insertCss(css, container) {
	if (container) {
		css = css
			.replace(/\/\*([\s\S]*?)\*\//g, '') //Delete comments
			.replace(/([\s\S]+?)\{([\s\S]*?)\}/g, function(str, p1, p2) { //Replace each selector
				var result = '';

				p1 = p1.replace(/\s+/g, ' ');

				if (/^\s*@/.test(p1)) { //Not a selector ?
					if (/^\s*(@.+;)+/.test(p1)) {
						result += /@.+;/g.exec(p1).join('');
					} else {
						return p1+'{'+p2+'}';
					}
				}

				var selectors = p1.split(',');

				result += container+' '+selectors.join(', '+container+' ')+'{'+p2+'}';
				result = result.replace(/\s+/g, ' ');
				return result;
			});
	}

	//Insert CSS rules
	var cssTag = document.createElement('style');
	cssTag.setAttribute('type', 'text/css');
	var cssText = document.createTextNode(css);
	cssTag.appendChild(cssText);
	$('head').append(cssTag);
};
<|endoftext|>


<commit_before>var ace = window.ace

var wrap = require("../../wrap")

module.exports = PlayerRepl

function PlayerRepl(row) {
    var div = document.createElement("input")
    div.className = "repl"
    document.body.appendChild(div)

    div.onkeyup = function (e) {
        if(e.keyCode == 13) { //enter
            row.set('message', {
                text: e.target.value
                fill : 'blue',
                stroke : 'yellow'
            })
        }
    }

/*
    console.log("editor", editor)

    editor.commands.addCommand({
        name: "run"
        , bindKey: {
            win: "Return"
            , mac: "Return"
        }
        , exec: handleRun
    })

    function handleRun() {
        var source = editor.getValue()
        wrap(source)(row.api)

        editor.setValue("")
        editor.textInput.blur()
    }
*/

}
<commit_msg>fix syntax<commit_after>var ace = window.ace

var wrap = require("../../wrap")

module.exports = PlayerRepl

function PlayerRepl(row) {
    var div = document.createElement("input")
    div.className = "repl"
    document.body.appendChild(div)

    div.onkeyup = function (e) {
        if(e.keyCode == 13) { //enter
            row.set('message', {
                text: e.target.value,
                fill : 'blue',
                stroke : 'yellow'
            })
        }
    }

/*
    console.log("editor", editor)

    editor.commands.addCommand({
        name: "run"
        , bindKey: {
            win: "Return"
            , mac: "Return"
        }
        , exec: handleRun
    })

    function handleRun() {
        var source = editor.getValue()
        wrap(source)(row.api)

        editor.setValue("")
        editor.textInput.blur()
    }
*/

}
<|endoftext|>


<commit_before>/* Copyright (c) Royal Holloway, University of London | Contact Blake Loring (blake@parsed.uk), Duncan Mitchell (Duncan.Mitchell.2015@rhul.ac.uk), or Johannes Kinder (johannes.kinder@rhul.ac.uk) for details or support | LICENSE.md for license details */

const { app, BrowserWindow } = require("electron");

app.commandLine.appendSwitch("ignore-certificate-errors");
app.commandLine.appendSwitch("disable-web-security");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

const createWindow = () => {
	// Create the browser window.
	mainWindow = new BrowserWindow({
		title: `ExpoSE ${process.argv[process.argv.length - 2]} ${process.argv[process.argv.length - 1]}`,
		width: 800,
		height: 600,
		webPreferences: {
			webSecurity: false
		}
	});

	mainWindow.on("page-title-updated", (e) => {
		e.preventDefault();
	});

	mainWindow.webContents.session.clearCache(function() {
		mainWindow.webContents.session.setProxy({proxyRules:"http://localhost:8080"}, function () {
			mainWindow.loadURL(process.argv[process.argv.length - 2]);
		});
	});

	// Emitted when the window is closed.
	mainWindow.on("closed", () => {
		mainWindow = null;
	});
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", () => {
	console.log("Window all closed");
	app.quit();
});
<commit_msg>The Browser instance should reflect our userAgent and Cookie changes on the network request<commit_after>/* Copyright (c) Royal Holloway, University of London | Contact Blake Loring (blake@parsed.uk), Duncan Mitchell (Duncan.Mitchell.2015@rhul.ac.uk), or Johannes Kinder (johannes.kinder@rhul.ac.uk) for details or support | LICENSE.md for license details */

const { app, BrowserWindow, session } = require("electron");

const TestCaseParameters = JSON.parse(process.argv[process.argv.length - 1]);

session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {

  function rif(offset, o2) {
    if (TestCaseParameters[offset]) { details.requestHeaders[o2] = TestCaseParameters[offset]; }
  }

  rif('userAgent', 'User-Agent');
  rif('cookie', 'Cookie');

  callback({ cancel: false, requestHeaders: details.requestHeaders });
});

app.commandLine.appendSwitch("ignore-certificate-errors");
app.commandLine.appendSwitch("disable-web-security");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

const createWindow = () => {
	// Create the browser window.
	mainWindow = new BrowserWindow({
		title: `ExpoSE ${process.argv[process.argv.length - 2]} ${process.argv[process.argv.length - 1]}`,
		width: 800,
		height: 600,
		webPreferences: {
			webSecurity: false
		}
	});

	mainWindow.on("page-title-updated", (e) => {
		e.preventDefault();
	});

	mainWindow.webContents.session.clearCache(function() {
		mainWindow.webContents.session.setProxy({proxyRules:"http://localhost:8080"}, function () {
			mainWindow.loadURL(process.argv[process.argv.length - 2]);
		});
	});

	// Emitted when the window is closed.
	mainWindow.on("closed", () => {
		mainWindow = null;
	});
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", () => {
	console.log("Window all closed");
	app.quit();
});
<|endoftext|>


<commit_before>var expect = chai.expect

describe('bacon.jquery', function() {
  describe('textFieldValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<input type="text" id="text" value="defaultVal">')
      field = $('#bacon-dom #text')
    })

    describe('when textField is not found', function() {
      it('returns empty string as value', function() {
        var model = Bacon.$.textFieldValue($('bacon-dom').find('.asdfqwer'))
        specifyValue(model, '')
      })
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
          var model = Bacon.$.textFieldValue(field, 'initVal')
          expect(field.val()).to.equal('initVal')
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.textFieldValue(field, 'initVal')
        specifyValue(model, 'initVal')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
          var model = Bacon.$.textFieldValue(field)
          expect(field.val()).to.equal('defaultVal')
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.textFieldValue(field)
        specifyValue(model, 'defaultVal')
      })
      describe('with empty default value', function() {
        it('waits for browser to autofill textfield', function(done) {
          field.val('')
          var model = Bacon.$.textFieldValue(field)
          model.filter(function (v) {
              return v
          }).onValue(function() {
            specifyValue(model, 'newVal')
            done()
          })
          field.val("newVal")
        })
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
          Bacon.$.textFieldValue(field).set('newVal')
          expect(field.val()).to.equal('newVal')
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.textFieldValue(field)
        field.val("newVal")
        field.trigger("keyup")
        specifyValue(model, "newVal")
      })

      it('ignores duplicates', function() {
        var model = Bacon.$.textFieldValue(field)
        field.val("newVal")
        field.trigger("keyup")
        specifyValue(model, "newVal")
        var values = collectValues(model)
        field.trigger("keyup")
        field.trigger("keyup")
        expect(values).to.deep.equal(["newVal"])
      })
    })
  })

  describe('checkBoxValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<input type="checkbox" id="checkbox">')
      field = $('#bacon-dom #checkbox')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
          var model = Bacon.$.checkBoxValue(field, true)
          expect(field.prop("checked")).to.equal(true)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.checkBoxValue(field, true)
        specifyValue(model, true)
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
          var model = Bacon.$.checkBoxValue(field)
          expect(field.prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.checkBoxValue(field)
        specifyValue(model, false)
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
          Bacon.$.checkBoxValue(field).set(true)
          expect(field.prop("checked")).to.equal(true)
      })
      it('leaves defaultChecked property as is', function() {
          Bacon.$.checkBoxValue(field).set(true)
          expect(field.prop("defaultChecked")).to.equal(false)
      })
    })
    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.checkBoxValue(field)
        field.trigger("click")
        specifyValue(model, true)
      })
    })
  })

  describe('selectValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<select id="select"><option value="a">A</option><option value="b" selected>B</option></select>')
      field = $('#bacon-dom #select')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.selectValue(field, 'a')
        expect(field.val()).to.equal('a')
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.selectValue(field, 'a')
        specifyValue(model, 'a')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.selectValue(field)
        expect(field.val()).to.equal('b')
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.selectValue(field)
        specifyValue(model, 'b')
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.selectValue(field).set('a')
        expect(field.val()).to.equal('a')
      })
    })
    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.selectValue(field)
        field.val("a")
        field.trigger("change")
        specifyValue(model, "a")
      })
    })
  })

  describe('selectValue without any options', function() {
    it('sets `null` as initial value of the model', function() {
      $('#bacon-dom').html('<select id="select"></select>')
      var model = Bacon.$.selectValue($('#bacon-dom #select'))
      specifyValue(model, null)
    })
  })

  describe('radioGroupValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('A<input type="radio" id="a" value="a">
B<input type="radio" id="b" value="b" checked>')
      field = $('#a,#b')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.radioGroupValue(field, 'a')
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.radioGroupValue(field, 'a')
        specifyValue(model, 'a')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.radioGroupValue(field)
        expect($("#b").prop("checked")).to.equal(true)
        expect($("#a").prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.radioGroupValue(field)
        specifyValue(model, 'b')
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.radioGroupValue(field).set('a')
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('leaves defaultChecked property as is', function() {
        Bacon.$.radioGroupValue(field).set('a')
        expect($("#a").prop("defaultChecked")).to.equal(false)
        expect($("#b").prop("defaultChecked")).to.equal(true)
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.radioGroupValue(field)
        $("#b").click()
        $("#a").click()
        specifyValue(model, "a")
      })
    })
  })

  describe('checkBoxGroupValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('A<input type="checkbox" id="a" value="a">
B<input type="checkbox" id="b" value="b" checked>')
      field = $('#a,#b')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.checkBoxGroupValue(field, ['a'])
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.checkBoxGroupValue(field, ['a'])
        specifyValue(model, ['a'])
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        expect($("#b").prop("checked")).to.equal(true)
        expect($("#a").prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        specifyValue(model, ['b'])
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.checkBoxGroupValue(field).set(['a', 'b'])
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(true)
      })
      it('leaves defaultChecked property as is', function() {
        Bacon.$.checkBoxGroupValue(field).set(['a', 'b'])
        expect($("#a").prop("defaultChecked")).to.equal(false)
        expect($("#b").prop("defaultChecked")).to.equal(true)
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        $("#a").trigger("click")
        specifyValue(model, ["a", "b"])
        $("#b").trigger("click")
        specifyValue(model, ["a"])
        $("#a").trigger("click")
        specifyValue(model, [])
      })
    })
  })

  testEventHelper('click')
  testEventHelper('keyup')
  testEventHelper('keydown')
  testEventHelper('mouseup')

  describe("AJAX", function() {
    $.mockjax({
      url: "/test",
      responseTime: 0,
      responseText: "good"
    })
    describe("Converts EventStream of requests into EventStream of responses", function() {
      expectStreamValues(Bacon.once({url:"/test"}).ajax(), ["good"])
    })
    describe("Converts Property of requests into EventStream of responses", function() {
      expectStreamValues(Bacon.once({url:"/test"}).toProperty().ajax(), ["good"])
    })
  })

})

function expectStreamValues(stream, expectedValues) {
  var values = []
  before(function(done) {
    stream.onValue(function(value) { values.push(value) })
    stream.onEnd(done)
  })
  it("is an EventStream", function() {
    expect(stream instanceof Bacon.EventStream).to.be.ok()
  })
  it("contains expected values", function() {
    expect(values).to.deep.equal(expectedValues)
  })
}

function testEventHelper(eventName) {
  var methodName = eventName + "E"
  describe(methodName, function() {
    it("captures DOM events as EventStream", function() {
      $('#bacon-dom').html('<input type="text" id="text">')
      var el = $('#bacon-dom #text')
      var stream = el[methodName]()
      var values = collectValues(stream)
      el[eventName]()
      expect(values.length).to.equal(1)
    })
  })
}

function specifyValue(obs, expected) {
  var gotIt = false
  var value
  obs.onValue(function(v) {
    gotIt = true
    value = v
  })
  expect(gotIt).to.equal(true)
  expect(value).to.deep.equal(expected)
}

function collectValues(observable) {
  var values = [];
  observable.onValue(function(value) {
    return values.push(value);
  });
  return values;
}
<commit_msg>re-order<commit_after>var expect = chai.expect

describe('bacon.jquery', function() {
  describe('textFieldValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<input type="text" id="text" value="defaultVal">')
      field = $('#bacon-dom #text')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
          var model = Bacon.$.textFieldValue(field, 'initVal')
          expect(field.val()).to.equal('initVal')
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.textFieldValue(field, 'initVal')
        specifyValue(model, 'initVal')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
          var model = Bacon.$.textFieldValue(field)
          expect(field.val()).to.equal('defaultVal')
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.textFieldValue(field)
        specifyValue(model, 'defaultVal')
      })
      describe('with empty default value', function() {
        it('waits for browser to autofill textfield', function(done) {
          field.val('')
          var model = Bacon.$.textFieldValue(field)
          model.filter(function (v) {
              return v
          }).onValue(function() {
            specifyValue(model, 'newVal')
            done()
          })
          field.val("newVal")
        })
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
          Bacon.$.textFieldValue(field).set('newVal')
          expect(field.val()).to.equal('newVal')
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.textFieldValue(field)
        field.val("newVal")
        field.trigger("keyup")
        specifyValue(model, "newVal")
      })

      it('ignores duplicates', function() {
        var model = Bacon.$.textFieldValue(field)
        field.val("newVal")
        field.trigger("keyup")
        specifyValue(model, "newVal")
        var values = collectValues(model)
        field.trigger("keyup")
        field.trigger("keyup")
        expect(values).to.deep.equal(["newVal"])
      })
    })

    describe('when element is not found', function() {
      it('returns empty string as value', function() {
        var model = Bacon.$.textFieldValue($('bacon-dom').find('.asdfqwer'))
        specifyValue(model, '')
      })
    })
  })

  describe('checkBoxValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<input type="checkbox" id="checkbox">')
      field = $('#bacon-dom #checkbox')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
          var model = Bacon.$.checkBoxValue(field, true)
          expect(field.prop("checked")).to.equal(true)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.checkBoxValue(field, true)
        specifyValue(model, true)
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
          var model = Bacon.$.checkBoxValue(field)
          expect(field.prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.checkBoxValue(field)
        specifyValue(model, false)
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
          Bacon.$.checkBoxValue(field).set(true)
          expect(field.prop("checked")).to.equal(true)
      })
      it('leaves defaultChecked property as is', function() {
          Bacon.$.checkBoxValue(field).set(true)
          expect(field.prop("defaultChecked")).to.equal(false)
      })
    })
    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.checkBoxValue(field)
        field.trigger("click")
        specifyValue(model, true)
      })
    })
  })

  describe('selectValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('<select id="select"><option value="a">A</option><option value="b" selected>B</option></select>')
      field = $('#bacon-dom #select')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.selectValue(field, 'a')
        expect(field.val()).to.equal('a')
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.selectValue(field, 'a')
        specifyValue(model, 'a')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.selectValue(field)
        expect(field.val()).to.equal('b')
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.selectValue(field)
        specifyValue(model, 'b')
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.selectValue(field).set('a')
        expect(field.val()).to.equal('a')
      })
    })
    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.selectValue(field)
        field.val("a")
        field.trigger("change")
        specifyValue(model, "a")
      })
    })
  })

  describe('selectValue without any options', function() {
    it('sets `null` as initial value of the model', function() {
      $('#bacon-dom').html('<select id="select"></select>')
      var model = Bacon.$.selectValue($('#bacon-dom #select'))
      specifyValue(model, null)
    })
  })

  describe('radioGroupValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('A<input type="radio" id="a" value="a">
B<input type="radio" id="b" value="b" checked>')
      field = $('#a,#b')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.radioGroupValue(field, 'a')
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.radioGroupValue(field, 'a')
        specifyValue(model, 'a')
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.radioGroupValue(field)
        expect($("#b").prop("checked")).to.equal(true)
        expect($("#a").prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.radioGroupValue(field)
        specifyValue(model, 'b')
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.radioGroupValue(field).set('a')
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('leaves defaultChecked property as is', function() {
        Bacon.$.radioGroupValue(field).set('a')
        expect($("#a").prop("defaultChecked")).to.equal(false)
        expect($("#b").prop("defaultChecked")).to.equal(true)
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.radioGroupValue(field)
        $("#b").click()
        $("#a").click()
        specifyValue(model, "a")
      })
    })
  })

  describe('checkBoxGroupValue', function() {
    var field
    beforeEach(function() {
      $('#bacon-dom').html('A<input type="checkbox" id="a" value="a">
B<input type="checkbox" id="b" value="b" checked>')
      field = $('#a,#b')
    })

    describe('with initVal', function() {
      it('sets value to DOM', function() {
        var model = Bacon.$.checkBoxGroupValue(field, ['a'])
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(false)
      })
      it('sets the initVal as the initial value of the model', function() {
        var model = Bacon.$.checkBoxGroupValue(field, ['a'])
        specifyValue(model, ['a'])
      })
    })

    describe('without initVal', function() {
      it('leaves DOM unaffected', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        expect($("#b").prop("checked")).to.equal(true)
        expect($("#a").prop("checked")).to.equal(false)
      })
      it('uses value from DOM as initial value of the model', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        specifyValue(model, ['b'])
      })
    })

    describe('when setting value of model', function() {
      it('sets value to DOM', function() {
        Bacon.$.checkBoxGroupValue(field).set(['a', 'b'])
        expect($("#a").prop("checked")).to.equal(true)
        expect($("#b").prop("checked")).to.equal(true)
      })
      it('leaves defaultChecked property as is', function() {
        Bacon.$.checkBoxGroupValue(field).set(['a', 'b'])
        expect($("#a").prop("defaultChecked")).to.equal(false)
        expect($("#b").prop("defaultChecked")).to.equal(true)
      })
    })

    describe('when DOM value changes', function() {
      it('updates value of model', function() {
        var model = Bacon.$.checkBoxGroupValue(field)
        $("#a").trigger("click")
        specifyValue(model, ["a", "b"])
        $("#b").trigger("click")
        specifyValue(model, ["a"])
        $("#a").trigger("click")
        specifyValue(model, [])
      })
    })
  })

  testEventHelper('click')
  testEventHelper('keyup')
  testEventHelper('keydown')
  testEventHelper('mouseup')

  describe("AJAX", function() {
    $.mockjax({
      url: "/test",
      responseTime: 0,
      responseText: "good"
    })
    describe("Converts EventStream of requests into EventStream of responses", function() {
      expectStreamValues(Bacon.once({url:"/test"}).ajax(), ["good"])
    })
    describe("Converts Property of requests into EventStream of responses", function() {
      expectStreamValues(Bacon.once({url:"/test"}).toProperty().ajax(), ["good"])
    })
  })

})

function expectStreamValues(stream, expectedValues) {
  var values = []
  before(function(done) {
    stream.onValue(function(value) { values.push(value) })
    stream.onEnd(done)
  })
  it("is an EventStream", function() {
    expect(stream instanceof Bacon.EventStream).to.be.ok()
  })
  it("contains expected values", function() {
    expect(values).to.deep.equal(expectedValues)
  })
}

function testEventHelper(eventName) {
  var methodName = eventName + "E"
  describe(methodName, function() {
    it("captures DOM events as EventStream", function() {
      $('#bacon-dom').html('<input type="text" id="text">')
      var el = $('#bacon-dom #text')
      var stream = el[methodName]()
      var values = collectValues(stream)
      el[eventName]()
      expect(values.length).to.equal(1)
    })
  })
}

function specifyValue(obs, expected) {
  var gotIt = false
  var value
  obs.onValue(function(v) {
    gotIt = true
    value = v
  })
  expect(gotIt).to.equal(true)
  expect(value).to.deep.equal(expected)
}

function collectValues(observable) {
  var values = [];
  observable.onValue(function(value) {
    return values.push(value);
  });
  return values;
}
<|endoftext|>


<commit_before>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	{src} = require('config'),
	{resolve} = require('@pzlr/build-core');

/**
 * Parameters for webpack.resolve
 */
module.exports = {
	extensions: ['.ts', '.js', '.json'],
	modules: [resolve.sourceDir, src.cwd(), ...resolve.rootDependencies, src.lib()],
	alias: include('build/alias.webpack')
};
<commit_msg>:wrench:<commit_after>'use strict';

/*!
 * V4Fire Client Core
 * https://github.com/V4Fire/Client
 *
 * Released under the MIT license
 * https://github.com/V4Fire/Client/blob/master/LICENSE
 */

const
	{src} = require('config'),
	{resolve} = require('@pzlr/build-core');

/**
 * Parameters for webpack.resolve
 */
module.exports = {
	extensions: ['.ts', '.js', '.json'],
	modules: [resolve.blockSync(), resolve.sourceDir, src.cwd(), ...resolve.rootDependencies, src.lib()],
	alias: include('build/alias.webpack')
};
<|endoftext|>


<commit_before>/*jshint node: true */
'use strict';
var buildRules = require('../configure');
module.exports = function (grunt) {
	grunt.registerMultiTask('configure', function () {

		var done = this.async();
		var options = this.options({
      rules: ['lib/rules/**/*.json'],
      checks: ['lib/checks/**/*.json'],
      tools: ['lib/tools/**/*.json'],
      style: ['lib/**/*.less'],
      misc: ['lib/misc/**/*.json'],
			blacklist: ['metadata'],
			tags: '',
      locale: null
		});
		var that = this;
		this.files.forEach(function (file) {
			var commons = file.src[0];
			buildRules(grunt, options, commons, function (result) {
				grunt.file.write(that.data.dest.auto, 'axe._load(' + result.auto + ');');
				grunt.file.write(that.data.dest.descriptions, result.descriptions);
				done();
			});
		});
	});
};
<commit_msg>some code cleanup<commit_after>/*jshint node: true */
'use strict';
var buildRules = require('../configure');
module.exports = function (grunt) {
	grunt.registerMultiTask('configure', function () {

		var done = this.async();
		var options = this.options({
      rules: ['lib/rules/**/*.json'],
      checks: ['lib/checks/**/*.json'],
      tools: ['lib/tools/**/*.json'],
      style: ['lib/**/*.less'],
      misc: ['lib/misc/**/*.json'],
			blacklist: ['metadata'],
			tags: ''
		});
		var that = this;
		this.files.forEach(function (file) {
			var commons = file.src[0];
			buildRules(grunt, options, commons, function (result) {
				grunt.file.write(that.data.dest.auto, 'axe._load(' + result.auto + ');');
				grunt.file.write(that.data.dest.descriptions, result.descriptions);
				done();
			});
		});
	});
};
<|endoftext|>


<commit_before>"use strict";
var gulp = require('gulp');
var gutil = require('gulp-util');
var through = require('through2');
var path = require('path');
var File = require('vinyl');
var fs = require('fs');
var grayMatter = require('gray-matter');

var config = require('../config');
var handleError = require('../utils/handle-error');

module.exports = function(outputFile) {

  var files = [];
  var fileList = [];

  return through.obj(function(file, enc, cb) {

    if (file.isNull()) {
      cb(null, file);
      return;
    }

    if (file.isStream()) {
      cb(new gutil.PluginError('pageList', 'Streams not supported'));
      return;
    }

    files.push(file);

    var originalPath = file.path;
    var relativePath = path.relative(config.paths.views.pages, originalPath);
    var outputPath = relativePath.replace('.hbs', '.html');

    var contents = fs.readFileSync(path.relative(process.cwd(), originalPath), 'utf8');
    var pageData = grayMatter(contents);

    pageData.url = outputPath;

    if (pageData.data.styleguide === true) {
      fileList.unshift(data);
    } else {
      fileList.push(data);
    }

    this.push(file);
    cb();
  }, function(cb) {
    var fileListFile = new File({
      path: outputFile,
      contents: new Buffer(JSON.stringify(fileList, null, '  '))
    });

    this.push(fileListFile);
    cb();
  });
};
<commit_msg>Fix variable names in page-list<commit_after>"use strict";
var gulp = require('gulp');
var gutil = require('gulp-util');
var through = require('through2');
var path = require('path');
var File = require('vinyl');
var fs = require('fs');
var grayMatter = require('gray-matter');

var config = require('../config');
var handleError = require('../utils/handle-error');

module.exports = function(outputFile) {

  var files = [];
  var fileList = [];

  return through.obj(function(file, enc, cb) {

    if (file.isNull()) {
      cb(null, file);
      return;
    }

    if (file.isStream()) {
      cb(new gutil.PluginError('pageList', 'Streams not supported'));
      return;
    }

    files.push(file);

    var originalPath = file.path;
    var relativePath = path.relative(config.paths.views.pages, originalPath);
    var outputPath = relativePath.replace('.hbs', '.html');

    var contents = fs.readFileSync(path.relative(process.cwd(), originalPath), 'utf8');
    var pageData = grayMatter(contents);

    pageData.url = outputPath;

    if (pageData.data.styleguide === true) {
      fileList.unshift(pageData);
    } else {
      fileList.push(pageData);
    }

    this.push(file);
    cb();
  }, function(cb) {
    var fileListFile = new File({
      path: outputFile,
      contents: new Buffer(JSON.stringify(fileList, null, '  '))
    });

    this.push(fileListFile);
    cb();
  });
};
<|endoftext|>


<commit_before>// app.js
// Copyright 2012 Stripe Consulting
// Author: Damian Mullins
// Updated: 31/07/2012

jQuery(document).ready(function ($) {

  var keyword = $("input[name=url]").val();

  if(!keyword){
    $("input[name^='title']").keyup(function(){
       var text = $(this).val();
           text = text.toLowerCase();
           text = text.replace(/[^a-zA-Z0-9]+/g,'-');
       $("input[name=url]").val(text);
     });
  }

});<commit_msg>Confirmation alert box when deleting a post<commit_after>// app.js
// Copyright 2012 Stripe Consulting
// Author: Damian Mullins
// Updated: 31/07/2012

jQuery(document).ready(function ($) {

  var keyword = $("input[name=url]").val();

  if(!keyword){
    $("input[name^='title']").keyup(function(){
       var text = $(this).val();
           text = text.toLowerCase();
           text = text.replace(/[^a-zA-Z0-9]+/g,'-');
       $("input[name=url]").val(text);
     });
  }

	$('.delete').click(function() {
		return confirm('Last chance! Are you sure you would like to delete this post?');
	})
});<|endoftext|>


<commit_before>/*
 * File:        FixedColumns.js
 * Version:     1.0.1.dev
 * Description: "Fix" columns on the left of a scrolling DataTable
 * Author:      Allan Jardine (www.sprymedia.co.uk)
 * Created:     Sat Sep 18 09:28:54 BST 2010
 * Language:    Javascript
 * License:     GPL v2 or BSD 3 point style
 * Project:     Just a little bit of fun - enjoy :-)
 * Contact:     www.sprymedia.co.uk/contact
 *
 * Copyright 2010 Allan Jardine, all rights reserved.
 */

var FixedColumns = function ( oDT, oInit ) {
	/* Sanity check - you just know it will happen */
	if ( typeof this._fnConstruct != 'function' )
	{
		alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
		return;
	}

	if ( typeof oInit == 'undefined' )
	{
		oInit = {};
	}

	/**
	 * @namespace Settings object which contains customisable information for FixedColumns instance
	 */
	this.s = {
		/**
		 * DataTables settings objects
     *  @property dt
     *  @type     object
     *  @default  null
		 */
		"dt": oDT.fnSettings(),

		/**
		 * Number of columns to fix in position
     *  @property columns
     *  @type     int
     *  @default  1
		 */
		"columns": 1
	};

	/**
	 * @namespace Common and useful DOM elements for the class instance
	 */
	this.dom = {
		/**
		 * DataTables scrolling element
		 *  @property scroller
		 *  @type     node
		 *  @default  null
		 */
		"scroller": null,

		/**
		 * DataTables header table
		 *  @property header
		 *  @type     node
		 *  @default  null
		 */
		"header": null,

		/**
		 * DataTables body table
		 *  @property body
		 *  @type     node
		 *  @default  null
		 */
		"body": null,

		/**
		 * DataTables footer table
		 *  @property footer
		 *  @type     node
		 *  @default  null
		 */
		"footer": null,

		/**
		 * @namespace Cloned table nodes
		 */
		"clone": {
			/**
			 * Cloned header table
			 *  @property header
			 *  @type     node
			 *  @default  null
			 */
			"header": null,

			/**
			 * Cloned body table
			 *  @property body
			 *  @type     node
			 *  @default  null
			 */
			"body": null,

			/**
			 * Cloned footer table
			 *  @property footer
			 *  @type     node
			 *  @default  null
			 */
			"footer": null
		}
	};

	/* Let's do it */
	this._fnConstruct( oInit );
};

FixedColumns.prototype = {
	/**
	 * Initialisation for FixedColumns
	 *  @method  _fnConstruct
	 *  @param   {Object} oInit User settings for initialisation
	 *  @returns void
	 */
	"_fnConstruct": function ( oInit )
	{
		var that = this;

		/* Sanity checking */
		if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
		     this.s.dt.oInstance.fnVersionCheck( '1.7.0' ) !== true )
		{
			alert( "FixedColumns 2 required DataTables 1.7.0 or later. "+
				"Please upgrade your DataTables installation" );
			return;
		}

		if ( this.s.dt.oScroll.sX === "" )
		{
			this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
				"x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
				"column fixing when scrolling is not enabled" );
			return;
		}

		if ( typeof oInit.columns != 'undefined' )
		{
			if ( oInit.columns < 1 )
			{
				this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
					"columns to be fixed), so no action will be taken" );
				return;
			}
			this.s.columns = oInit.columns;
		}

		/* Set up the DOM as we need it and cache nodes */
		this.dom.body = this.s.dt.nTable;
		this.dom.scroller = this.dom.body.parentNode;
		this.dom.scroller.style.position = "relative";

		this.dom.header = this.s.dt.nTHead.parentNode;
		this.dom.header.parentNode.parentNode.style.position = "relative";

		if ( this.s.dt.nTFoot )
		{
			this.dom.footer = this.s.dt.nTFoot.parentNode;
			this.dom.footer.parentNode.parentNode.style.position = "relative";
		}

		/* Event handlers */
		$(this.dom.scroller).scroll( function () {
			that._fnScroll.call( that );
		} );

		this.s.dt.aoDrawCallback.push( {
			"fn": function () {
				that._fnClone.call( that );
				that._fnScroll.call( that );
			},
			"sName": "FixedColumns"
		} );

		/* Get things right to start with */
		this._fnClone();
		this._fnScroll();
	},

	/**
	 * Clone the DataTable nodes and place them in the DOM (sized correctly)
	 *  @method  _fnClone
	 *  @returns void
	 *  @private
	 */
	"_fnClone": function ()
	{
		var
			that = this,
			iTableWidth = 0,
			aiCellWidth = [],
			i, iLen, jq,
			bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));

		/* Grab the widths that we are going to need */
		for ( i=0, iLen=this.s.columns ; i<iLen ; i++ )
		{
			jq = $('thead th:eq('+i+')', this.dom.header);
			iTableWidth += jq.outerWidth();
			aiCellWidth.push( jq.width() );
		}

		/* Header */
		$(this.dom.header).css( 'height', 'auto' ); /* Remove heights for Firefox bug */

		if ( this.dom.clone.header !== null )
		{
			this.dom.clone.header.parentNode.removeChild( this.dom.clone.header );
		}
		this.dom.clone.header = $(this.dom.header).clone(true)[0];
		this.dom.clone.header.className += " FixedColumns_Cloned";

		$('thead tr:eq(0)', this.dom.clone.header).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );
		$(this.dom.header).height( $(that.dom.header).height() ); /* Needed for a Firefox bug */
		$(this.dom.clone.header).height( $(that.dom.header).height() );

		$('thead tr:gt(0)', this.dom.clone.header).remove();

		$('thead th', this.dom.clone.header).each( function (i) {
			this.style.width = aiCellWidth[i]+"px";
		} );

		this.dom.clone.header.style.position = "absolute";
		this.dom.clone.header.style.top = "0px";
		this.dom.clone.header.style.left = "0px";
		this.dom.clone.header.style.width = iTableWidth+"px";
		this.dom.header.parentNode.appendChild( this.dom.clone.header );

		/* Body */
		/* Remove any heights which have been applied already and let the browser figure it out */
		$('tbody tr', that.dom.body).css('height', 'auto');

		if ( this.dom.clone.body !== null )
		{
			this.dom.clone.body.parentNode.removeChild( this.dom.clone.body );
		}
		this.dom.clone.body = $(this.dom.body).clone(true)[0];
		this.dom.clone.body.className += " FixedColumns_Cloned";
		if ( this.dom.clone.body.getAttribute('id') !== null )
		{
			this.dom.clone.body.removeAttribute('id');
		}

		$('thead tr:eq(0)', this.dom.clone.body).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );

		$('thead tr:gt(0)', this.dom.clone.body).remove();

		var iTopPadding = $('tbody tr:eq(0) td:eq(0)', that.dom.body).css('paddingTop');
		iTopPadding = parseInt( iTopPadding.replace('px', ''), 10 );

		var iBottomPadding = $('tbody tr:eq(0) td:eq(0)', that.dom.body).css('paddingBottom');
		iBottomPadding = parseInt( iBottomPadding.replace('px', ''), 10 );

		var iTopBorder = $('tbody tr:eq(0) td:eq(0)', that.dom.body).css('borderTopWidth');
		iTopBorder = parseInt( iTopBorder.replace('px', ''), 10 );

		var iBottomBorder = $('tbody tr:eq(0) td:eq(0)', that.dom.body).css('borderBottomWidth');
		iBottomBorder = parseInt( iBottomBorder.replace('px', ''), 10 );

		var iBoxHack = iTopPadding + iBottomPadding + iTopBorder + iBottomBorder;

		/* Remove cells which are not needed and copy the height from the original table */
		$('tbody tr', this.dom.clone.body).each( function (k) {
			$('td:gt('+(that.s.columns-1)+')', this).remove();

			/* Can we use some kind of object detection here?! This is very nasty - damn browsers */
			if ( $.browser.mozilla || $.browser.opera )
			{
				$('td', this).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() );
			}
			else
			{
				$('td', this).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() - iBoxHack );
			}

			/* It's only really IE8 and Firefox which need this, but to simplify, lets apply to all.
			 * The reason it is needed at all is sub-pixel rounded, which is done differently in every
			 * browser... Except of course IE6 and IE7 - applying the height to them causes the cell
			 * size to grow, but they don't mess around with sub-pixels so we can do nothing.
			 */
			if ( !bRubbishOldIE )
			{
				$('tbody tr:eq('+k+')', that.dom.body).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() );
			}
		} );

		$('tfoot tr:eq(0)', this.dom.clone.body).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );

		$('tfoot tr:gt(0)', this.dom.clone.body).remove();

		this.dom.clone.body.style.position = "absolute";
		this.dom.clone.body.style.top = "0px";
		this.dom.clone.body.style.left = "0px";
		this.dom.clone.body.style.width = iTableWidth+"px";
		this.dom.body.parentNode.appendChild( this.dom.clone.body );

		/* Footer */
		if ( this.s.dt.nTFoot !== null )
		{
			$(this.dom.footer).css( 'height', 'auto' ); /* Remove heights for Firefox bug */

			if ( this.dom.clone.footer !== null )
			{
				this.dom.clone.footer.parentNode.removeChild( this.dom.clone.footer );
			}
			this.dom.clone.footer = $(this.dom.footer).clone(true)[0];
			this.dom.clone.footer.className += " FixedColumns_Cloned";

			$('tfoot tr:eq(0)', this.dom.clone.footer).each( function () {
				$('th:gt('+(that.s.columns-1)+')', this).remove();
				$(this).height( $(that.dom.footer).height() );
			} );
			$(this.dom.footer).height( $(that.dom.footer).height() );
			$(this.dom.clone.footer).height( $(that.dom.footer).height() );

			$('tfoot tr:gt(0)', this.dom.clone.footer).remove();

			$('tfoot th', this.dom.clone.footer).each( function (i) {
				this.style.width = aiCellWidth[i]+"px";
			} );

			this.dom.clone.footer.style.position = "absolute";
			this.dom.clone.footer.style.top = "0px";
			this.dom.clone.footer.style.left = "0px";
			this.dom.clone.footer.style.width = iTableWidth+"px";
			this.dom.footer.parentNode.appendChild( this.dom.clone.footer );
		}
	},

	/**
	 * Set the absolute position of the fixed column tables when scrolling the DataTable
	 *  @method  _fnScroll
	 *  @returns void
	 *  @private
	 */
	"_fnScroll": function ()
	{
		var iScrollLeft = $(this.dom.scroller).scrollLeft();

		this.dom.clone.header.style.left = iScrollLeft+"px";
		this.dom.clone.body.style.left = iScrollLeft+"px";
		if ( this.dom.footer )
		{
			this.dom.clone.footer.style.left = iScrollLeft+"px";
		}
	}
};
<commit_msg>Fixed: Issue with IE and the height of the header / footer elements (noted when using rowspan - 2815). Need to apply the height of the table to the TR element, and not the cell or the table.<commit_after>/*
 * File:        FixedColumns.js
 * Version:     1.0.1.dev
 * Description: "Fix" columns on the left of a scrolling DataTable
 * Author:      Allan Jardine (www.sprymedia.co.uk)
 * Created:     Sat Sep 18 09:28:54 BST 2010
 * Language:    Javascript
 * License:     GPL v2 or BSD 3 point style
 * Project:     Just a little bit of fun - enjoy :-)
 * Contact:     www.sprymedia.co.uk/contact
 *
 * Copyright 2010 Allan Jardine, all rights reserved.
 */

var FixedColumns = function ( oDT, oInit ) {
	/* Sanity check - you just know it will happen */
	if ( typeof this._fnConstruct != 'function' )
	{
		alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
		return;
	}

	if ( typeof oInit == 'undefined' )
	{
		oInit = {};
	}

	/**
	 * @namespace Settings object which contains customisable information for FixedColumns instance
	 */
	this.s = {
		/**
		 * DataTables settings objects
     *  @property dt
     *  @type     object
     *  @default  null
		 */
		"dt": oDT.fnSettings(),

		/**
		 * Number of columns to fix in position
     *  @property columns
     *  @type     int
     *  @default  1
		 */
		"columns": 1
	};

	/**
	 * @namespace Common and useful DOM elements for the class instance
	 */
	this.dom = {
		/**
		 * DataTables scrolling element
		 *  @property scroller
		 *  @type     node
		 *  @default  null
		 */
		"scroller": null,

		/**
		 * DataTables header table
		 *  @property header
		 *  @type     node
		 *  @default  null
		 */
		"header": null,

		/**
		 * DataTables body table
		 *  @property body
		 *  @type     node
		 *  @default  null
		 */
		"body": null,

		/**
		 * DataTables footer table
		 *  @property footer
		 *  @type     node
		 *  @default  null
		 */
		"footer": null,

		/**
		 * @namespace Cloned table nodes
		 */
		"clone": {
			/**
			 * Cloned header table
			 *  @property header
			 *  @type     node
			 *  @default  null
			 */
			"header": null,

			/**
			 * Cloned body table
			 *  @property body
			 *  @type     node
			 *  @default  null
			 */
			"body": null,

			/**
			 * Cloned footer table
			 *  @property footer
			 *  @type     node
			 *  @default  null
			 */
			"footer": null
		}
	};

	/* Let's do it */
	this._fnConstruct( oInit );
};

FixedColumns.prototype = {
	/**
	 * Initialisation for FixedColumns
	 *  @method  _fnConstruct
	 *  @param   {Object} oInit User settings for initialisation
	 *  @returns void
	 */
	"_fnConstruct": function ( oInit )
	{
		var that = this;

		/* Sanity checking */
		if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
		     this.s.dt.oInstance.fnVersionCheck( '1.7.0' ) !== true )
		{
			alert( "FixedColumns 2 required DataTables 1.7.0 or later. "+
				"Please upgrade your DataTables installation" );
			return;
		}

		if ( this.s.dt.oScroll.sX === "" )
		{
			this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
				"x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
				"column fixing when scrolling is not enabled" );
			return;
		}

		if ( typeof oInit.columns != 'undefined' )
		{
			if ( oInit.columns < 1 )
			{
				this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
					"columns to be fixed), so no action will be taken" );
				return;
			}
			this.s.columns = oInit.columns;
		}

		/* Set up the DOM as we need it and cache nodes */
		this.dom.body = this.s.dt.nTable;
		this.dom.scroller = this.dom.body.parentNode;
		this.dom.scroller.style.position = "relative";

		this.dom.header = this.s.dt.nTHead.parentNode;
		this.dom.header.parentNode.parentNode.style.position = "relative";

		if ( this.s.dt.nTFoot )
		{
			this.dom.footer = this.s.dt.nTFoot.parentNode;
			this.dom.footer.parentNode.parentNode.style.position = "relative";
		}

		/* Event handlers */
		$(this.dom.scroller).scroll( function () {
			that._fnScroll.call( that );
		} );

		this.s.dt.aoDrawCallback.push( {
			"fn": function () {
				that._fnClone.call( that );
				that._fnScroll.call( that );
			},
			"sName": "FixedColumns"
		} );

		/* Get things right to start with */
		this._fnClone();
		this._fnScroll();
	},

	/**
	 * Clone the DataTable nodes and place them in the DOM (sized correctly)
	 *  @method  _fnClone
	 *  @returns void
	 *  @private
	 */
	"_fnClone": function ()
	{
		var
			that = this,
			iTableWidth = 0,
			aiCellWidth = [],
			i, iLen, jq,
			bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));

		/* Grab the widths that we are going to need */
		for ( i=0, iLen=this.s.columns ; i<iLen ; i++ )
		{
			jq = $('thead th:eq('+i+')', this.dom.header);
			iTableWidth += jq.outerWidth();
			aiCellWidth.push( jq.width() );
		}

		/* Header */
		if ( this.dom.clone.header !== null )
		{
			this.dom.clone.header.parentNode.removeChild( this.dom.clone.header );
		}
		this.dom.clone.header = $(this.dom.header).clone(true)[0];
		this.dom.clone.header.className += " FixedColumns_Cloned";

		$('thead tr:eq(0)', this.dom.clone.header).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );
		$('tr', this.dom.clone.header).height( $(that.dom.header).height() );

		$('thead tr:gt(0)', this.dom.clone.header).remove();

		$('thead th', this.dom.clone.header).each( function (i) {
			this.style.width = aiCellWidth[i]+"px";
		} );

		this.dom.clone.header.style.position = "absolute";
		this.dom.clone.header.style.top = "0px";
		this.dom.clone.header.style.left = "0px";
		this.dom.clone.header.style.width = iTableWidth+"px";
		this.dom.header.parentNode.appendChild( this.dom.clone.header );

		/* Body */
		/* Remove any heights which have been applied already and let the browser figure it out */
		$('tbody tr', that.dom.body).css('height', 'auto');

		if ( this.dom.clone.body !== null )
		{
			this.dom.clone.body.parentNode.removeChild( this.dom.clone.body );
		}
		this.dom.clone.body = $(this.dom.body).clone(true)[0];
		this.dom.clone.body.className += " FixedColumns_Cloned";
		if ( this.dom.clone.body.getAttribute('id') !== null )
		{
			this.dom.clone.body.removeAttribute('id');
		}

		$('thead tr:eq(0)', this.dom.clone.body).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );

		$('thead tr:gt(0)', this.dom.clone.body).remove();

		var jqBoxHack = $('tbody tr:eq(0) td:eq(0)', that.dom.body);
		var iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height();

		/* Remove cells which are not needed and copy the height from the original table */
		$('tbody tr', this.dom.clone.body).each( function (k) {
			$('td:gt('+(that.s.columns-1)+')', this).remove();

			/* Can we use some kind of object detection here?! This is very nasty - damn browsers */
			if ( $.browser.mozilla || $.browser.opera )
			{
				$('td', this).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() );
			}
			else
			{
				$('td', this).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() - iBoxHack );
			}

			/* It's only really IE8 and Firefox which need this, but to simplify, lets apply to all.
			 * The reason it is needed at all is sub-pixel rounded, which is done differently in every
			 * browser... Except of course IE6 and IE7 - applying the height to them causes the cell
			 * size to grow, but they don't mess around with sub-pixels so we can do nothing.
			 */
			if ( !bRubbishOldIE )
			{
				$('tbody tr:eq('+k+')', that.dom.body).height( $('tbody tr:eq('+k+')', that.dom.body).outerHeight() );
			}
		} );

		$('tfoot tr:eq(0)', this.dom.clone.body).each( function () {
			$('th:gt('+(that.s.columns-1)+')', this).remove();
		} );

		$('tfoot tr:gt(0)', this.dom.clone.body).remove();

		this.dom.clone.body.style.position = "absolute";
		this.dom.clone.body.style.top = "0px";
		this.dom.clone.body.style.left = "0px";
		this.dom.clone.body.style.width = iTableWidth+"px";
		this.dom.body.parentNode.appendChild( this.dom.clone.body );

		/* Footer */
		if ( this.s.dt.nTFoot !== null )
		{
			if ( this.dom.clone.footer !== null )
			{
				this.dom.clone.footer.parentNode.removeChild( this.dom.clone.footer );
			}
			this.dom.clone.footer = $(this.dom.footer).clone(true)[0];
			this.dom.clone.footer.className += " FixedColumns_Cloned";

			$('tfoot tr:eq(0)', this.dom.clone.footer).each( function () {
				$('th:gt('+(that.s.columns-1)+')', this).remove();
				$(this).height( $(that.dom.footer).height() );
			} );
			$('tr', this.dom.clone.footer).height( $(that.dom.footer).height() );

			$('tfoot tr:gt(0)', this.dom.clone.footer).remove();

			$('tfoot th', this.dom.clone.footer).each( function (i) {
				this.style.width = aiCellWidth[i]+"px";
			} );

			this.dom.clone.footer.style.position = "absolute";
			this.dom.clone.footer.style.top = "0px";
			this.dom.clone.footer.style.left = "0px";
			this.dom.clone.footer.style.width = iTableWidth+"px";
			this.dom.footer.parentNode.appendChild( this.dom.clone.footer );
		}
	},

	/**
	 * Set the absolute position of the fixed column tables when scrolling the DataTable
	 *  @method  _fnScroll
	 *  @returns void
	 *  @private
	 */
	"_fnScroll": function ()
	{
		var iScrollLeft = $(this.dom.scroller).scrollLeft();

		this.dom.clone.header.style.left = iScrollLeft+"px";
		this.dom.clone.body.style.left = iScrollLeft+"px";
		if ( this.dom.footer )
		{
			this.dom.clone.footer.style.left = iScrollLeft+"px";
		}
	}
};
<|endoftext|>


<commit_before>/*
 * WINDOW VIEW
 * TODO: Break it up :/
 */

'use strict';

+function(window, $, _, notify) {

    window.LCB = window.LCB || {};

    window.LCB.WindowView = Backbone.View.extend({
        el: 'html',
        focus: true,
        count: 0,
        mentions: 0,
        countFavicon: new Favico({
            position: 'down',
            animation: 'none',
            bgColor: '#b94a48'
        }),
        mentionsFavicon: new Favico({
            position: 'left',
            animation: 'none',
            bgColor: '#f22472'
        }),
        initialize: function(options) {

            var that = this;

            this.client = options.client;
            this.rooms = options.rooms;
            this.originalTitle = this.$('title').text();
            this.title = this.originalTitle;

            $(window).on('focus blur', _.bind(this.onFocusBlur, this));

            this.rooms.current.on('change:id', function(current, id) {
                var room = this.rooms.get(id),
                    title = room ? room.get('name') : 'Rooms';
                this.updateTitle(title);
            }, this);

            this.rooms.on('change:name', function(room) {
                if (room.id !== this.rooms.current.get('id')) {
                    return;
                }
                this.updateTitle(room.get('name'));
            }, this);

            this.rooms.on('messages:new', this.onNewMessage, this);

            // Last man standing
            _.defer(function() {
                that.updateTitle();
            });

        },
        onFocusBlur: function(e) {
            this.focus = (e.type === 'focus');
            if (this.focus) {
                clearInterval(this.titleTimer);
                clearInterval(this.faviconBadgeTimer);
                this.count = 0;
                this.mentions = 0;
                this.titleTimer = false;
                this.titleTimerFlip = false;
                this.faviconBadgeTimer = false;
                this.faviconBadgeTimerFlip = false;
                this.updateTitle();
                this.mentionsFavicon.reset();
            }
        },
        onNewMessage: function(message) {
            if (this.focus || message.historical || message.owner.id === this.client.user.id) {
                return;
            }
            this.countMessage(message);
            this.flashTitle()
            this.flashFaviconBadge();
        },
        countMessage: function(message) {
            ++this.count;
            message.mentioned && ++this.mentions;
        },
        flashTitle: function() {
            var titlePrefix = '';
            if (this.count > 0) {
                titlePrefix += '(' + parseInt(this.count);
                if (this.mentions > 0) {
                    titlePrefix += '/' + parseInt(this.mentions) + '@';
                }
                titlePrefix += ') ';
            }
            this.$('title').html(titlePrefix + this.title);
        },
        flashFaviconBadge: function() {
            if (!this.faviconBadgeTimer) {
                this._flashFaviconBadge();
                var flashFaviconBadge = _.bind(this._flashFaviconBadge, this);
                this.faviconBadgeTimer = setInterval(flashFaviconBadge, 1 * 2000);
            }
        },
        _flashFaviconBadge: function() {
            if (this.mentions > 0 && this.faviconBadgeTimerFlip) {
                this.mentionsFavicon.badge(this.mentions);
            } else {
                this.countFavicon.badge(this.count);
            }
            this.faviconBadgeTimerFlip = !this.faviconBadgeTimerFlip;
        },
        updateTitle: function(name) {
            if (!name) {
                var room = this.rooms.get(this.rooms.current.get('id'));
                name = (room && room.get('name')) || 'Rooms';
            }
            if (name) {
                this.title = $('<pre />').text(name).html() +
                ' · ' + this.originalTitle;
            } else {
                this.title = this.originalTitle;
            }
            this.$('title').html(this.title);
        }
    });

    window.LCB.HotKeysView = Backbone.View.extend({
        el: 'html',
        keys: {
            'up+shift+alt down+shift+alt': 'nextRoom',
            's+shift+alt': 'toggleRoomSidebar',
            'g+shift+alt': 'openGiphyModal',
            'space+shift+alt': 'recallRoom'
        },
        initialize: function(options) {
            this.client = options.client;
            this.rooms = options.rooms;
        },
        nextRoom: function(e) {
            var method = e.keyCode === 40 ? 'next' : 'prev',
                selector = e.keyCode === 40 ? 'first' : 'last',
                $next = this.$('.lcb-tabs').find('[data-id].selected')[method]();
            if ($next.length === 0) {
                $next = this.$('.lcb-tabs').find('[data-id]:' + selector);
            }
            this.client.events.trigger('rooms:switch', $next.data('id'));
        },
        recallRoom: function() {
            this.client.events.trigger('rooms:switch', this.rooms.last.get('id'));
        },
        toggleRoomSidebar: function(e) {
            e.preventDefault();
            var view = this.client.view.panes.views[this.rooms.current.get('id')];
            view && view.toggleSidebar && view.toggleSidebar();
        },
        openGiphyModal: function(e) {
            if (this.client.options.giphyEnabled) {
                e.preventDefault();
                $('.lcb-giphy').modal('show');
            }
        }
    });

    window.LCB.DesktopNotificationsView = Backbone.View.extend({
        focus: true,
        openNotifications: [],
        openMentions: [],
        initialize: function(options) {
            notify.config({
                pageVisibility: false
            });
            this.client = options.client;
            this.rooms = options.rooms;
            $(window).on('focus blur unload', _.bind(this.onFocusBlur, this));
            this.rooms.on('messages:new', this.onNewMessage, this);
        },
        onFocusBlur: function(e) {
            this.focus = (e.type === 'focus');
            _.each(_.merge(this.openNotifications, this.openMentions), function(notification) {
                notification.close && notification.close();
            });
        },
        onNewMessage: function(message) {
            if (this.focus || message.historical || message.owner.id === this.client.user.id) {
                return;
            }
            this.createDesktopNotification(message);
        },
        createDesktopNotification: function(message) {

            var that = this;

            if (!notify.isSupported ||
                notify.permissionLevel() != notify.PERMISSION_GRANTED) {
                return;
            }

            var roomID = message.room.id,
                avatar = message.owner.avatar,
                icon = 'https://www.gravatar.com/avatar/' + avatar + '?s=50',
                title = message.owner.displayName + ' in ' + message.room.name,
                mention = message.mentioned;

            var notification = notify.createNotification(title, {
                body: message.text,
                icon: icon,
                tag: message.id,
                onclick: function() {
                    window.focus();
                    that.client.events.trigger('rooms:switch', roomID);
                }
            });

            //
            // Mentions
            //
            if (mention) {
                if (this.openMentions.length > 2) {
                    this.openMentions[0].close();
                    this.openMentions.shift();
                }
                this.openMentions.push(notification);
                // Quit early!
                return;
            }
            //
            // Everything else
            //
            if (this.openNotifications.length > 2) {
                this.openNotifications[0].close();
                this.openNotifications.shift();
            }
            this.openNotifications.push(notification);

            setTimeout(function() {
                notification.close && notification.close();
            }, 3000);

        }
    });

}(window, $, _, notify);
<commit_msg>Fix changing the browser title<commit_after>/*
 * WINDOW VIEW
 * TODO: Break it up :/
 */

'use strict';

+function(window, $, _, notify) {

    window.LCB = window.LCB || {};

    window.LCB.WindowView = Backbone.View.extend({
        el: 'html',
        focus: true,
        count: 0,
        mentions: 0,
        countFavicon: new Favico({
            position: 'down',
            animation: 'none',
            bgColor: '#b94a48'
        }),
        mentionsFavicon: new Favico({
            position: 'left',
            animation: 'none',
            bgColor: '#f22472'
        }),
        initialize: function(options) {

            var that = this;

            this.client = options.client;
            this.rooms = options.rooms;
            this.originalTitle = document.title;
            this.title = this.originalTitle;

            $(window).on('focus blur', _.bind(this.onFocusBlur, this));

            this.rooms.current.on('change:id', function(current, id) {
                var room = this.rooms.get(id),
                    title = room ? room.get('name') : 'Rooms';
                this.updateTitle(title);
            }, this);

            this.rooms.on('change:name', function(room) {
                if (room.id !== this.rooms.current.get('id')) {
                    return;
                }
                this.updateTitle(room.get('name'));
            }, this);

            this.rooms.on('messages:new', this.onNewMessage, this);

            // Last man standing
            _.defer(function() {
                that.updateTitle();
            });

        },
        onFocusBlur: function(e) {
            this.focus = (e.type === 'focus');
            if (this.focus) {
                clearInterval(this.titleTimer);
                clearInterval(this.faviconBadgeTimer);
                this.count = 0;
                this.mentions = 0;
                this.titleTimer = false;
                this.titleTimerFlip = false;
                this.faviconBadgeTimer = false;
                this.faviconBadgeTimerFlip = false;
                this.updateTitle();
                this.mentionsFavicon.reset();
            }
        },
        onNewMessage: function(message) {
            if (this.focus || message.historical || message.owner.id === this.client.user.id) {
                return;
            }
            this.countMessage(message);
            this.flashTitle()
            this.flashFaviconBadge();
        },
        countMessage: function(message) {
            ++this.count;
            message.mentioned && ++this.mentions;
        },
        flashTitle: function() {
            var titlePrefix = '';
            if (this.count > 0) {
                titlePrefix += '(' + parseInt(this.count);
                if (this.mentions > 0) {
                    titlePrefix += '/' + parseInt(this.mentions) + '@';
                }
                titlePrefix += ') ';
            }
            document.title = titlePrefix + this.title;
        },
        flashFaviconBadge: function() {
            if (!this.faviconBadgeTimer) {
                this._flashFaviconBadge();
                var flashFaviconBadge = _.bind(this._flashFaviconBadge, this);
                this.faviconBadgeTimer = setInterval(flashFaviconBadge, 1 * 2000);
            }
        },
        _flashFaviconBadge: function() {
            if (this.mentions > 0 && this.faviconBadgeTimerFlip) {
                this.mentionsFavicon.badge(this.mentions);
            } else {
                this.countFavicon.badge(this.count);
            }
            this.faviconBadgeTimerFlip = !this.faviconBadgeTimerFlip;
        },
        updateTitle: function(name) {
            if (!name) {
                var room = this.rooms.get(this.rooms.current.get('id'));
                name = (room && room.get('name')) || 'Rooms';
            }
            if (name) {
                this.title = $('<pre />').text(name).html() +
                ' \u00B7 ' + this.originalTitle;
            } else {
                this.title = this.originalTitle;
            }
            document.title = this.title;
        }
    });

    window.LCB.HotKeysView = Backbone.View.extend({
        el: 'html',
        keys: {
            'up+shift+alt down+shift+alt': 'nextRoom',
            's+shift+alt': 'toggleRoomSidebar',
            'g+shift+alt': 'openGiphyModal',
            'space+shift+alt': 'recallRoom'
        },
        initialize: function(options) {
            this.client = options.client;
            this.rooms = options.rooms;
        },
        nextRoom: function(e) {
            var method = e.keyCode === 40 ? 'next' : 'prev',
                selector = e.keyCode === 40 ? 'first' : 'last',
                $next = this.$('.lcb-tabs').find('[data-id].selected')[method]();
            if ($next.length === 0) {
                $next = this.$('.lcb-tabs').find('[data-id]:' + selector);
            }
            this.client.events.trigger('rooms:switch', $next.data('id'));
        },
        recallRoom: function() {
            this.client.events.trigger('rooms:switch', this.rooms.last.get('id'));
        },
        toggleRoomSidebar: function(e) {
            e.preventDefault();
            var view = this.client.view.panes.views[this.rooms.current.get('id')];
            view && view.toggleSidebar && view.toggleSidebar();
        },
        openGiphyModal: function(e) {
            if (this.client.options.giphyEnabled) {
                e.preventDefault();
                $('.lcb-giphy').modal('show');
            }
        }
    });

    window.LCB.DesktopNotificationsView = Backbone.View.extend({
        focus: true,
        openNotifications: [],
        openMentions: [],
        initialize: function(options) {
            notify.config({
                pageVisibility: false
            });
            this.client = options.client;
            this.rooms = options.rooms;
            $(window).on('focus blur unload', _.bind(this.onFocusBlur, this));
            this.rooms.on('messages:new', this.onNewMessage, this);
        },
        onFocusBlur: function(e) {
            this.focus = (e.type === 'focus');
            _.each(_.merge(this.openNotifications, this.openMentions), function(notification) {
                notification.close && notification.close();
            });
        },
        onNewMessage: function(message) {
            if (this.focus || message.historical || message.owner.id === this.client.user.id) {
                return;
            }
            this.createDesktopNotification(message);
        },
        createDesktopNotification: function(message) {

            var that = this;

            if (!notify.isSupported ||
                notify.permissionLevel() != notify.PERMISSION_GRANTED) {
                return;
            }

            var roomID = message.room.id,
                avatar = message.owner.avatar,
                icon = 'https://www.gravatar.com/avatar/' + avatar + '?s=50',
                title = message.owner.displayName + ' in ' + message.room.name,
                mention = message.mentioned;

            var notification = notify.createNotification(title, {
                body: message.text,
                icon: icon,
                tag: message.id,
                onclick: function() {
                    window.focus();
                    that.client.events.trigger('rooms:switch', roomID);
                }
            });

            //
            // Mentions
            //
            if (mention) {
                if (this.openMentions.length > 2) {
                    this.openMentions[0].close();
                    this.openMentions.shift();
                }
                this.openMentions.push(notification);
                // Quit early!
                return;
            }
            //
            // Everything else
            //
            if (this.openNotifications.length > 2) {
                this.openNotifications[0].close();
                this.openNotifications.shift();
            }
            this.openNotifications.push(notification);

            setTimeout(function() {
                notification.close && notification.close();
            }, 3000);

        }
    });

}(window, $, _, notify);
<|endoftext|>