<commit_before>var N = require('../nuve/nuveClient/dist/nuve'),
	config = require('../licode_config');

 var TIMEOUT=10000,
     ROOM_NAME="myTestRoom";

var id;

describe("nuve", function () {

    beforeEach(function () {
        N.API.init(config.nuve.superserviceID, config.nuve.superserviceKey, 'http://localhost:3000/');
    });

    it("should not list rooms with wrong credentials", function () {
        var rooms;
        var received = false, ok = false;

        N.API.init("Jose Antonio", config.nuve.superserviceKey, 'http://localhost:3000/');

        N.API.getRooms(function(rooms_) {
            received = true;
            ok = true;
        }, function(error) {
        	received = true;
            ok = false;
        });

        waitsFor(function () {
        	return received;
        }, "Nuve should have answered", TIMEOUT);

        runs(function () {
            expect(ok).toBe(false);
        });
    });

    it("should list rooms", function () {
        var rooms;
        var received = false, ok = false;

        N.API.getRooms(function(rooms_) {
            received = true;
            ok = true;
        }, function(error) {
        	received = true;
            ok = false;
        });

        waitsFor(function () {
        	return received;
        }, "Nuve should have answered", TIMEOUT);

        runs(function () {
            expect(ok).toBe(true);
        });
    });

    it("should create normal rooms", function () {
        N.API.createRoom(ROOM_NAME, function(room) {
            id = room._id;
        });

        waitsFor(function () {
            return id !== undefined;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(id).toNotBe(undefined);
        });
    });

    it("should get normal rooms", function () {
    	var obtained = false;
    	var received = false;
        N.API.getRoom(id, function(result) {
            obtained = true;
            received = true;
        }, function(error) {
        	obtained = false;
        	received = true;
        });

        waitsFor(function () {
            return received;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(obtained).toBe(true);
        });
    });

    it("should create tokens for users in normal rooms", function () {
    	var obtained = false;
    	var received = false;
        N.API.createToken(id, "user", "presenter", function(token) {
            obtained = true;
            received = true;
        }, function(error) {
        	obtained = false;
        	received = true;
        });

        waitsFor(function () {
            return received;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(obtained).toBe(true);
        });
    });

    it("should create tokens for users with special characters in normal rooms", function () {
    	var obtained = false;
    	var received = false;
        N.API.createToken(id, "user_with_$?üóñ", "role", function(token) {
            obtained = true;
            received = true;
        }, function(error) {
        	obtained = false;
        	received = true;
        });

        waitsFor(function () {
            return received;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(obtained).toBe(true);
        });
    });

    it("should get users in normal rooms", function () {
    	var obtained = false;
    	var received = false;
        N.API.getUsers(id, function(token) {
            obtained = true;
            received = true;
        }, function(error) {
        	obtained = false;
        	received = true;
        });

        waitsFor(function () {
            return received;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(obtained).toBe(true);
        });
    });

    it("should delete normal rooms", function () {
        N.API.deleteRoom(id, function(result) {
            id = undefined;
        }, function(error) {

        });

        waitsFor(function () {
            return id === undefined;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(id).toBe(undefined);
        });
    });

    it("should create p2p rooms", function () {
        N.API.createRoom(ROOM_NAME, function(room) {
            id = room._id;
        }, function() {}, {p2p: true});

        waitsFor(function () {
            return id !== undefined;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(id).toNotBe(undefined);
        });
    });

    it("should get p2p rooms", function () {
    	var obtained = false;
    	var received = false;
        N.API.getRoom(id, function(result) {
            obtained = true;
            received = true;
        }, function(error) {
        	obtained = false;
        	received = true;
        });

        waitsFor(function () {
            return received;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(obtained).toBe(true);
        });
    });

    it("should delete p2p rooms", function () {
        N.API.deleteRoom(id, function(result) {
            id = undefined;
        }, function(error) {

        });

        waitsFor(function () {
            return id === undefined;
        }, "Nuve should have created the room", TIMEOUT);

        runs(function () {
            expect(id).toBe(undefined);
        });
    });
});
<commit_msg>Migrated nuve tests to mocha<commit_after>var assert = require('chai').assert;
var N = require('../nuve/nuveClient/dist/nuve'),
	config = require('../licode_config');

 var TIMEOUT=10000,
     ROOM_NAME="myTestRoom";

var id;

describe("nuve", function () {
    this.timeout (TIMEOUT);

    beforeEach(function () {
        N.API.init(config.nuve.superserviceID, config.nuve.superserviceKey, 'http://localhost:3000/');
    });

    it("should not list rooms with wrong credentials", function () {
        var rooms;
        var received = false, ok = false;

        N.API.init("Jose Antonio", config.nuve.superserviceKey, 'http://localhost:3000/');

        N.API.getRooms(function(rooms_) {
            ok = true;
            throw "Succeeded and it should fail with bad credentials";
        }, function(error) {
            ok = false;
            done();
        });

    });

    it("should list rooms", function () {
        var rooms;
        var received = false, ok = false;

        N.API.getRooms(function(rooms_) {
            assert.isArray(rooms_, "This should be an array of rooms");
            done();
        }, function(error) {
            throw error;
        });
    });

    it("should create normal rooms", function () {
        N.API.createRoom(ROOM_NAME, function(room) {
            done();
        });
    });

    it("should get normal rooms", function () {
        N.API.getRoom(id, function(result) {
            done();
        }, function(error) {
            throw error;
        });
    });

    it("should create tokens for users in normal rooms", function () {
        N.API.createToken(id, "user", "presenter", function(token) {
            done();
        }, function(error) {
            throw error;
        });
    });

    it("should create tokens for users with special characters in normal rooms", function () {
        N.API.createToken(id, "user_with_$?üóñ", "role", function(token) {
            done()
        }, function(error) {
            throw error;
        });
    });

    it("should get users in normal rooms", function () {
        N.API.getUsers(id, function(token) {
            done()
        }, function(error) {
            throw error;
        });
    });

    it("should delete normal rooms", function () {
        N.API.deleteRoom(id, function(result) {
            done;
        }, function(error) {
            throw error;
        });

    });

    it("should create p2p rooms", function () {
        N.API.createRoom(ROOM_NAME, function(room) {
            id = room._id;
            assert.notEqual(id, undefined, 'Did not provide a valid id');
            done()
        }, function() {}, {p2p: true});
    });

    it("should get p2p rooms", function () {
        N.API.getRoom(id, function(result) {
            done()
        }, function(error) {
            throw error;
        });
    });

    it("should delete p2p rooms", function () {
        N.API.deleteRoom(id, function(result) {
            done()
        }, function(error) {
            throw error;
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
	"Same paths": function (t, a) {
		a.equal(t('/a/b/c', '/a/b/c'), '');
	},
	"From deeper than to": function (t, a) {
		a.equal(t('/a/b/c/d/', '/a/b/'), '../../');
	},
	"From deeper than to (file)": function (t, a) {
		a.equal(t('/a/b/c/d/', '/a/b/x'), '../../x');
	},
	"To deeper than from": function (t, a) {
		a.equal(t('/a/b/', '/a/b/c/d/'), 'c/d/');
	},
	"To deeper than from (file)": function (t, a) {
		a.equal(t('/a/b/', '/a/b/c/d/x'), 'c/d/x');
	},
	"Different paths": function (t, a) {
		a.equal(t('/a/b/c/', '/e/f/g/'), '../../../e/f/g/');
	},
	"CWD": function (t, a) {
		a.equal(t(__dirname + '/a/b/'), t(process.cwd(), __dirname + '/a/b/'));
	},
	"Error on relative from": function (t, a) {
		a.throws(function () {
			t('raz/dwa', '/fad/ra/');
		});
	},
	"Error on relative to": function (t, a) {
		a.throws(function () {
			t('/raz/dwa', 'fad/ra/');
		});
	}
};
<commit_msg>path.relative now also works for relative input<commit_after>'use strict';

module.exports = {
	"Same paths": function (t, a) {
		a.equal(t('/a/b/c', '/a/b/c'), '');
	},
	"From deeper than to": function (t, a) {
		a.equal(t('/a/b/c/d/', '/a/b/'), '../../');
	},
	"From deeper than to (file)": function (t, a) {
		a.equal(t('/a/b/c/d/', '/a/b/x'), '../../x');
	},
	"To deeper than from": function (t, a) {
		a.equal(t('/a/b/', '/a/b/c/d/'), 'c/d/');
	},
	"To deeper than from (file)": function (t, a) {
		a.equal(t('/a/b/', '/a/b/c/d/x'), 'c/d/x');
	},
	"Different paths": function (t, a) {
		a.equal(t('/a/b/c/', '/e/f/g/'), '../../../e/f/g/');
	},
	"CWD": function (t, a) {
		a.equal(t(__dirname + '/a/b/'), t(process.cwd(), __dirname + '/a/b/'));
	}
};
<|endoftext|>


<commit_before>$(document).ready(function() {
  $('.menu-title .toggle').click(function(event) {
    event.stopPropagation();
    event.preventDefault();

    $('.menu-wrapper').toggleClass('visible');
    $('.action-dropdown').removeClass('revealed');
  });

  $(document).click(function() {
    $('.menu-wrapper').removeClass('visible');
  });
});<commit_msg>Remove active class form search dropdown<commit_after>$(document).ready(function() {
  $('.menu-title .toggle').click(function(event) {
    event.stopPropagation();
    event.preventDefault();

    $('.menu-wrapper').toggleClass('visible');
    // Hide other open dropdowns.
    $('.action-dropdown').removeClass('revealed');
    $('a[data-dropdown="action-bttn"]').removeClass('current');
  });

  $(document).click(function() {
    $('.menu-wrapper').removeClass('visible');
  });
});<|endoftext|>


<commit_before>/**
 * @description Select multiple albums or photos.
 * @copyright   2015 by Tobias Reich
 */

multiselect = {

	ids: []

}

multiselect.position = {

	top    : null,
	right  : null,
	bottom : null,
	left   : null

}

multiselect.bind = function() {

	$('.content').on('mousedown', (e) => { if (e.which===1) multiselect.show(e) })

	return true

}

multiselect.isSelected = function(id) {

	let pos = $.inArray(id, multiselect.ids)

	return {
		selected : (pos===-1 ? false : true),
		position : pos
	}

}

multiselect.toggleItem = function(object, id) {

	let selected = multiselect.isSelected(id).selected

	if (selected===false) multiselect.addItem(object, id)
	else                  multiselect.removeItem(object, id)

}

multiselect.addItem = function(object, id) {

	if (album.isSmartID(id)) return
	if (multiselect.isSelected(id).selected===true) return

	multiselect.ids.push(id)
	multiselect.select(object)

}

multiselect.removeItem = function(object, id) {

	let { selected, pos } = multiselect.isSelected(id)

	if (selected===false) return

	multiselect.ids.splice(pos, 1)
	multiselect.deselect(object)

}

multiselect.albumClick = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if (e.metaKey===true) multiselect.toggleItem(albumObj, id)
	else                  lychee.goto(id)

}

multiselect.photoClick = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if (e.metaKey===true) multiselect.toggleItem(photoObj, id)
	else                  lychee.goto(album.getID() + '/' + id)

}

multiselect.albumContextMenu = function(e, albumObj) {

	let id       = albumObj.attr('data-id')
	let selected = multiselect.isSelected(id).selected

	if (selected!==false) {
		contextMenu.albumMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.album(album.getID(), e)
	}

}

multiselect.photoContextMenu = function(e, photoObj) {

	let id       = photoObj.attr('data-id')
	let selected = multiselect.isSelected(id).selected

	if (selected!==false) {
		contextMenu.photoMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.photo(photo.getID(), e)
	}

}

multiselect.clearSelection = function() {

	multiselect.deselect('.photo.active, .album.active')
	multiselect.ids = []

}

multiselect.show = function(e) {

	if (lychee.publicMode)                          return false
	if (!visible.albums() && !visible.album())      return false
	if ($('.album:hover, .photo:hover').length!==0) return false
	if (visible.search())                           return false
	if (visible.multiselect())                      $('#multiselect').remove()

	sidebar.setSelectable(false)

	multiselect.position.top    = e.pageY
	multiselect.position.right  = -1 * (e.pageX - $(document).width())
	multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height())
	multiselect.position.left   = e.pageX

	$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))

	$(document)
		.on('mousemove', multiselect.resize)
		.on('mouseup', (e) => { if (e.which===1) multiselect.getSelection(e) })

}

multiselect.resize = function(e) {

	if (multiselect.position.top    === null ||
	    multiselect.position.right  === null ||
	    multiselect.position.bottom === null ||
	    multiselect.position.left   === null) return false

	let newSize      = {}
	let documentSize = {}

	// Get the position of the mouse
	let mousePos = {
		x : e.pageX,
		y : e.pageY
	}

	// Default CSS
	let newCSS = {
		top    : null,
		bottom : null,
		height : null,
		left   : null,
		right  : null,
		width  : null
	}

	if (mousePos.y>=multiselect.position.top) {

		documentSize.height = $(document).height()

		// Do not leave the screen
		newSize.height = mousePos.y - multiselect.position.top
		if ((multiselect.position.top + newSize.height)>=documentSize.height) {
			newSize.height -= (multiselect.position.top + newSize.height) - documentSize.height + 2
		}

		newCSS.top    = multiselect.position.top
		newCSS.bottom = 'inherit'
		newCSS.height = newSize.height

	} else {

		newCSS.top    = 'inherit'
		newCSS.bottom = multiselect.position.bottom
		newCSS.height = multiselect.position.top - e.pageY

	}

	if (mousePos.x>=multiselect.position.left) {

		documentSize.width = $(document).width()

		// Do not leave the screen
		newSize.width = mousePos.x - multiselect.position.left
		if ((multiselect.position.left + newSize.width)>=documentSize.width) {
			newSize.width -= (multiselect.position.left + newSize.width) - documentSize.width + 2
		}

		newCSS.right = 'inherit'
		newCSS.left  = multiselect.position.left
		newCSS.width = newSize.width

	} else {

		newCSS.right = multiselect.position.right
		newCSS.left  = 'inherit'
		newCSS.width = multiselect.position.left - e.pageX

	}

	// Updated all CSS properties at once
	$('#multiselect').css(newCSS)

}

multiselect.stopResize = function() {

	if (multiselect.position.top!==null) $(document).off('mousemove mouseup')

}

multiselect.getSize = function() {

	if (!visible.multiselect()) return false

	let $elem  = $('#multiselect')
	let offset = $elem.offset()

	return {
		top    : offset.top,
		left   : offset.left,
		width  : parseInt($elem.css('width').replace('px', '')),
		height : parseInt($elem.css('height').replace('px', ''))
	}

}

multiselect.getSelection = function(e) {

	let tolerance = 150
	let ids       = []
	let size      = multiselect.getSize()

	if (visible.contextMenu())  return false
	if (!visible.multiselect()) return false

	if (e.metaKey===false && (size.width==0 || size.height==0)) {
		multiselect.close()
		return false
	}

	$('.photo, .album').each(function() {

		let offset = $(this).offset()

		if (offset.top>=(size.top - tolerance) &&
			offset.left>=(size.left - tolerance) &&
			(offset.top + 206)<=(size.top + size.height + tolerance) &&
			(offset.left + 206)<=(size.left + size.width + tolerance)) {

			let id = $(this).attr('data-id')

			multiselect.addItem($(this), id)

		}

	})

	multiselect.hide()

}

multiselect.select = function(id) {

	let el = $(id)

	el.addClass('selected')
	el.addClass('active')

}

multiselect.deselect = function(id) {

	let el = $(id)

	el.removeClass('selected')
	el.removeClass('active')

}

multiselect.hide = function() {

	sidebar.setSelectable(true)

	multiselect.stopResize()

	multiselect.position.top    = null
	multiselect.position.right  = null
	multiselect.position.bottom = null
	multiselect.position.left   = null

	lychee.animate('#multiselect', 'fadeOut')
	setTimeout(() => $('#multiselect').remove(), 300)

}

multiselect.close = function() {

	multiselect.clearSelection()

	multiselect.hide()

}<commit_msg>Support metaKey and ctrlKey for selection.<commit_after>/**
 * @description Select multiple albums or photos.
 * @copyright   2015 by Tobias Reich
 */

const isSelectKeyPressed = function(e) {

	return e.metaKey || e.ctrlKey

}

multiselect = {

	ids: []

}

multiselect.position = {

	top    : null,
	right  : null,
	bottom : null,
	left   : null

}

multiselect.bind = function() {

	$('.content').on('mousedown', (e) => { if (e.which===1) multiselect.show(e) })

	return true

}

multiselect.isSelected = function(id) {

	let pos = $.inArray(id, multiselect.ids)

	return {
		selected : (pos===-1 ? false : true),
		position : pos
	}

}

multiselect.toggleItem = function(object, id) {

	let selected = multiselect.isSelected(id).selected

	if (selected===false) multiselect.addItem(object, id)
	else                  multiselect.removeItem(object, id)

}

multiselect.addItem = function(object, id) {

	if (album.isSmartID(id)) return
	if (multiselect.isSelected(id).selected===true) return

	multiselect.ids.push(id)
	multiselect.select(object)

}

multiselect.removeItem = function(object, id) {

	let { selected, pos } = multiselect.isSelected(id)

	if (selected===false) return

	multiselect.ids.splice(pos, 1)
	multiselect.deselect(object)

}

multiselect.albumClick = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if (isSelectKeyPressed(e)) multiselect.toggleItem(albumObj, id)
	else                       lychee.goto(id)

}

multiselect.photoClick = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if (isSelectKeyPressed(e)) multiselect.toggleItem(photoObj, id)
	else                       lychee.goto(album.getID() + '/' + id)

}

multiselect.albumContextMenu = function(e, albumObj) {

	let id       = albumObj.attr('data-id')
	let selected = multiselect.isSelected(id).selected

	if (selected!==false) {
		contextMenu.albumMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.album(album.getID(), e)
	}

}

multiselect.photoContextMenu = function(e, photoObj) {

	let id       = photoObj.attr('data-id')
	let selected = multiselect.isSelected(id).selected

	if (selected!==false) {
		contextMenu.photoMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.photo(photo.getID(), e)
	}

}

multiselect.clearSelection = function() {

	multiselect.deselect('.photo.active, .album.active')
	multiselect.ids = []

}

multiselect.show = function(e) {

	if (lychee.publicMode)                          return false
	if (!visible.albums() && !visible.album())      return false
	if ($('.album:hover, .photo:hover').length!==0) return false
	if (visible.search())                           return false
	if (visible.multiselect())                      $('#multiselect').remove()

	sidebar.setSelectable(false)

	multiselect.position.top    = e.pageY
	multiselect.position.right  = -1 * (e.pageX - $(document).width())
	multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height())
	multiselect.position.left   = e.pageX

	$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))

	$(document)
		.on('mousemove', multiselect.resize)
		.on('mouseup', (e) => { if (e.which===1) multiselect.getSelection(e) })

}

multiselect.resize = function(e) {

	if (multiselect.position.top    === null ||
	    multiselect.position.right  === null ||
	    multiselect.position.bottom === null ||
	    multiselect.position.left   === null) return false

	let newSize      = {}
	let documentSize = {}

	// Get the position of the mouse
	let mousePos = {
		x : e.pageX,
		y : e.pageY
	}

	// Default CSS
	let newCSS = {
		top    : null,
		bottom : null,
		height : null,
		left   : null,
		right  : null,
		width  : null
	}

	if (mousePos.y>=multiselect.position.top) {

		documentSize.height = $(document).height()

		// Do not leave the screen
		newSize.height = mousePos.y - multiselect.position.top
		if ((multiselect.position.top + newSize.height)>=documentSize.height) {
			newSize.height -= (multiselect.position.top + newSize.height) - documentSize.height + 2
		}

		newCSS.top    = multiselect.position.top
		newCSS.bottom = 'inherit'
		newCSS.height = newSize.height

	} else {

		newCSS.top    = 'inherit'
		newCSS.bottom = multiselect.position.bottom
		newCSS.height = multiselect.position.top - e.pageY

	}

	if (mousePos.x>=multiselect.position.left) {

		documentSize.width = $(document).width()

		// Do not leave the screen
		newSize.width = mousePos.x - multiselect.position.left
		if ((multiselect.position.left + newSize.width)>=documentSize.width) {
			newSize.width -= (multiselect.position.left + newSize.width) - documentSize.width + 2
		}

		newCSS.right = 'inherit'
		newCSS.left  = multiselect.position.left
		newCSS.width = newSize.width

	} else {

		newCSS.right = multiselect.position.right
		newCSS.left  = 'inherit'
		newCSS.width = multiselect.position.left - e.pageX

	}

	// Updated all CSS properties at once
	$('#multiselect').css(newCSS)

}

multiselect.stopResize = function() {

	if (multiselect.position.top!==null) $(document).off('mousemove mouseup')

}

multiselect.getSize = function() {

	if (!visible.multiselect()) return false

	let $elem  = $('#multiselect')
	let offset = $elem.offset()

	return {
		top    : offset.top,
		left   : offset.left,
		width  : parseInt($elem.css('width').replace('px', '')),
		height : parseInt($elem.css('height').replace('px', ''))
	}

}

multiselect.getSelection = function(e) {

	let tolerance = 150
	let ids       = []
	let size      = multiselect.getSize()

	if (visible.contextMenu())  return false
	if (!visible.multiselect()) return false

	if (!isSelectKeyPressed(e) && (size.width==0 || size.height==0)) {
		multiselect.close()
		return false
	}

	$('.photo, .album').each(function() {

		let offset = $(this).offset()

		if (offset.top>=(size.top - tolerance) &&
			offset.left>=(size.left - tolerance) &&
			(offset.top + 206)<=(size.top + size.height + tolerance) &&
			(offset.left + 206)<=(size.left + size.width + tolerance)) {

			let id = $(this).attr('data-id')

			multiselect.addItem($(this), id)

		}

	})

	multiselect.hide()

}

multiselect.select = function(id) {

	let el = $(id)

	el.addClass('selected')
	el.addClass('active')

}

multiselect.deselect = function(id) {

	let el = $(id)

	el.removeClass('selected')
	el.removeClass('active')

}

multiselect.hide = function() {

	sidebar.setSelectable(true)

	multiselect.stopResize()

	multiselect.position.top    = null
	multiselect.position.right  = null
	multiselect.position.bottom = null
	multiselect.position.left   = null

	lychee.animate('#multiselect', 'fadeOut')
	setTimeout(() => $('#multiselect').remove(), 300)

}

multiselect.close = function() {

	multiselect.clearSelection()

	multiselect.hide()

}<|endoftext|>


<commit_before>/* eslint-disable max-statements-per-line */

'use strict';

const fs = require('fs');
const path = require('path');
const assert = require('assert');
const theFile = path.join(__dirname, 'test-z-asset.css');
const theDirectory = __dirname;

function dumpError (error) {
  let s = error.message;
  if (s === 'Bad argument') {
    s = 'fd must be a file descriptor';
  } else
  if ((s === 'EBADF, bad file descriptor') && (error.syscall === 'fstat')) {
    s = 'EBADF: bad file descriptor, fstat';
  } else
  if (s === 'EBADF, bad file descriptor') {
    s = 'EBADF: bad file descriptor';
  } else
  if (s === 'EBADF: bad file descriptor, close') {
    s = 'EBADF: bad file descriptor';
  } else
  if (s === 'EISDIR, illegal operation on a directory') {
    s = 'EISDIR: illegal operation on a directory, read';
  }
  console.log(s);
}

test01();

function test01 () {
  console.log('<<< test01 >>>');

  fs.stat(theFile, function (error, stats) {
    assert.equal(error, null);
    console.log('stats.size', stats.size);
    fs.open(theFile, 'r', function (error2, fd) {
      assert.equal(error2, null);
      console.log('typeof fd', typeof fd);
      fs.fstat(fd, function (error3, fstats) {
        assert.equal(error3, null);
        console.log('fstats.size', fstats.size);
        const buffer = new Buffer(stats.size / 2 | 0);
        fs.read(fd, buffer, 0, buffer.length, null, function (error4, bytesRead, buffer2) {
          assert.equal(error4, null);
          assert(buffer === buffer2); // should be same instances
          const data2 = buffer2.toString('utf8', 0, buffer2.length);
          console.log('data2', data2);
          fs.close(fd, function (error5, wtf) {
            assert.equal(error5, null);
            console.log('typeof wtf', typeof wtf);
            fs.readFile(theFile, function (error6, buffer3) {
              assert.equal(error6, null);
              const data3 = buffer3.toString('utf8', 0, buffer3.length);
              console.log('data3', data3);
              const buffer4 = buffer3;
              fs.writeFile(theFile, buffer4, function (error7, wtf2) {
                if (process.pkg) {
                  assert.equal(typeof error7, 'object'); // TODO maybe code=EACCESS?
                } else {
                  assert.equal(error7, null);
                }
                assert.equal(wtf2, undefined);
                fs.readdir(theDirectory, function (error8, list) {
                  assert.equal(error8, null);
                  assert(Array.isArray(list));
                  console.log('typeof list', typeof list);
                  test01e(fd);
                });
                console.log('after fs.readdir');
              });
              console.log('after fs.writeFile');
            });
            console.log('after fs.readFile');
          });
          console.log('after fs.close');
        });
        console.log('after fs.read');
      });
      console.log('after fs.fstat');
    });
    console.log('after fs.open');
  });
  console.log('after fs.stat');
}

function test01e (badFd) {
  console.log('<<< test01e >>>');

  fs.stat(theFile + '.notExists', function (error, stats) {
    console.log('fs.stat.error.code', error.code);
    fs.open(theFile + '.notExists', 'r', function (error2, fd) {
      console.log('fs.open.error2.code', error2.code);
      fd = badFd;
      fs.fstat(fd, function (error3, fstats) {
        console.log('fs.fstat.error3.code', error3.code);
        const buffer = new Buffer(1024);
        fs.read(fd, buffer, 0, buffer.length, null, function (error4, bytesRead, buffer2) {
          console.log('fs.read.error4.code', error4.code);
          console.log('typeof bytesRead', typeof bytesRead);
          console.log('typeof buffer2', typeof buffer2);
          fs.close(fd, function (error5) {
            console.log('fs.close.error5.code', error5.code);
            fs.readFile(theFile + '.notExists', function (error6, buffer3) {
              console.log('fs.readFile.error6.code', error6.code);
              console.log('typeof buffer3', typeof buffer3);
              const buffer4 = new Buffer(1024);
              fs.writeFile(theFile + '/canNotWrite', buffer4, function (error7, wtf2) {
                if (process.pkg) {
                  assert.equal(error7.code, 'ENOENT');
                } else {
                  assert.equal(error7.code, 'ENOTDIR');
                }
                assert.equal(wtf2, undefined);
                fs.readdir(theDirectory + '.notExists', function (error8, list) {
                  console.log('fs.readdir.error8.code', error8.code);
                  console.log('typeof list', typeof list);
                  test02();
                });
                console.log('after fs.readdir');
              });
              console.log('after fs.writeFile');
            });
            console.log('after fs.readFile');
          });
          console.log('after fs.close');
        });
        console.log('after fs.read');
      });
      console.log('after fs.fstat');
    });
    console.log('after fs.open');
  });
  console.log('after fs.stat');
}

function test02 () {
  console.log('<<< test02 >>>');

  const stats = fs.statSync(theFile);
  console.log('stats.size', stats.size);
  const fd = fs.openSync(theFile, 'r');
  const fstats = fs.fstatSync(fd);
  console.log('fstats.size', fstats.size);
  const buffer = new Buffer(stats.size);
  const bytesRead = fs.readSync(fd, buffer, 0, buffer.length);
  console.log('bytesRead', bytesRead);
  const data = buffer.toString('utf8', 0, buffer.length);
  console.log('data', data);
  fs.closeSync(fd);
  test03();
}

function test03 () {
  console.log('<<< test03 >>>');

  const stats = fs.statSync(theFile);
  console.log('stats.size', stats.size);
  const fd = fs.openSync(theFile, 'r');
  const fstats = fs.fstatSync(fd);
  console.log('fstats.size', fstats.size);
  const buffer = new Buffer(6);
  let bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_a', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_b', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 1, 5);
  console.log('bytesRead_c', bytesRead, 'buffer', buffer[1], buffer[2]);
  try { bytesRead = fs.readSync(fd, buffer, 1, 6); } catch (error) { dumpError(error); }
  console.log('bytesRead_d', bytesRead, 'buffer', buffer[1], buffer[2]);
  bytesRead = fs.readSync(fd, buffer, 5, 1);
  console.log('bytesRead_e', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, 6, 0); } catch (error) { dumpError(error); }
  console.log('bytesRead_f', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 5); } catch (error) { dumpError(error); }
  console.log('bytesRead_g', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 9); } catch (error) { dumpError(error); }
  console.log('bytesRead_h', bytesRead, 'buffer', buffer[4], buffer[5]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_i', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_j', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_k', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_l', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_m', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6, 20);
  console.log('bytesRead_n', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6, 80);
  console.log('bytesRead_o', bytesRead, 'buffer', buffer[0], buffer[1]);
  fs.closeSync(fd);
  test04();
}

function test04 () {
  const stats = fs.statSync(theDirectory);
  console.log(stats.size);
  const fd = fs.openSync(theDirectory, 'r');
  const fstats = fs.fstatSync(fd);
  console.log(fstats.size);
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, 6, 0); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 3); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, 0, 4); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  fs.closeSync(fd);
  test05();
}

function test05 () {
  const fd = 'incorrect fd as string';
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { console.log(fs.fstatSync(fd)); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { fs.closeSync(fd); } catch (error) { dumpError(error); }
  test06();
}

function test06 () {
  const fd = 7890;
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { console.log(fs.fstatSync(fd)); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { fs.closeSync(fd); console.log('EBADF: bad file descriptor'); } catch (error) { dumpError(error); }
  test07();
}

function test07 () {
  console.log('before createReadStream');
  const rs = fs.createReadStream(theFile);
  console.log('after createReadStream');

  rs.on('open', function () {
    console.log('open');
  });

  rs.on('readable', function () {
    console.log('before read');
    let r = rs.read();
    console.log('after read');
    if (!r) {
      r = 'null';
    } else
    if (r.length >= 2) {
      console.log('length:', r.length);
      r = r[0].toString() + ', ' + r[1].toString();
    }
    console.log('readable:', r);
  });

  rs.on('end', function () {
    console.log('end');
  });
}
<commit_msg>testing fs.readdir(theFile) on fs-layer-2<commit_after>/* eslint-disable max-statements-per-line */

'use strict';

const fs = require('fs');
const path = require('path');
const assert = require('assert');
const theFile = path.join(__dirname, 'test-z-asset.css');
const theDirectory = __dirname;

function dumpError (error) {
  let s = error.message;
  if (s === 'Bad argument') {
    s = 'fd must be a file descriptor';
  } else
  if ((s === 'EBADF, bad file descriptor') && (error.syscall === 'fstat')) {
    s = 'EBADF: bad file descriptor, fstat';
  } else
  if (s === 'EBADF, bad file descriptor') {
    s = 'EBADF: bad file descriptor';
  } else
  if (s === 'EBADF: bad file descriptor, close') {
    s = 'EBADF: bad file descriptor';
  } else
  if (s === 'EISDIR, illegal operation on a directory') {
    s = 'EISDIR: illegal operation on a directory, read';
  }
  console.log(s);
}

test01();

function test01 () {
  console.log('<<< test01 >>>');

  fs.stat(theFile, function (error, stats) {
    assert.equal(error, null);
    console.log('stats.size', stats.size);
    fs.open(theFile, 'r', function (error2, fd) {
      assert.equal(error2, null);
      console.log('typeof fd', typeof fd);
      fs.fstat(fd, function (error3, fstats) {
        assert.equal(error3, null);
        console.log('fstats.size', fstats.size);
        const buffer = new Buffer(stats.size / 2 | 0);
        fs.read(fd, buffer, 0, buffer.length, null, function (error4, bytesRead, buffer2) {
          assert.equal(error4, null);
          assert(buffer === buffer2); // should be same instances
          const data2 = buffer2.toString('utf8', 0, buffer2.length);
          console.log('data2', data2);
          fs.close(fd, function (error5, wtf) {
            assert.equal(error5, null);
            console.log('typeof wtf', typeof wtf);
            fs.readFile(theFile, function (error6, buffer3) {
              assert.equal(error6, null);
              const data3 = buffer3.toString('utf8', 0, buffer3.length);
              console.log('data3', data3);
              const buffer4 = buffer3;
              fs.writeFile(theFile, buffer4, function (error7, wtf2) {
                if (process.pkg) {
                  assert.equal(typeof error7, 'object'); // TODO maybe code=EACCESS?
                } else {
                  assert.equal(error7, null);
                }
                assert.equal(wtf2, undefined);
                fs.readdir(theDirectory, function (error8, list) {
                  assert.equal(error8, null);
                  assert(Array.isArray(list));
                  console.log('typeof list', typeof list);
                  test01e(fd);
                });
                console.log('after fs.readdir');
              });
              console.log('after fs.writeFile');
            });
            console.log('after fs.readFile');
          });
          console.log('after fs.close');
        });
        console.log('after fs.read');
      });
      console.log('after fs.fstat');
    });
    console.log('after fs.open');
  });
  console.log('after fs.stat');
}

function test01e (badFd) {
  console.log('<<< test01e >>>');

  fs.stat(theFile + '.notExists', function (error, stats) {
    console.log('fs.stat.error.code', error.code);
    fs.open(theFile + '.notExists', 'r', function (error2, fd) {
      console.log('fs.open.error2.code', error2.code);
      fd = badFd;
      fs.fstat(fd, function (error3, fstats) {
        console.log('fs.fstat.error3.code', error3.code);
        const buffer = new Buffer(1024);
        fs.read(fd, buffer, 0, buffer.length, null, function (error4, bytesRead, buffer2) {
          console.log('fs.read.error4.code', error4.code);
          console.log('typeof bytesRead', typeof bytesRead);
          console.log('typeof buffer2', typeof buffer2);
          fs.close(fd, function (error5) {
            console.log('fs.close.error5.code', error5.code);
            fs.readFile(theFile + '.notExists', function (error6, buffer3) {
              console.log('fs.readFile.error6.code', error6.code);
              console.log('typeof buffer3', typeof buffer3);
              const buffer4 = new Buffer(1024);
              fs.writeFile(theFile + '/canNotWrite', buffer4, function (error7, wtf2) {
                if (process.pkg) {
                  assert.equal(error7.code, 'ENOENT');
                } else {
                  assert.equal(error7.code, 'ENOTDIR');
                }
                assert.equal(wtf2, undefined);
                fs.readdir(theFile, function (error8, list) {
                  console.log('fs.readdir.error8.code', error8.code);
                  console.log('typeof list', typeof list);
                  fs.readdir(theDirectory + '.notExists', function (error9, list2) {
                    console.log('fs.readdir.error9.code', error9.code);
                    console.log('typeof list2', typeof list2);
                    test02();
                  });
                  console.log('after fs.readdir');
                });
                console.log('after fs.readdir');
              });
              console.log('after fs.writeFile');
            });
            console.log('after fs.readFile');
          });
          console.log('after fs.close');
        });
        console.log('after fs.read');
      });
      console.log('after fs.fstat');
    });
    console.log('after fs.open');
  });
  console.log('after fs.stat');
}

function test02 () {
  console.log('<<< test02 >>>');

  const stats = fs.statSync(theFile);
  console.log('stats.size', stats.size);
  const fd = fs.openSync(theFile, 'r');
  const fstats = fs.fstatSync(fd);
  console.log('fstats.size', fstats.size);
  const buffer = new Buffer(stats.size);
  const bytesRead = fs.readSync(fd, buffer, 0, buffer.length);
  console.log('bytesRead', bytesRead);
  const data = buffer.toString('utf8', 0, buffer.length);
  console.log('data', data);
  fs.closeSync(fd);
  test03();
}

function test03 () {
  console.log('<<< test03 >>>');

  const stats = fs.statSync(theFile);
  console.log('stats.size', stats.size);
  const fd = fs.openSync(theFile, 'r');
  const fstats = fs.fstatSync(fd);
  console.log('fstats.size', fstats.size);
  const buffer = new Buffer(6);
  let bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_a', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_b', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 1, 5);
  console.log('bytesRead_c', bytesRead, 'buffer', buffer[1], buffer[2]);
  try { bytesRead = fs.readSync(fd, buffer, 1, 6); } catch (error) { dumpError(error); }
  console.log('bytesRead_d', bytesRead, 'buffer', buffer[1], buffer[2]);
  bytesRead = fs.readSync(fd, buffer, 5, 1);
  console.log('bytesRead_e', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, 6, 0); } catch (error) { dumpError(error); }
  console.log('bytesRead_f', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 5); } catch (error) { dumpError(error); }
  console.log('bytesRead_g', bytesRead, 'buffer', buffer[4], buffer[5]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 9); } catch (error) { dumpError(error); }
  console.log('bytesRead_h', bytesRead, 'buffer', buffer[4], buffer[5]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_i', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_j', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_k', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_l', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6);
  console.log('bytesRead_m', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6, 20);
  console.log('bytesRead_n', bytesRead, 'buffer', buffer[0], buffer[1]);
  bytesRead = fs.readSync(fd, buffer, 0, 6, 80);
  console.log('bytesRead_o', bytesRead, 'buffer', buffer[0], buffer[1]);
  fs.closeSync(fd);
  test04();
}

function test04 () {
  const stats = fs.statSync(theDirectory);
  console.log(stats.size);
  const fd = fs.openSync(theDirectory, 'r');
  const fstats = fs.fstatSync(fd);
  console.log(fstats.size);
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, 6, 0); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, -1, 3); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { bytesRead = fs.readSync(fd, buffer, 0, 4); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  fs.closeSync(fd);
  test05();
}

function test05 () {
  const fd = 'incorrect fd as string';
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { console.log(fs.fstatSync(fd)); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { fs.closeSync(fd); } catch (error) { dumpError(error); }
  test06();
}

function test06 () {
  const fd = 7890;
  const buffer = new Buffer([ 12, 34, 56, 78 ]);
  let bytesRead;
  try { bytesRead = fs.readSync(fd, buffer, 0, 6); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { console.log(fs.fstatSync(fd)); } catch (error) { dumpError(error); }
  console.log(bytesRead, buffer[0], buffer[1]);
  try { fs.closeSync(fd); console.log('EBADF: bad file descriptor'); } catch (error) { dumpError(error); }
  test07();
}

function test07 () {
  console.log('before createReadStream');
  const rs = fs.createReadStream(theFile);
  console.log('after createReadStream');

  rs.on('open', function () {
    console.log('open');
  });

  rs.on('readable', function () {
    console.log('before read');
    let r = rs.read();
    console.log('after read');
    if (!r) {
      r = 'null';
    } else
    if (r.length >= 2) {
      console.log('length:', r.length);
      r = r[0].toString() + ', ' + r[1].toString();
    }
    console.log('readable:', r);
  });

  rs.on('end', function () {
    console.log('end');
  });
}
<|endoftext|>


<commit_before>/**
 * @description Select multiple albums or photos.
 * @copyright   2015 by Tobias Reich
 */

multiselect = {

	ids: []

}

multiselect.position = {

	top    : null,
	right  : null,
	bottom : null,
	left   : null

}

multiselect.bind = function() {

	$('.content').on('mousedown', (e) => { if (e.which===1) multiselect.show(e) })

	return true

}

multiselect.toggleItem = function(object, id) {

	if (album.isSmartID(id)) return

	let pos = $.inArray(id, multiselect.ids)

	if (pos!=-1) {
		multiselect.ids.splice(pos, 1)
		multiselect.deselect(object)
	} else {
		multiselect.ids.push(id)
		multiselect.select(object)
	}

}

multiselect.albumClick = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if (e.shiftKey) multiselect.toggleItem(albumObj, id)
	else            lychee.goto(id)

}

multiselect.photoClick = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if (e.shiftKey) multiselect.toggleItem(photoObj, id)
	else            lychee.goto(album.getID() + '/' + id)

}

multiselect.albumContextMenu = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if ($.inArray(id, multiselect.ids)!=-1) {
		contextMenu.albumMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.album(album.getID(), e)
	}

}

multiselect.photoContextMenu = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if ($.inArray(id, multiselect.ids)!=-1) {
		contextMenu.photoMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.photo(photo.getID(), e)
	}

}

multiselect.clearSelection = function() {

	multiselect.deselect('.photo.active, .album.active')
	multiselect.ids = []

}

multiselect.show = function(e) {

	if (lychee.publicMode)                          return false
	if (!visible.albums() && !visible.album())      return false
	if ($('.album:hover, .photo:hover').length!==0) return false
	if (visible.search())                           return false
	if (visible.multiselect())                      $('#multiselect').remove()

	multiselect.clearSelection()

	sidebar.setSelectable(false)

	multiselect.position.top    = e.pageY
	multiselect.position.right  = -1 * (e.pageX - $(document).width())
	multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height())
	multiselect.position.left   = e.pageX

	$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))

	$(document)
		.on('mousemove', multiselect.resize)
		.on('mouseup', (e) => { if (e.which===1) multiselect.getSelection(e) })

}

multiselect.resize = function(e) {

	if (multiselect.position.top    === null ||
	    multiselect.position.right  === null ||
	    multiselect.position.bottom === null ||
	    multiselect.position.left   === null) return false

	let newSize      = {}
	let documentSize = {}

	// Get the position of the mouse
	let mousePos = {
		x : e.pageX,
		y : e.pageY
	}

	// Default CSS
	let newCSS = {
		top    : null,
		bottom : null,
		height : null,
		left   : null,
		right  : null,
		width  : null
	}

	if (mousePos.y>=multiselect.position.top) {

		documentSize.height = $(document).height()

		// Do not leave the screen
		newSize.height = mousePos.y - multiselect.position.top
		if ((multiselect.position.top + newSize.height)>=documentSize.height) {
			newSize.height -= (multiselect.position.top + newSize.height) - documentSize.height + 2
		}

		newCSS.top    = multiselect.position.top
		newCSS.bottom = 'inherit'
		newCSS.height = newSize.height

	} else {

		newCSS.top    = 'inherit'
		newCSS.bottom = multiselect.position.bottom
		newCSS.height = multiselect.position.top - e.pageY

	}

	if (mousePos.x>=multiselect.position.left) {

		documentSize.width = $(document).width()

		// Do not leave the screen
		newSize.width = mousePos.x - multiselect.position.left
		if ((multiselect.position.left + newSize.width)>=documentSize.width) {
			newSize.width -= (multiselect.position.left + newSize.width) - documentSize.width + 2
		}

		newCSS.right = 'inherit'
		newCSS.left  = multiselect.position.left
		newCSS.width = newSize.width

	} else {

		newCSS.right = multiselect.position.right
		newCSS.left  = 'inherit'
		newCSS.width = multiselect.position.left - e.pageX

	}

	// Updated all CSS properties at once
	$('#multiselect').css(newCSS)

}

multiselect.stopResize = function() {

	if (multiselect.position.top!==null) $(document).off('mousemove mouseup')

}

multiselect.getSize = function() {

	if (!visible.multiselect()) return false

	let $elem  = $('#multiselect')
	let offset = $elem.offset()

	return {
		top    : offset.top,
		left   : offset.left,
		width  : parseInt($elem.css('width').replace('px', '')),
		height : parseInt($elem.css('height').replace('px', ''))
	}

}

multiselect.getSelection = function(e) {

	let tolerance = 150
	let ids       = []
	let size      = multiselect.getSize()

	if (visible.contextMenu())  return false
	if (!visible.multiselect()) return false

	$('.photo, .album').each(function() {

		let offset = $(this).offset()

		if (offset.top>=(size.top - tolerance) &&
			offset.left>=(size.left - tolerance) &&
			(offset.top + 206)<=(size.top + size.height + tolerance) &&
			(offset.left + 206)<=(size.left + size.width + tolerance)) {

			let id = $(this).data('id')

			if (id!=null && id!==0 && album.isSmartID(id)===false) {

				ids.push(id)
				multiselect.select(this)

			}

		}

	})

	if (ids.length!==0 && visible.album())       contextMenu.photoMulti(ids, e)
	else if (ids.length!==0 && visible.albums()) contextMenu.albumMulti(ids, e)
	else                                         multiselect.close()

}

multiselect.select = function(id) {

	let el = $(id)

	el.addClass('selected')
	el.addClass('active')

}

multiselect.deselect = function(id) {

	let el = $(id)

	el.removeClass('selected')
	el.removeClass('active')

}

multiselect.close = function() {

	multiselect.clearSelection()

	sidebar.setSelectable(true)

	multiselect.stopResize()

	multiselect.position.top    = null
	multiselect.position.right  = null
	multiselect.position.bottom = null
	multiselect.position.left   = null

	lychee.animate('#multiselect', 'fadeOut')
	setTimeout(() => $('#multiselect').remove(), 300)

}<commit_msg>Don't show context menu on finished multiselect.<commit_after>/**
 * @description Select multiple albums or photos.
 * @copyright   2015 by Tobias Reich
 */

multiselect = {

	ids: []

}

multiselect.position = {

	top    : null,
	right  : null,
	bottom : null,
	left   : null

}

multiselect.bind = function() {

	$('.content').on('mousedown', (e) => { if (e.which===1) multiselect.show(e) })

	return true

}

multiselect.toggleItem = function(object, id) {

	if (album.isSmartID(id)) return

	let pos = $.inArray(id, multiselect.ids)

	if (pos!=-1) {
		multiselect.ids.splice(pos, 1)
		multiselect.deselect(object)
	} else {
		multiselect.ids.push(id)
		multiselect.select(object)
	}

}

multiselect.addItem = function(object, id) {

	let pos = $.inArray(id, multiselect.ids)

	if (pos==-1) {
		multiselect.ids.push(id)
		multiselect.select(object)
	}

}

multiselect.albumClick = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if (e.shiftKey) multiselect.toggleItem(albumObj, id)
	else            lychee.goto(id)

}

multiselect.photoClick = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if (e.shiftKey) multiselect.toggleItem(photoObj, id)
	else            lychee.goto(album.getID() + '/' + id)

}

multiselect.albumContextMenu = function(e, albumObj) {

	let id = albumObj.attr('data-id')

	if ($.inArray(id, multiselect.ids)!=-1) {
		contextMenu.albumMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.album(album.getID(), e)
	}

}

multiselect.photoContextMenu = function(e, photoObj) {

	let id = photoObj.attr('data-id')

	if ($.inArray(id, multiselect.ids)!=-1) {
		contextMenu.photoMulti(multiselect.ids, e)
		multiselect.ids = []
	} else {
		multiselect.clearSelection()
		contextMenu.photo(photo.getID(), e)
	}

}

multiselect.clearSelection = function() {

	multiselect.deselect('.photo.active, .album.active')
	multiselect.ids = []

}

multiselect.show = function(e) {

	if (lychee.publicMode)                          return false
	if (!visible.albums() && !visible.album())      return false
	if ($('.album:hover, .photo:hover').length!==0) return false
	if (visible.search())                           return false
	if (visible.multiselect())                      $('#multiselect').remove()

	sidebar.setSelectable(false)

	multiselect.position.top    = e.pageY
	multiselect.position.right  = -1 * (e.pageX - $(document).width())
	multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height())
	multiselect.position.left   = e.pageX

	$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left))

	$(document)
		.on('mousemove', multiselect.resize)
		.on('mouseup', (e) => { if (e.which===1) multiselect.getSelection(e) })

}

multiselect.resize = function(e) {

	if (multiselect.position.top    === null ||
	    multiselect.position.right  === null ||
	    multiselect.position.bottom === null ||
	    multiselect.position.left   === null) return false

	let newSize      = {}
	let documentSize = {}

	// Get the position of the mouse
	let mousePos = {
		x : e.pageX,
		y : e.pageY
	}

	// Default CSS
	let newCSS = {
		top    : null,
		bottom : null,
		height : null,
		left   : null,
		right  : null,
		width  : null
	}

	if (mousePos.y>=multiselect.position.top) {

		documentSize.height = $(document).height()

		// Do not leave the screen
		newSize.height = mousePos.y - multiselect.position.top
		if ((multiselect.position.top + newSize.height)>=documentSize.height) {
			newSize.height -= (multiselect.position.top + newSize.height) - documentSize.height + 2
		}

		newCSS.top    = multiselect.position.top
		newCSS.bottom = 'inherit'
		newCSS.height = newSize.height

	} else {

		newCSS.top    = 'inherit'
		newCSS.bottom = multiselect.position.bottom
		newCSS.height = multiselect.position.top - e.pageY

	}

	if (mousePos.x>=multiselect.position.left) {

		documentSize.width = $(document).width()

		// Do not leave the screen
		newSize.width = mousePos.x - multiselect.position.left
		if ((multiselect.position.left + newSize.width)>=documentSize.width) {
			newSize.width -= (multiselect.position.left + newSize.width) - documentSize.width + 2
		}

		newCSS.right = 'inherit'
		newCSS.left  = multiselect.position.left
		newCSS.width = newSize.width

	} else {

		newCSS.right = multiselect.position.right
		newCSS.left  = 'inherit'
		newCSS.width = multiselect.position.left - e.pageX

	}

	// Updated all CSS properties at once
	$('#multiselect').css(newCSS)

}

multiselect.stopResize = function() {

	if (multiselect.position.top!==null) $(document).off('mousemove mouseup')

}

multiselect.getSize = function() {

	if (!visible.multiselect()) return false

	let $elem  = $('#multiselect')
	let offset = $elem.offset()

	return {
		top    : offset.top,
		left   : offset.left,
		width  : parseInt($elem.css('width').replace('px', '')),
		height : parseInt($elem.css('height').replace('px', ''))
	}

}

multiselect.getSelection = function(e) {

	let tolerance = 150
	let ids       = []
	let size      = multiselect.getSize()

	if (visible.contextMenu())  return false
	if (!visible.multiselect()) return false

	if (!e.shiftKey && (size.width==0 || size.height==0)) {
		multiselect.close()
		return false
	}

	$('.photo, .album').each(function() {

		let offset = $(this).offset()

		if (offset.top>=(size.top - tolerance) &&
			offset.left>=(size.left - tolerance) &&
			(offset.top + 206)<=(size.top + size.height + tolerance) &&
			(offset.left + 206)<=(size.left + size.width + tolerance)) {

			let id = $(this).attr('data-id')

			multiselect.addItem($(this), id)

		}

	})

	multiselect.hide()

}

multiselect.select = function(id) {

	let el = $(id)

	el.addClass('selected')
	el.addClass('active')

}

multiselect.deselect = function(id) {

	let el = $(id)

	el.removeClass('selected')
	el.removeClass('active')

}

multiselect.hide = function() {

	sidebar.setSelectable(true)

	multiselect.stopResize()

	multiselect.position.top    = null
	multiselect.position.right  = null
	multiselect.position.bottom = null
	multiselect.position.left   = null

	lychee.animate('#multiselect', 'fadeOut')
	setTimeout(() => $('#multiselect').remove(), 300)

}

multiselect.close = function() {

	multiselect.clearSelection()

	multiselect.hide()

}<|endoftext|>


<commit_before>describe('podio-js', function() {

  var PodioJS = require('../lib/podio-js');
  var sinon = require('sinon');

  describe('constructor', function() {

    it('should have version property', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123,
      };
      var instance = new PodioJS(authOptions);

      expect(instance.VERSION).toEqual(require('../package.json').version);
    });

    it('should set auth data correctly', function() {
      var authOptions = {
        authType: 'server',
        clientId: 123,
        clientSecret: 'abcdef'
      };
      var instance = new PodioJS(authOptions);

      expect(instance.authType).toEqual(authOptions.authType);
      expect(instance.clientId).toEqual(authOptions.clientId);
      expect(instance.clientSecret).toEqual(authOptions.clientSecret);
    });

    it('should throw an exception if one of the auth properties is missing', function() {
      expect(function() { new PodioJS(); }).toThrow(new Error('Authentication options are missing'));
      expect(function() { new PodioJS({}); }).toThrow(new Error('Missing auth property authType'));
      expect(function() { new PodioJS({ authType: 'client' }); }).toThrow(new Error('Missing auth property clientId'));
      expect(function() { new PodioJS({ authType: 'server', clientId: 123 }); }).toThrow(new Error('Missing auth property clientSecret'));
    });

    it('should not throw an exception if clientSecret is missing for a client auth', function() {
      var instance = new PodioJS({ authType: 'client', clientId: 123 });

      expect(instance).toBeDefined();
    });

    it('should set the API URL to default', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var instance = new PodioJS(authOptions);

      expect(instance.apiURL).toEqual('https://api.podio.com:443');
    });

    it('should set the API URL to the one provided in options', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var apiURL = 'https://api2.podio.com';
      var instance = new PodioJS(authOptions, { apiURL: apiURL});

      expect(instance.apiURL).toEqual(apiURL);
    });

    it('should get the authObject from the session store when one is provided', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var sessionStore = {};
      var instance;

      sinon.stub(PodioJS.prototype, '_getAuthFromStore');

      instance = new PodioJS(authOptions, { sessionStore: sessionStore });

      expect(instance._getAuthFromStore.calledOnce).toBe(true);

      PodioJS.prototype._getAuthFromStore.restore();
    });

    it('should still set the apiURL to default when a session store is provided', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var sessionStore = {
        get: sinon.stub().returns({ accessToken: 123 })
      };
      var instance = new PodioJS(authOptions, { sessionStore: sessionStore });

      expect(instance.apiURL).toEqual('https://api.podio.com:443');
    });

    it('should set onTokenWillRefresh callback', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var onTokenWillRefresh = function() {};

      var instance = new PodioJS(authOptions, { onTokenWillRefresh: onTokenWillRefresh });

      expect(instance.onTokenWillRefresh).toEqual(onTokenWillRefresh);
    });

  });

});
<commit_msg>Add test for setting "afterTokenRefreshed" callback<commit_after>describe('podio-js', function() {

  var PodioJS = require('../lib/podio-js');
  var sinon = require('sinon');

  describe('constructor', function() {

    it('should have version property', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123,
      };
      var instance = new PodioJS(authOptions);

      expect(instance.VERSION).toEqual(require('../package.json').version);
    });

    it('should set auth data correctly', function() {
      var authOptions = {
        authType: 'server',
        clientId: 123,
        clientSecret: 'abcdef'
      };
      var instance = new PodioJS(authOptions);

      expect(instance.authType).toEqual(authOptions.authType);
      expect(instance.clientId).toEqual(authOptions.clientId);
      expect(instance.clientSecret).toEqual(authOptions.clientSecret);
    });

    it('should throw an exception if one of the auth properties is missing', function() {
      expect(function() { new PodioJS(); }).toThrow(new Error('Authentication options are missing'));
      expect(function() { new PodioJS({}); }).toThrow(new Error('Missing auth property authType'));
      expect(function() { new PodioJS({ authType: 'client' }); }).toThrow(new Error('Missing auth property clientId'));
      expect(function() { new PodioJS({ authType: 'server', clientId: 123 }); }).toThrow(new Error('Missing auth property clientSecret'));
    });

    it('should not throw an exception if clientSecret is missing for a client auth', function() {
      var instance = new PodioJS({ authType: 'client', clientId: 123 });

      expect(instance).toBeDefined();
    });

    it('should set the API URL to default', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var instance = new PodioJS(authOptions);

      expect(instance.apiURL).toEqual('https://api.podio.com:443');
    });

    it('should set the API URL to the one provided in options', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var apiURL = 'https://api2.podio.com';
      var instance = new PodioJS(authOptions, { apiURL: apiURL});

      expect(instance.apiURL).toEqual(apiURL);
    });

    it('should get the authObject from the session store when one is provided', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var sessionStore = {};
      var instance;

      sinon.stub(PodioJS.prototype, '_getAuthFromStore');

      instance = new PodioJS(authOptions, { sessionStore: sessionStore });

      expect(instance._getAuthFromStore.calledOnce).toBe(true);

      PodioJS.prototype._getAuthFromStore.restore();
    });

    it('should still set the apiURL to default when a session store is provided', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var sessionStore = {
        get: sinon.stub().returns({ accessToken: 123 })
      };
      var instance = new PodioJS(authOptions, { sessionStore: sessionStore });

      expect(instance.apiURL).toEqual('https://api.podio.com:443');
    });

    it('should set onTokenWillRefresh callback', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var onTokenWillRefresh = function() {};

      var instance = new PodioJS(authOptions, { onTokenWillRefresh: onTokenWillRefresh });

      expect(instance.onTokenWillRefresh).toEqual(onTokenWillRefresh);
    });

    it('should set afterTokenRefreshed callback', function() {
      var authOptions = {
        authType: 'client',
        clientId: 123
      };
      var afterTokenRefreshed = function() {};

      var instance = new PodioJS(authOptions, { afterTokenRefreshed: afterTokenRefreshed });

      expect(instance.afterTokenRefreshed).toEqual(afterTokenRefreshed);
    });

  });

});
<|endoftext|>


<commit_before>describe("Property", function () {
	before(function () {
		fixture.setBase("test/fixtures");
	});

	beforeEach(function () {
		fixture.load("property_elements.html");

		this.input = document.getElementById("id1");
		this.$input = Jamon.get(this.input);

		this.el = document.getElementById("id3");
		this.$el = Jamon.get(this.el);

		this.el2 = document.getElementById("id4");
		this.$el2 = Jamon.get(this.el2);

		this.property = "disabled";
		this.customProperty = "customProperty";
		this.$empty = Jamon.get();
		this.content = "
new content
";
	});

	afterEach(function () {
		fixture.cleanup();
	});

	describe("prop(name)", function () {
		it("should return the property value", function () {
			expect(this.$input.prop(this.property)).to.be.false;

			this.input[this.property] = true;

			expect(this.$input.prop(this.property)).to.be.true;

			this.input[this.property] = this.property;

			expect(this.$input.prop(this.property)).to.be.true;
		});

		it("should return undefined when the property is missing", function () {
			expect(this.$input.prop("fakeProperty")).to.be.undefined;
		});

		it("should work on an empty collection", function () {
			expect(this.$empty.prop(this.property)).to.be.undefined;
		});

		it("should work on multiple items", function () {
			this.input[this.property] = true;

			expect(Jamon.getAll("#id1, #id2").prop(this.property)).to.equal(this.input[this.property]);
		});

	});

	describe("prop(name, value)", function () {

		it("should set the property value", function () {
			this.$input.prop(this.property, true);

			expect(this.input[this.property]).to.be.true;

			this.$input.prop(this.property, this.property);

			expect(this.input[this.property]).to.be.true;
		});

		it("should create a new property if there was none", function () {
			var value = "newValue";

			expect(this.input[this.customProperty]).to.not.exist;

			this.$input.prop(this.customProperty, value);

			expect(this.input[this.customProperty]).to.exist;
			expect(this.input[this.customProperty]).to.equal(value);
		});

		it("should remove the property when called with null", function () {
			this.input[this.customProperty] = true;
			this.$input.prop(this.customProperty, null);

			expect(this.input[this.customProperty]).to.not.exist;
		});

		it("should work on an empty collection", function () {
			expect(calling(this.$empty.prop).on(this.$empty).with(this.property, true)).to.not.throw(Error);
		});

		it("should work on multiple items", function () {
			var input2 = document.getElementById("id2");

			expect(this.input[this.property]).to.be.false;
			expect(input2[this.property]).to.be.false;

			Jamon.getAll("#id1, #id2").prop(this.property, true);

			expect(this.input[this.property]).to.be.true;
			expect(input2[this.property]).to.be.true;
		});

		it("should return the Jamón instance", function () {
			expect(this.$input.prop(this.property, true)).to.equal(this.$input);
		});

	});

	describe("html()", function () {
		it("should return the html content", function () {
			var html = this.el.innerHTML
			expect(html).to.not.equal("");

			expect(this.$el.html()).to.equal(html);
		});

		it("should return empty string when the element is empty", function () {
			expect(this.$el2.html()).to.equal("");
		});

		it("should work on an empty collection", function () {
			expect(this.$empty.html()).to.be.undefined;
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.innerHTML).to.not.equal(this.el2.innerHTML);
			expect(els.html()).to.equal(this.el.innerHTML);
		});
	});

	describe("html(value)", function () {
		it("should set the html content", function () {
			this.$el.html(this.content);

			expect(this.el.innerHTML).to.equal(this.content);
		});

		it("calling with empty string should empty html content", function () {
			this.$el.html("");
			expect(this.el.innerHTML).to.equal("");
		});

		it("calling with null should empty html content", function () {
			this.$el.html(null);
			expect(this.el.innerHTML).to.equal("");
		});

		it("should work on an empty collection", function () {
			expect(calling(this.$empty.html).on(this.$empty).with(this.content)).to.not.throw(Error);
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.innerHTML).to.not.equal(this.el2.innerHTML);

			els.html(this.content);

			expect(this.el.innerHTML).to.equal(this.content);
			expect(this.el2.innerHTML).to.equal(this.content);
		});

		it("should return the Jamón instance", function () {
			expect(this.$el.html(this.content)).to.equal(this.$el);
		});
	});
	/*
	describe("text()", function () {
		it("should return the text content", function () {
		});

		it("should return empty string when the element is empty", function () {
		});

		it("should work on an empty collection", function () {
		});

		it("should work on multiple items", function () {
		});

	});

	describe("text(value)", function () {

	});
	*/
});
<commit_msg>Tests for text()<commit_after>describe("Property", function () {
	before(function () {
		fixture.setBase("test/fixtures");
	});

	beforeEach(function () {
		fixture.load("property_elements.html");

		this.input = document.getElementById("id1");
		this.$input = Jamon.get(this.input);

		this.el = document.getElementById("id3");
		this.$el = Jamon.get(this.el);

		this.el2 = document.getElementById("id4");
		this.$el2 = Jamon.get(this.el2);

		this.property = "disabled";
		this.customProperty = "customProperty";
		this.$empty = Jamon.get();
		this.content = "
new content
";
	});

	afterEach(function () {
		fixture.cleanup();
	});

	describe("prop(name)", function () {
		it("should return the property value", function () {
			expect(this.$input.prop(this.property)).to.be.false;

			this.input[this.property] = true;

			expect(this.$input.prop(this.property)).to.be.true;

			this.input[this.property] = this.property;

			expect(this.$input.prop(this.property)).to.be.true;
		});

		it("should return undefined when the property is missing", function () {
			expect(this.$input.prop("fakeProperty")).to.be.undefined;
		});

		it("should work on an empty collection", function () {
			expect(this.$empty.prop(this.property)).to.be.undefined;
		});

		it("should work on multiple items", function () {
			this.input[this.property] = true;

			expect(Jamon.getAll("#id1, #id2").prop(this.property)).to.equal(this.input[this.property]);
		});

	});

	describe("prop(name, value)", function () {

		it("should set the property value", function () {
			this.$input.prop(this.property, true);

			expect(this.input[this.property]).to.be.true;

			this.$input.prop(this.property, this.property);

			expect(this.input[this.property]).to.be.true;
		});

		it("should create a new property if there was none", function () {
			var value = "newValue";

			expect(this.input[this.customProperty]).to.not.exist;

			this.$input.prop(this.customProperty, value);

			expect(this.input[this.customProperty]).to.exist;
			expect(this.input[this.customProperty]).to.equal(value);
		});

		it("should remove the property when called with null", function () {
			this.input[this.customProperty] = true;
			this.$input.prop(this.customProperty, null);

			expect(this.input[this.customProperty]).to.not.exist;
		});

		it("should work on an empty collection", function () {
			expect(calling(this.$empty.prop).on(this.$empty).with(this.property, true)).to.not.throw(Error);
		});

		it("should work on multiple items", function () {
			var input2 = document.getElementById("id2");

			expect(this.input[this.property]).to.be.false;
			expect(input2[this.property]).to.be.false;

			Jamon.getAll("#id1, #id2").prop(this.property, true);

			expect(this.input[this.property]).to.be.true;
			expect(input2[this.property]).to.be.true;
		});

		it("should return the Jamón instance", function () {
			expect(this.$input.prop(this.property, true)).to.equal(this.$input);
		});

	});

	describe("html()", function () {
		it("should return the html content", function () {
			var html = this.el.innerHTML
			expect(html).to.not.equal("");

			expect(this.$el.html()).to.equal(html);
		});

		it("should return empty string when the element is empty", function () {
			expect(this.$el2.html()).to.equal("");
		});

		it("should work on an empty collection", function () {
			expect(this.$empty.html()).to.be.undefined;
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.innerHTML).to.not.equal(this.el2.innerHTML);
			expect(els.html()).to.equal(this.el.innerHTML);
		});
	});

	describe("html(value)", function () {
		it("should set the html content", function () {
			this.$el.html(this.content);

			expect(this.el.innerHTML).to.equal(this.content);
		});

		it("calling with empty string should empty html content", function () {
			this.$el.html("");
			expect(this.el.innerHTML).to.equal("");
		});

		it("calling with null should empty html content", function () {
			this.$el.html(null);
			expect(this.el.innerHTML).to.equal("");
		});

		it("should work on an empty collection", function () {
			expect(calling(this.$empty.html).on(this.$empty).with(this.content)).to.not.throw(Error);
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.innerHTML).to.not.equal(this.el2.innerHTML);

			els.html(this.content);

			expect(this.el.innerHTML).to.equal(this.content);
			expect(this.el2.innerHTML).to.equal(this.content);
		});

		it("should return the Jamón instance", function () {
			expect(this.$el.html(this.content)).to.equal(this.$el);
		});
	});

	describe("text()", function () {
		it("should return the text content", function () {
			var text = this.el.textContent
			expect(text).to.not.equal("");

			expect(this.$el.text()).to.equal(text);
		});

		it("should return empty string when the element is empty", function () {
			var text = this.el2.textContent
			expect(text).to.equal("");

			expect(this.$el2.text()).to.equal(text);
		});

		it("should work on an empty collection", function () {
			expect(this.$empty.text()).to.be.undefined;
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.textContent).to.not.equal(this.el2.textContent);
			expect(els.text()).to.equal(this.el.textContent);
		});

	});

	describe("text(value)", function () {
		it("should set the text content", function () {
			this.$el.text(this.content);

			expect(this.el.textContent).to.equal(this.content);
		});

		it("calling with empty string should empty text content", function () {
			this.$el.text("");
			expect(this.el.textContent).to.equal("");
		});

		it("calling with null should empty text content", function () {
			this.$el.text(null);
			expect(this.el.textContent).to.equal("");
		});

		it("should work on an empty collection", function () {
			expect(calling(this.$empty.text).on(this.$empty).with(this.content)).to.not.throw(Error);
		});

		it("should work on multiple items", function () {
			var els = Jamon.getAll([this.el, this.el2]);

			expect(this.el.textContent).to.not.equal(this.el2.textContent);

			els.text(this.content);

			expect(this.el.textContent).to.equal(this.content);
			expect(this.el2.textContent).to.equal(this.content);
		});

		it("should return the Jamón instance", function () {
			expect(this.$el.text(this.content)).to.equal(this.$el);
		});

	});

});
<|endoftext|>


<commit_before>import db from '..'
import EventModel from './event-model'

export default class Visit extends EventModel {
    /**
     * @type {number} duration Time user was active during visit (ms).
     */
    duration

    /**
     * @type {number} scrollPx Y-axis pixel scrolled to at point in time.
     */
    scrollPx

    /**
     * @type {number} scrollPerc
     */
    scrollPerc

    /**
     * @type {number} scrollMaxPx Furthest y-axis pixel scrolled to during visit.
     */
    scrollMaxPx

    /**
     * @type {number} scrollMaxPerc
     */
    scrollMaxPerc

    constructor({ url, time, ...props }) {
        super({ url, time })

        this.duration = props.duration
        this.scrollPx = props.scrollPx
        this.scrollPerc = props.scrollPerc
        this.scrollMaxPx = props.scrollMaxPx
        this.scrollMaxPerc = props.scrollMaxPerc
    }

    save() {
        return db.visits.put(this)
    }
}
<commit_msg>Compare page visits before DB writes<commit_after>import db from '..'
import EventModel from './event-model'

export default class Visit extends EventModel {
    /**
     * @type {number} duration Time user was active during visit (ms).
     */
    duration

    /**
     * @type {number} scrollPx Y-axis pixel scrolled to at point in time.
     */
    scrollPx

    /**
     * @type {number} scrollPerc
     */
    scrollPerc

    /**
     * @type {number} scrollMaxPx Furthest y-axis pixel scrolled to during visit.
     */
    scrollMaxPx

    /**
     * @type {number} scrollMaxPerc
     */
    scrollMaxPerc

    constructor({ url, time, ...props }) {
        super({ url, time })

        this.duration = props.duration
        this.scrollPx = props.scrollPx
        this.scrollPerc = props.scrollPerc
        this.scrollMaxPx = props.scrollMaxPx
        this.scrollMaxPerc = props.scrollMaxPerc
    }

    get pk() {
        return [this.time, this.url]
    }

    /**
     * @param {Visit} visit
     * @returns {boolean}
     */
    _hasChanged(visit) {
        return (
            visit == null ||
            visit.duration !== this.duration ||
            visit.scrollPx !== this.scrollPx ||
            visit.scrollPerc !== this.scrollPerc ||
            visit.scrollMaxPx !== this.scrollMaxPx ||
            visit.scrollMaxPerc !== this.scrollMaxPerc
        )
    }

    async save() {
        // Only update if changes detected between existing visit and this one
        const existingVisit = await db.visits.get(this.pk)
        if (!this._hasChanged(existingVisit)) {
            return this.pk
        }

        return db.visits.put(this)
    }
}
<|endoftext|>


<commit_before>import _ from 'lodash'
import React, { Component } from 'react'
import PropType from 'prop-types'
import classNames from 'classnames'

import Arrow from './Arrow'
import Clear from './Clear'
import FocusPlaceholder from './FocusPlaceholder'
import Input from './Input'
import Menu from './Menu'
import Option from './Option'
import Value from './Value'
import ValueGroup from './ValueGroup'

import './select.scss'

// credit to https://github.com/JedWatson/react-select for many patterns and techniques used here
class SimpleSelect extends Component {
  static propTypes = {
    autoCloseMenu: PropType.bool,
    clearable: PropType.bool,
    clearComponent: PropType.func,
    clearRenderer: PropType.func,
    focusPlaceholderComponent: PropType.func,
    inputComponent: PropType.func,
    inputValue: PropType.string,
    isOpen: PropType.bool,
    labelKey: PropType.string,
    menuComponent: PropType.func,
    onBlur: PropType.func,
    onChange: PropType.func,
    onFocus: PropType.func,
    onInputChange: PropType.func,
    optionComponent: PropType.func,
    optionRenderer: PropType.func,
    options: PropType.array,
    placeholder: PropType.string,
    showInput: PropType.bool,
    value: PropType.oneOfType([PropType.object, PropType.array]),
    valueComponent: PropType.func,
    valueRenderer: PropType.func,
    valueGroupComponent: PropType.func,
    valueGroupRenderer: PropType.func,
    valueKey: PropType.string
  }

  static defaultProps = {
    autoCloseMenu: PropType.bool,
    clearable: false,
    clearComponent: Clear,
    clearRenderer: null,
    focusPlaceholderComponent: FocusPlaceholder,
    inputComponent: Input,
    inputValue: '',
    isOpen: false,
    labelKey: 'label',
    menuComponent: Menu,
    onBlur: null,
    onChange: null,
    onFocus: null,
    onInputChange: null,
    optionComponent: Option,
    optionRenderer: null,
    options: [],
    placeholder: 'Select value...',
    showInput: false,
    value: null,
    valueComponent: Value,
    valueRenderer: null,
    valueGroupComponent: ValueGroup,
    valueGroupRenderer: null,
    valueKey: 'value'
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      isFocused: false
    }
  }

  componentDidMount() {
    document.addEventListener('touchstart', this.onDocumentClick, false)
  }

  componentWillUnmount() {
    document.removeEventListener('touchstart', this.onDocumentClick, false)
  }

  onDocumentClick = (event) => {
    if (this.container && !this.container.contains(event.target)) {
      this.setState({ isOpen: false })
    }
  }

  onValueMouseDown = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (event.target.tagName === 'INPUT') {
      // allow user to select input text
      return
    }

    const isOpen = this.props.isOpen || this.props.showInput ? true : !this.state.isOpen

    event.stopPropagation()
    event.preventDefault()

    this.focus()
    this.setState({ isOpen })
  }

  onInputBlur = (event) => {
    if (this.props.onBlur) {
      this.props.onBlur(event)
    }

    this.setState({
      isOpen: false,
      isFocused: false
    })
  }

  onInputFocus = (event) => {
    if (this.props.onFocus) {
      this.props.onFocus(event)
    }

    const isOpen = this.props.isOpen || this.state.isOpen

    this.setState({
      isOpen,
      isFocused: true
    })
  }

  onInputChange = (event) => {
    const inputValue = event.target.value

    if (this.props.inputValue === inputValue) {
      return
    }

    if (this.props.onInputChange) {
      this.props.onInputChange(inputValue, event)
    }
  }

  onArrowClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (this.props.isOpen || !this.state.isOpen) {
      // let it bubble up
      return
    }

    event.stopPropagation()
    event.preventDefault()

    // we know we are closing menu
    this.closeMenu()
  }

  onClearClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.closeMenu()
    this.emitValueChange(null)
  }

  onOptionClick = (event, option) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.closeMenu(!this.props.autoCloseMenu)
    this.emitValueChange(option)
  }

  emitValueChange = (option) => {
    const { value, valueKey } = this.props
    const valueSelected = (option === null || value === null) && option !== value
    const valueChanged = _.isArray(value)
      ? true
      : value && option && value[valueKey] !== option[valueKey]

    if ((valueSelected || valueChanged) && this.props.onChange) {
      this.props.onChange(option)
    }

    this.clearInputValue()

    if (this.props.autoCloseMenu) {
      this.focus()
    }
  }

  closeMenu = (isOpen = false) => {
    this.setState({ isOpen }, this.focus)
  }

  // check for right-clicks, etc
  isSecondayClick = event => (event.type === 'mousedown' && event.button !== 0)

  focus = () => { this.input.focus() }

  blur = () => { this.input.blur() }

  clearInputValue = (event) => {
    if (this.props.onInputChange) {
      this.props.onInputChange('', event)
    }
  }

  renderMenu() {
    const {
      labelKey,
      optionComponent,
      optionRenderer,
      options,
      value,
      valueKey
    } = this.props
    const MenuComponent = this.props.menuComponent
    return (

        <MenuComponent
          labelKey={labelKey}
          onOptionClick={this.onOptionClick}
          optionComponent={optionComponent}
          optionRenderer={optionRenderer}
          options={options}
          value={value}
          valueKey={valueKey}
        />

    )
  }

  renderInput(isOpen) {
    const inputProps = {
      // TODO implement static list of select ids
      ariaControls: '',
      ariaExpanded: isOpen ? 'true' : 'false',
      getRef: (ref) => { this.input = ref },
      inputValue: this.props.inputValue,
      onBlur: this.onInputBlur,
      onChange: this.onInputChange,
      onFocus: this.onInputFocus
    }
    const InputComponent = this.props.inputComponent

    return this.props.showInput
      ? <InputComponent {...inputProps} />
      : this.renderFocusPlaceholder(inputProps)
  }

  renderFocusPlaceholder(inputProps) {
    const FocusPlaceholderComponent = this.props.focusPlaceholderComponent
    const props = _.omit(inputProps, 'onChange', 'inputValue')
    return <FocusPlaceholderComponent {...props} />
  }

  renderClear() {
    const ClearComponent = this.props.clearComponent
    const { clearable, clearRenderer, value } = this.props
    const hasValue = _.isArray(value) ? value.length : value

    if (!clearable || !hasValue) {
      return null
    }

    return <ClearComponent clearRenderer={clearRenderer} onClearClick={this.onClearClick} />
  }

  render() {
    const isFocused = this.state.isFocused
    const isOpen = this.props.isOpen || this.state.isOpen
    const {
      inputValue,
      labelKey,
      placeholder,
      showInput,
      value,
      valueComponent,
      valueGroupRenderer,
      valueRenderer
    } = this.props
    const ValueGroupComponent = this.props.valueGroupComponent
    const selectClassName = classNames('crane-select', { open: isOpen, focus: isFocused })

    return (

 { this.container = container }}>

          <ValueGroupComponent
            inputValue={inputValue}
            labelKey={labelKey}
            placeholder={placeholder}
            showInput={showInput}
            value={value}
            valueRenderer={valueRenderer}
            valueComponent={valueComponent}
            valueGroupRenderer={valueGroupRenderer}
          />
          {this.renderInput(isOpen)}
          {this.renderClear()}
          <Arrow onArrowClick={this.onArrowClick} />

        {isOpen && this.renderMenu()}

    )
  }
}

export default SimpleSelect
<commit_msg>Allow Arrow components to be replaced.<commit_after>import _ from 'lodash'
import React, { Component } from 'react'
import PropType from 'prop-types'
import classNames from 'classnames'

import Arrow from './Arrow'
import Clear from './Clear'
import FocusPlaceholder from './FocusPlaceholder'
import Input from './Input'
import Menu from './Menu'
import Option from './Option'
import Value from './Value'
import ValueGroup from './ValueGroup'

import './select.scss'

// credit to https://github.com/JedWatson/react-select for many patterns and techniques used here
class SimpleSelect extends Component {
  static propTypes = {
    autoCloseMenu: PropType.bool,
    arrowComponent: PropType.arrowComponent,
    arrowRenderer: PropType.arrowRenderer,
    clearable: PropType.bool,
    clearComponent: PropType.func,
    clearRenderer: PropType.func,
    focusPlaceholderComponent: PropType.func,
    inputComponent: PropType.func,
    inputValue: PropType.string,
    isOpen: PropType.bool,
    labelKey: PropType.string,
    menuComponent: PropType.func,
    onBlur: PropType.func,
    onChange: PropType.func,
    onFocus: PropType.func,
    onInputChange: PropType.func,
    optionComponent: PropType.func,
    optionRenderer: PropType.func,
    options: PropType.array,
    placeholder: PropType.string,
    showInput: PropType.bool,
    value: PropType.oneOfType([PropType.object, PropType.array]),
    valueComponent: PropType.func,
    valueRenderer: PropType.func,
    valueGroupComponent: PropType.func,
    valueGroupRenderer: PropType.func,
    valueKey: PropType.string
  }

  static defaultProps = {
    autoCloseMenu: PropType.bool,
    arrowComponent: Arrow,
    arrowRenderer: null,
    clearable: false,
    clearComponent: Clear,
    clearRenderer: null,
    focusPlaceholderComponent: FocusPlaceholder,
    inputComponent: Input,
    inputValue: '',
    isOpen: false,
    labelKey: 'label',
    menuComponent: Menu,
    onBlur: null,
    onChange: null,
    onFocus: null,
    onInputChange: null,
    optionComponent: Option,
    optionRenderer: null,
    options: [],
    placeholder: 'Select value...',
    showInput: false,
    value: null,
    valueComponent: Value,
    valueRenderer: null,
    valueGroupComponent: ValueGroup,
    valueGroupRenderer: null,
    valueKey: 'value'
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      isFocused: false
    }
  }

  componentDidMount() {
    document.addEventListener('touchstart', this.onDocumentClick, false)
  }

  componentWillUnmount() {
    document.removeEventListener('touchstart', this.onDocumentClick, false)
  }

  onDocumentClick = (event) => {
    if (this.container && !this.container.contains(event.target)) {
      this.setState({ isOpen: false })
    }
  }

  onValueMouseDown = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (event.target.tagName === 'INPUT') {
      // allow user to select input text
      return
    }

    const isOpen = this.props.isOpen || this.props.showInput ? true : !this.state.isOpen

    event.stopPropagation()
    event.preventDefault()

    this.focus()
    this.setState({ isOpen })
  }

  onInputBlur = (event) => {
    if (this.props.onBlur) {
      this.props.onBlur(event)
    }

    this.setState({
      isOpen: false,
      isFocused: false
    })
  }

  onInputFocus = (event) => {
    if (this.props.onFocus) {
      this.props.onFocus(event)
    }

    const isOpen = this.props.isOpen || this.state.isOpen

    this.setState({
      isOpen,
      isFocused: true
    })
  }

  onInputChange = (event) => {
    const inputValue = event.target.value

    if (this.props.inputValue === inputValue) {
      return
    }

    if (this.props.onInputChange) {
      this.props.onInputChange(inputValue, event)
    }
  }

  onArrowClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (this.props.isOpen || !this.state.isOpen) {
      // let it bubble up
      return
    }

    event.stopPropagation()
    event.preventDefault()

    // we know we are closing menu
    this.closeMenu()
  }

  onClearClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.closeMenu()
    this.emitValueChange(null)
  }

  onOptionClick = (event, option) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.closeMenu(!this.props.autoCloseMenu)
    this.emitValueChange(option)
  }

  emitValueChange = (option) => {
    const { value, valueKey } = this.props
    const valueSelected = (option === null || value === null) && option !== value
    const valueChanged = _.isArray(value)
      ? true
      : value && option && value[valueKey] !== option[valueKey]

    if ((valueSelected || valueChanged) && this.props.onChange) {
      this.props.onChange(option)
    }

    this.clearInputValue()

    if (this.props.autoCloseMenu) {
      this.focus()
    }
  }

  closeMenu = (isOpen = false) => {
    this.setState({ isOpen }, this.focus)
  }

  // check for right-clicks, etc
  isSecondayClick = event => (event.type === 'mousedown' && event.button !== 0)

  focus = () => { this.input.focus() }

  blur = () => { this.input.blur() }

  clearInputValue = (event) => {
    if (this.props.onInputChange) {
      this.props.onInputChange('', event)
    }
  }

  renderArrow() {
    const ArrowComponent = this.props.arrowComponent
    const { arrowRenderer } = this.props

    return <ArrowComponent arrowRenderer={arrowRenderer} onArrowClick={this.onArrowClick} />
  }

  renderClear() {
    const ClearComponent = this.props.clearComponent
    const { clearable, clearRenderer, value } = this.props
    const hasValue = _.isArray(value) ? value.length : value

    if (!clearable || !hasValue) {
      return null
    }

    return <ClearComponent clearRenderer={clearRenderer} onClearClick={this.onClearClick} />
  }

  renderMenu() {
    const {
      labelKey,
      optionComponent,
      optionRenderer,
      options,
      value,
      valueKey
    } = this.props
    const MenuComponent = this.props.menuComponent
    return (

        <MenuComponent
          labelKey={labelKey}
          onOptionClick={this.onOptionClick}
          optionComponent={optionComponent}
          optionRenderer={optionRenderer}
          options={options}
          value={value}
          valueKey={valueKey}
        />

    )
  }

  renderInput(isOpen) {
    const inputProps = {
      // TODO implement static list of select ids
      ariaControls: '',
      ariaExpanded: isOpen ? 'true' : 'false',
      getRef: (ref) => { this.input = ref },
      inputValue: this.props.inputValue,
      onBlur: this.onInputBlur,
      onChange: this.onInputChange,
      onFocus: this.onInputFocus
    }
    const InputComponent = this.props.inputComponent

    return this.props.showInput
      ? <InputComponent {...inputProps} />
      : this.renderFocusPlaceholder(inputProps)
  }

  renderFocusPlaceholder(inputProps) {
    const FocusPlaceholderComponent = this.props.focusPlaceholderComponent
    const props = _.omit(inputProps, 'onChange', 'inputValue')
    return <FocusPlaceholderComponent {...props} />
  }

  render() {
    const isFocused = this.state.isFocused
    const isOpen = this.props.isOpen || this.state.isOpen
    const {
      inputValue,
      labelKey,
      placeholder,
      showInput,
      value,
      valueComponent,
      valueGroupRenderer,
      valueRenderer
    } = this.props
    const ValueGroupComponent = this.props.valueGroupComponent
    const selectClassName = classNames('crane-select', { open: isOpen, focus: isFocused })

    return (

 { this.container = container }}>

          <ValueGroupComponent
            inputValue={inputValue}
            labelKey={labelKey}
            placeholder={placeholder}
            showInput={showInput}
            value={value}
            valueRenderer={valueRenderer}
            valueComponent={valueComponent}
            valueGroupRenderer={valueGroupRenderer}
          />
          {this.renderInput(isOpen)}
          {this.renderClear()}
          {this.renderArrow()}

        {isOpen && this.renderMenu()}

    )
  }
}

export default SimpleSelect
<|endoftext|>