<commit_before>jest.mock('app/services/Firebase', () => {});
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import * as Analytics from 'app/utility/Analytics';
import * as SetsSelectors from 'app/redux/selectors/SetsSelectors';
import * as WorkoutSelectors from 'app/redux/selectors/WorkoutSelectors';
import * as sut from 'app/redux/shared_actions/WorkoutActionCreators';

const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);
var logEventSpy = null;
var store = null;

describe('endWorkout analytics', () => {

    beforeEach(() => {
        logEventSpy = jest.spyOn(Analytics, 'logEventWithAppState').mockImplementation(() => {});
        store = mockStore({
            sets: {
                workoutData: [{
                    exercise: 'derp',
                    reps: [],
                    tags: []
                }],
            },
            auth: {
                email: '',
            },
            workout: {
                removedCounter: '',
                restoredCounter: '',
            },
            history: {
                viewedCounter: 0
            },
            appState: {
                lockedCounter: 0,
            },
            connectedDevice: {
                numDisconnects: 0,
                numReconnects: 0,
            }
        });
    });

    afterEach(() => {
        logEventSpy.mockReset();
    });

    afterAll(() => {
        logEventSpy.mockReset();
        logEventSpy.mockRestore();
    });

    describe('num_reps', () => {
        var repsSpy = null;

        afterEach(() => {
            repsSpy.mockReset();
        });

        afterAll(() => {
            repsSpy.mockReset();
            repsSpy.mockRestore();
        });

        test('num_reps is 1', () => {
           repsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutReps').mockImplementation(() => 1);

           store.dispatch(sut.endWorkout());

           const event = logEventSpy.mock.calls[0][0];
           const params = logEventSpy.mock.calls[0][1];
           expect(event).toEqual('end_workout');
           expect(params.num_reps).toBe(1);
        });

        test('num_reps is 2', () => {
            repsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutReps').mockImplementation(() => 2);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_reps).toBe(2);
         });

        test('num_reps is 3', () => {
            repsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutReps').mockImplementation(() => 3);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_reps).toBe(3);
        });
    });

    describe('num_sets_with_fields', () => {
        var fieldsSpy = null;

        afterEach(() => {
            fieldsSpy.mockReset();
        });

        afterAll(() => {
            fieldsSpy.mockReset();
            fieldsSpy.mockRestore();
        })

        test('num_sets_with_fields is 1', () => {
            fieldsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutSetsWithFields').mockImplementation(() => 1);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(1);
        });

        test('num_sets_with_fields is 2', () => {
            fieldsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutSetsWithFields').mockImplementation(() => 2);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(2);
        });

        test('num_sets_with_fields is 3', () => {
            fieldsSpy = jest.spyOn(SetsSelectors, 'getNumWorkoutSetsWithFields').mockImplementation(() => 3);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(3);
        });
    });

    describe('manually_ended', () => {

        test('true', () => {
            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.manually_ended).toBeTruthy();
        });

        test('false', () => {
            store.dispatch(sut.autoEndWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.manually_ended).toBeFalsy();
        });
    });

    describe('percent_sets_with_fields', () => {
        var percentSpy = null;

        afterEach(() => {
            percentSpy.mockReset();
        });

        afterAll(() => {
            percentSpy.mockReset();
            percentSpy.restore();
        });

        test('25%', () => {
            percentSpy = jest.spyOn(SetsSelectors, 'getPercentWorkoutSetsWithFields').mockImplementation(() => 25);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(25);
        });

        test('50%', () => {
            percentSpy = jest.spyOn(SetsSelectors, 'getPercentWorkoutSetsWithFields').mockImplementation(() => 50);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(50);
        });

        test('75%', () => {
            percentSpy = jest.spyOn(SetsSelectors, 'getPercentWorkoutSetsWithFields').mockImplementation(() => 75);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(75);
        });

        test('100%', () => {
            percentSpy = jest.spyOn(SetsSelectors, 'getPercentWorkoutSetsWithFields').mockImplementation(() => 100);

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(100);
        });
    });
});
<commit_msg>Cleaned up spies in workoutactioncreators<commit_after>jest.mock('app/services/Firebase', () => {});
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import * as Analytics from 'app/utility/Analytics';
import * as SetsSelectors from 'app/redux/selectors/SetsSelectors';
import * as WorkoutSelectors from 'app/redux/selectors/WorkoutSelectors';
import * as sut from 'app/redux/shared_actions/WorkoutActionCreators';

const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);
var logEventSpy = null;
var store = null;

describe('endWorkout analytics', () => {

    beforeEach(() => {
        logEventSpy = jest.spyOn(Analytics, 'logEventWithAppState').mockImplementation(() => {});
        store = mockStore({
            sets: {
                workoutData: [{
                    exercise: 'derp',
                    reps: [],
                    tags: []
                }],
            },
            auth: {
                email: '',
            },
            workout: {
                removedCounter: '',
                restoredCounter: '',
            },
            history: {
                viewedCounter: 0
            },
            appState: {
                lockedCounter: 0,
            },
            connectedDevice: {
                numDisconnects: 0,
                numReconnects: 0,
            }
        });
    });

    afterEach(() => {
        logEventSpy.mockReset();
    });

    afterAll(() => {
        logEventSpy.mockRestore();
    });

    describe('num_reps', () => {
        const realGetNumWorkoutReps = SetsSelectors.getNumWorkoutReps;

        afterEach(() => {
            SetsSelectors.getNumWorkoutReps = realGetNumWorkoutReps;
        });

        test('num_reps is 1', () => {
            SetsSelectors.getNumWorkoutReps = () => 1;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_reps).toBe(1);
        });

        test('num_reps is 2', () => {
            SetsSelectors.getNumWorkoutReps = () => 2;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_reps).toBe(2);
         });

        test('num_reps is 3', () => {
            SetsSelectors.getNumWorkoutReps = () => 3;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_reps).toBe(3);
        });
    });

    describe('num_sets_with_fields', () => {
        const realGetNumWorkoutSetsWithFields = SetsSelectors.getNumWorkoutSetsWithFields;

        afterEach(() => {
            SetsSelectors.getNumWorkoutSetsWithFields = realGetNumWorkoutSetsWithFields;
        });

        test('num_sets_with_fields is 1', () => {
            SetsSelectors.getNumWorkoutSetsWithFields = () => 1;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(1);
        });

        test('num_sets_with_fields is 2', () => {
            SetsSelectors.getNumWorkoutSetsWithFields = () => 2;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(2);
        });

        test('num_sets_with_fields is 3', () => {
            SetsSelectors.getNumWorkoutSetsWithFields = () => 3;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.num_sets_with_fields).toBe(3);
        });
    });

    describe('manually_ended', () => {

        test('true', () => {
            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.manually_ended).toBeTruthy();
        });

        test('false', () => {
            store.dispatch(sut.autoEndWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.manually_ended).toBeFalsy();
        });
    });

    describe('percent_sets_with_fields', () => {
        const realGetPercentWorkoutSetsWithFields = SetsSelectors.getPercentWorkoutSetsWithFields;

        afterEach(() => {
            SetsSelectors.getPercentWorkoutSetsWithFields = realGetPercentWorkoutSetsWithFields;
        });

        test('25%', () => {
            SetsSelectors.getPercentWorkoutSetsWithFields = () => 25;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(25);
        });

        test('50%', () => {
            SetsSelectors.getPercentWorkoutSetsWithFields = () => 50;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(50);
        });

        test('75%', () => {
            SetsSelectors.getPercentWorkoutSetsWithFields = () => 75;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(75);
        });

        test('100%', () => {
            SetsSelectors.getPercentWorkoutSetsWithFields = () => 100;

            store.dispatch(sut.endWorkout());

            const event = logEventSpy.mock.calls[0][0];
            const params = logEventSpy.mock.calls[0][1];
            expect(event).toEqual('end_workout');
            expect(params.percent_sets_fields).toBe(100);
        });
    });
});
<|endoftext|>


<commit_before>/* eslint-disable no-console */
// <nowiki>
(function() {

    $('a').each(function(i, e) {
        try {
            var url = new URL(e.href);
            if (url.host.match(/\.(wikipedia|wiktionary|wikiquote|wikisource|wikinews|wikivoyage|wikibooks|wikiversity|wikimedia|mediawiki|wikidata)\.org$/)) {
                url.searchParams.set('dtenable', '1');
                e.href = url.href;
            }
        } catch (err) {
            console.log(e.href);
        }
    });

    var url = new URL(window.location.href);
    url.searchParams.set('dtenable', '1');
    mw.util.addPortletLink(
		'p-tb',
		url.href,
		'Enable DiscussionTools',
		't-dtenable'
    );

}
)();
// </nowiki>
<commit_msg>Disable on empty href and "#"<commit_after>/* eslint-disable no-console */
// <nowiki>
(function() {

    $('a').each(function(i, e) {
        var shorthref = e.getAttribute('href') || '';
        if (shorthref.match(/^(|#)$/)) {
            return;
        }
        try {
            var url = new URL(e.href);
            if (url.host.match(/\.(wikipedia|wiktionary|wikiquote|wikisource|wikinews|wikivoyage|wikibooks|wikiversity|wikimedia|mediawiki|wikidata)\.org$/)) {
                url.searchParams.set('dtenable', '1');
                e.href = url.href;
            }
        } catch (err) {
            console.error(e, err);
        }
    });

    var url = new URL(window.location.href);
    url.searchParams.set('dtenable', '1');
    mw.util.addPortletLink(
		'p-tb',
		url.href,
		'Enable DiscussionTools',
		't-dtenable'
    );

}
)();
// </nowiki>
<|endoftext|>


<commit_before>var app = angular.module('wows-stats', []);

app.controller('InstallCtrl', function ($scope, $http) {
	$scope.api = {};
	$scope.api.err = "Fetching settings...";
	$scope.api.region = [
		{
			name: "Russia",
			url: "http://api.worldofwarships.ru",
			devRoom: "http://ru.wargaming.net/developers/"
		},
		{
			name: "North America",
			url: "http://api.worldofwarships.com",
			devRoom: "http://na.wargaming.net/developers/"
		},
		{
			name: "Europe",
			url: "http://api.worldofwarships.eu",
			devRoom: "http://eu.wargaming.net/developers/"
		},
		{
			name: "Asia",
			url: "http://api.worldofwarships.asia",
			devRoom: "http://asia.wargaming.net/developers/"
		},
	];
	$scope.api.path = {};
	$scope.api.appId = {};
	$scope.api.save = {};

	$http({
		method:'GET',
		url: 'http://localhost:8080/api/env'
	}).success(function(data, status) {
		$scope.path = data.path;
		$scope.key = data.key;
		var regionFound = false;
		for (var i=0; i<$scope.api.region.length; i++) {
			if ($scope.api.region[i].url == data.url) {
				$scope.region = $scope.api.region[i];
				regionFound = true;
				break;
			}
		}
		if (!regionFound)
			$scope.region = $scope.api.region[1];
		$scope.developerRoom = $scope.region.devRoom;
		delete $scope.api.err;
	}).error(function(data, status) {
		$scope.api.err = "Fetch failed.";
	});

	$scope.onPathChange = function() {
		$scope.api.path = {};
		$scope.api.save = {};
	}

	$scope.onAppIdChange = function() {
		$scope.api.appId = {};
		$scope.api.save = {};
	}

	$scope.onRegionChange = function() {
		$scope.developerRoom = $scope.region.devRoom;
		$scope.api.appId = {};
		$scope.api.save = {};
	}

	$scope.api.validateAppId = function() {
		if ($scope.api.loading)
			return;
		$scope.api.loading = true;
		$http({
			method:'GET',
			url: $scope.region.url + '/wows/encyclopedia/info/?application_id=' + $scope.key
		}).success(function(data, status) {
			$scope.api.loading = false;
			if (data.status == "ok") {
				$scope.api.appId.result = "alert-success";
				$scope.api.appId.message = "This is a valid Application ID.";
			}
			else {
				$scope.api.appId.result = "alert alert-danger";
				$scope.api.appId.message = data.error.message;
			}
		}).error(function(data, status) {
			$scope.api.loading = false;
			$scope.api.appId.result = "alert-danger";
			$scope.api.appId.message = data;
		});
	}

	$scope.api.validatePath = function() {
		if ($scope.api.loading)
			return;
		$scope.api.loading = true;
		$http({
			method:'POST',
			url: 'http://localhost:8080/api/path',
			data: { path: $scope.path }
		}).success(function(data, status) {
			$scope.api.loading = false;
			$scope.api.path.result = "alert-success";
			$scope.api.path.message = "Found World_of_Warships.exe at this location.";
		}).error(function(data, status) {
			$scope.api.loading = false;
			$scope.api.path.result = "alert-danger";
			$scope.api.path.message = "World of Warships not found.";
		});
	}

	$scope.api.cancel = function() {
		if ($scope.api.loading)
			return;
		$http({
			method:'POST',
			url: 'http://localhost:8080/api/install',
			data: { action: "cancel" }
		});
		window.location.href="about:blank";
	}

	$scope.api.install = function() {
		if ($scope.api.loading)
			return;
		$http({
			method:'POST',
			url: 'http://localhost:8080/api/install',
			data: {
				action: "save",
				path: $scope.path,
				url: $scope.region.url,
				key: $scope.key
			}
		}).success(function(data, status) {
			$scope.api.save = {};
			window.location.href="about:blank";
		}).error(function(data, status) {
			$scope.api.save.result = "alert-danger";
			$scope.api.save.message = data;
		});
	}

	window.onbeforeunload = $scope.api.cancel;
});<commit_msg>added dynamic hostname to make it usable from tablets<commit_after>var app = angular.module('wows-stats', []);

app.controller('InstallCtrl', function ($scope, $http) {
	$scope.api = {};
	$scope.api.err = "Fetching settings...";
	$scope.api.region = [
		{
			name: "Russia",
			url: "http://api.worldofwarships.ru",
			devRoom: "http://ru.wargaming.net/developers/"
		},
		{
			name: "North America",
			url: "http://api.worldofwarships.com",
			devRoom: "http://na.wargaming.net/developers/"
		},
		{
			name: "Europe",
			url: "http://api.worldofwarships.eu",
			devRoom: "http://eu.wargaming.net/developers/"
		},
		{
			name: "Asia",
			url: "http://api.worldofwarships.asia",
			devRoom: "http://asia.wargaming.net/developers/"
		},
	];
	$scope.api.path = {};
	$scope.api.appId = {};
	$scope.api.save = {};

	$http({
		method:'GET',
		url: 'http://'+window.location.hostname+':'+window.location.port+':8080/api/env'
	}).success(function(data, status) {
		$scope.path = data.path;
		$scope.key = data.key;
		var regionFound = false;
		for (var i=0; i<$scope.api.region.length; i++) {
			if ($scope.api.region[i].url == data.url) {
				$scope.region = $scope.api.region[i];
				regionFound = true;
				break;
			}
		}
		if (!regionFound)
			$scope.region = $scope.api.region[1];
		$scope.developerRoom = $scope.region.devRoom;
		delete $scope.api.err;
	}).error(function(data, status) {
		$scope.api.err = "Fetch failed.";
	});

	$scope.onPathChange = function() {
		$scope.api.path = {};
		$scope.api.save = {};
	}

	$scope.onAppIdChange = function() {
		$scope.api.appId = {};
		$scope.api.save = {};
	}

	$scope.onRegionChange = function() {
		$scope.developerRoom = $scope.region.devRoom;
		$scope.api.appId = {};
		$scope.api.save = {};
	}

	$scope.api.validateAppId = function() {
		if ($scope.api.loading)
			return;
		$scope.api.loading = true;
		$http({
			method:'GET',
			url: $scope.region.url + '/wows/encyclopedia/info/?application_id=' + $scope.key
		}).success(function(data, status) {
			$scope.api.loading = false;
			if (data.status == "ok") {
				$scope.api.appId.result = "alert-success";
				$scope.api.appId.message = "This is a valid Application ID.";
			}
			else {
				$scope.api.appId.result = "alert alert-danger";
				$scope.api.appId.message = data.error.message;
			}
		}).error(function(data, status) {
			$scope.api.loading = false;
			$scope.api.appId.result = "alert-danger";
			$scope.api.appId.message = data;
		});
	}

	$scope.api.validatePath = function() {
		if ($scope.api.loading)
			return;
		$scope.api.loading = true;
		$http({
			method:'POST',
			url: 'http://'+window.location.hostname+':'+window.location.port+':8080/api/path',
			data: { path: $scope.path }
		}).success(function(data, status) {
			$scope.api.loading = false;
			$scope.api.path.result = "alert-success";
			$scope.api.path.message = "Found World_of_Warships.exe at this location.";
		}).error(function(data, status) {
			$scope.api.loading = false;
			$scope.api.path.result = "alert-danger";
			$scope.api.path.message = "World of Warships not found.";
		});
	}

	$scope.api.cancel = function() {
		if ($scope.api.loading)
			return;
		$http({
			method:'POST',
			url: 'http://'+window.location.hostname+':'+window.location.port+':8080/api/install',
			data: { action: "cancel" }
		});
		window.location.href="about:blank";
	}

	$scope.api.install = function() {
		if ($scope.api.loading)
			return;
		$http({
			method:'POST',
			url: 'http://'+window.location.hostname+':'+window.location.port+'/api/install',
			data: {
				action: "save",
				path: $scope.path,
				url: $scope.region.url,
				key: $scope.key
			}
		}).success(function(data, status) {
			$scope.api.save = {};
			window.location.href="about:blank";
		}).error(function(data, status) {
			$scope.api.save.result = "alert-danger";
			$scope.api.save.message = data;
		});
	}

	window.onbeforeunload = $scope.api.cancel;
});<|endoftext|>


<commit_before>(function(window) {
  var doc = window.document,
      imagelist = [],
      elemnames = ['previous', 'next', 'imginfo', 'imgarea'];

  var mk_gallery = function(imagelist, elements) {
    var that = {}, current = 0,
        last = imagelist.length - 1,
        currentimg;
    var images = imagelist.map(function(imgn) {
      return new_img(imgn);
    });

    that.init = function() {
      images.forEach(function(el) {
        hide(el);
        elements.imginfo.appendChild(el);
      });
    };

    that.previous = function() {
      var num = current;
      if (num === 0)
        num = last;
      else
        num--;
      that.load_img(num);
    };

    that.next = function() {
      var num = current;
      if (num === last)
        num = 0;
      else
        num++;
      that.load_img(num);
    };

    that.load_img = function(num) {
      var img = images[num];
      hide(currentimg);
      show(img);
      img.load_img();
      currentimg = img;
      current = num;
    };

    return that;
  };

  var new_img = function(src) {
    var link = doc.createElement('a'),
        img = doc.createElement('img');
    link.href = src;
    link.appendChild(img);
    link.load_img = function() {
      if (img.src !== src)
        img.src = src;
    };
    return link;
  };

  var hide = function(el) {
    if (el)
      el.style.display = 'none';
  };

  var show = function(el, param) {
    if (typeof param !== 'string')
      param = 'inline';
    if (el)
      el.style.display = param;
  };

  var is_ie = function() {
    return (navigator.appVersion.indexOf("MSIE") !== -1);
  };

  var load = function(fun) {
    if (/in/.test(document.readyState)) {
      if (is_ie())
        window.setTimeout(fun, 9);
      else
        window.setTimeout(fun, 9, false);
    } else {
      load(fun);
    }
  };

  load(function() {
    var elements = (function() {
      var obj = {};
      elemnames.forEach(function(eln) {
        obj[eln] = doc.getElementById(eln);
      });
      return obj;
    }());
    var imgl = window.imagelist || imagelist,
        gallery = mk_gallery(imgl, elements);
    gallery.init();
    gallery.load_img(0);
    window.instantgallery = gallery;
  });
}).call(null, window);<commit_msg>Oops, wrong element...<commit_after>(function(window) {
  var doc = window.document,
      imagelist = [],
      elemnames = ['previous', 'next', 'imginfo', 'imgarea'];

  var mk_gallery = function(imagelist, elements) {
    var that = {}, current = 0,
        last = imagelist.length - 1,
        currentimg;
    var images = imagelist.map(function(imgn) {
      return new_img(imgn);
    });

    that.init = function() {
      images.forEach(function(el) {
        hide(el);
        elements.imgarea.appendChild(el);
      });
    };

    that.previous = function() {
      var num = current;
      if (num === 0)
        num = last;
      else
        num--;
      that.load_img(num);
    };

    that.next = function() {
      var num = current;
      if (num === last)
        num = 0;
      else
        num++;
      that.load_img(num);
    };

    that.load_img = function(num) {
      var img = images[num];
      hide(currentimg);
      show(img);
      img.load_img();
      currentimg = img;
      current = num;
    };

    return that;
  };

  var new_img = function(src) {
    var link = doc.createElement('a'),
        img = doc.createElement('img');
    link.href = src;
    link.appendChild(img);
    link.load_img = function() {
      if (img.src !== src)
        img.src = src;
    };
    return link;
  };

  var hide = function(el) {
    if (el)
      el.style.display = 'none';
  };

  var show = function(el, param) {
    if (typeof param !== 'string')
      param = 'inline';
    if (el)
      el.style.display = param;
  };

  var is_ie = function() {
    return (navigator.appVersion.indexOf("MSIE") !== -1);
  };

  var load = function(fun) {
    if (/in/.test(document.readyState)) {
      if (is_ie())
        window.setTimeout(fun, 9);
      else
        window.setTimeout(fun, 9, false);
    } else {
      load(fun);
    }
  };

  load(function() {
    var elements = (function() {
      var obj = {};
      elemnames.forEach(function(eln) {
        obj[eln] = doc.getElementById(eln);
      });
      return obj;
    }());
    var imgl = window.imagelist || imagelist,
        gallery = mk_gallery(imgl, elements);
    gallery.init();
    gallery.load_img(0);
    window.instantgallery = gallery;
  });
}).call(null, window);<|endoftext|>


<commit_before>// see previous example for the things that are not commented

const assert = require('assert');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const Provider = require('oidc-provider');

assert(process.env.HEROKU_APP_NAME, 'process.env.HEROKU_APP_NAME missing');
assert(process.env.PORT, 'process.env.PORT missing');
assert(process.env.SECURE_KEY, 'process.env.SECURE_KEY missing, run `heroku addons:create securekey`');
assert.equal(process.env.SECURE_KEY.split(',').length, 2, 'process.env.SECURE_KEY format invalid');
assert(process.env.REDIS_URL, 'process.env.REDIS_URL missing, run `heroku-redis:hobby-dev`');

const RedisAdapter = require('./redis_adapter');
const jwks = require('./jwks.json');

// simple account model for this application, user list is defined like so
const Account = require('./account');

const oidc = new Provider(`https://${process.env.HEROKU_APP_NAME}.herokuapp.com`, {
  adapter: RedisAdapter,
  clients: [
    {
      client_id: 'foo',
      redirect_uris: ['https://example.com'],
      response_types: ['id_token'],
      grant_types: ['implicit'],
      token_endpoint_auth_method: 'none',
    },
  ],
  jwks,

  // oidc-provider only looks up the accounts by their ID when it has to read the claims,
  // passing it our Account model method is sufficient, it should return a Promise that resolves
  // with an object with accountId property and a claims method.
  findAccount: Account.findAccount,

  // let's tell oidc-provider you also support the email scope, which will contain email and
  // email_verified claims
  claims: {
    openid: ['sub'],
    email: ['email', 'email_verified'],
  },

  // let's tell oidc-provider where our own interactions will be
  // setting a nested route is just good practice so that users
  // don't run into weird issues with multiple interactions open
  // at a time.
  interactions: {
    url(ctx) {
      return `/interaction/${ctx.oidc.uid}`;
    },
  },
  features: {
    // disable the packaged interactions
    devInteractions: { enabled: false },

    introspection: { enabled: true },
    revocation: { enabled: true },
  },
});

oidc.proxy = true;
oidc.keys = process.env.SECURE_KEY.split(',');

// let's work with express here, below is just the interaction definition
const expressApp = express();
expressApp.set('trust proxy', true);
expressApp.set('view engine', 'ejs');
expressApp.set('views', path.resolve(__dirname, 'views'));

const parse = bodyParser.urlencoded({ extended: false });

function setNoCache(req, res, next) {
  res.set('Pragma', 'no-cache');
  res.set('Cache-Control', 'no-cache, no-store');
  next();
}

expressApp.get('/interaction/:uid', setNoCache, async (req, res, next) => {
  try {
    const details = await oidc.interactionDetails(req);
    console.log('see what else is available to you for interaction views', details);
    const { uid, prompt, params } = details;

    const client = await oidc.Client.find(params.client_id);

    if (prompt.name === 'login') {
      return res.render('login', {
        client,
        uid,
        details: prompt.details,
        params,
        title: 'Sign-in',
        flash: undefined,
      });
    }

    return res.render('interaction', {
      client,
      uid,
      details: prompt.details,
      params,
      title: 'Authorize',
    });
  } catch (err) {
    return next(err);
  }
});

expressApp.post('/interaction/:uid/login', setNoCache, parse, async (req, res, next) => {
  try {
    const { uid, prompt, params } = await oidc.interactionDetails(req);
    const client = await oidc.Client.find(params.client_id);

    const accountId = await Account.authenticate(req.body.email, req.body.password);

    if (!accountId) {
      res.render('login', {
        client,
        uid,
        details: prompt.details,
        params: {
          ...params,
          login_hint: req.body.email,
        },
        title: 'Sign-in',
        flash: 'Invalid email or password.',
      });
      return;
    }

    const result = {
      login: {
        account: accountId,
      },
    };

    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
  } catch (err) {
    next(err);
  }
});

expressApp.post('/interaction/:uid/confirm', setNoCache, parse, async (req, res, next) => {
  try {
    const result = {
      consent: {
        // rejectedScopes: [], // < uncomment and add rejections here
        // rejectedClaims: [], // < uncomment and add rejections here
      },
    };
    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
  } catch (err) {
    next(err);
  }
});

expressApp.get('/interaction/:uid/abort', setNoCache, async (req, res, next) => {
  try {
    const result = {
      error: 'access_denied',
      error_description: 'End-User aborted interaction',
    };
    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
  } catch (err) {
    next(err);
  }
});

// leave the rest of the requests to be handled by oidc-provider, there's a catch all 404 there
expressApp.use(oidc.callback);

// express listen
expressApp.listen(process.env.PORT);
<commit_msg>add res to interactionDetails call<commit_after>// see previous example for the things that are not commented

const assert = require('assert');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const Provider = require('oidc-provider');

assert(process.env.HEROKU_APP_NAME, 'process.env.HEROKU_APP_NAME missing');
assert(process.env.PORT, 'process.env.PORT missing');
assert(process.env.SECURE_KEY, 'process.env.SECURE_KEY missing, run `heroku addons:create securekey`');
assert.equal(process.env.SECURE_KEY.split(',').length, 2, 'process.env.SECURE_KEY format invalid');
assert(process.env.REDIS_URL, 'process.env.REDIS_URL missing, run `heroku-redis:hobby-dev`');

const RedisAdapter = require('./redis_adapter');
const jwks = require('./jwks.json');

// simple account model for this application, user list is defined like so
const Account = require('./account');

const oidc = new Provider(`https://${process.env.HEROKU_APP_NAME}.herokuapp.com`, {
  adapter: RedisAdapter,
  clients: [
    {
      client_id: 'foo',
      redirect_uris: ['https://example.com'],
      response_types: ['id_token'],
      grant_types: ['implicit'],
      token_endpoint_auth_method: 'none',
    },
  ],
  jwks,

  // oidc-provider only looks up the accounts by their ID when it has to read the claims,
  // passing it our Account model method is sufficient, it should return a Promise that resolves
  // with an object with accountId property and a claims method.
  findAccount: Account.findAccount,

  // let's tell oidc-provider you also support the email scope, which will contain email and
  // email_verified claims
  claims: {
    openid: ['sub'],
    email: ['email', 'email_verified'],
  },

  // let's tell oidc-provider where our own interactions will be
  // setting a nested route is just good practice so that users
  // don't run into weird issues with multiple interactions open
  // at a time.
  interactions: {
    url(ctx) {
      return `/interaction/${ctx.oidc.uid}`;
    },
  },
  features: {
    // disable the packaged interactions
    devInteractions: { enabled: false },

    introspection: { enabled: true },
    revocation: { enabled: true },
  },
});

oidc.proxy = true;
oidc.keys = process.env.SECURE_KEY.split(',');

// let's work with express here, below is just the interaction definition
const expressApp = express();
expressApp.set('trust proxy', true);
expressApp.set('view engine', 'ejs');
expressApp.set('views', path.resolve(__dirname, 'views'));

const parse = bodyParser.urlencoded({ extended: false });

function setNoCache(req, res, next) {
  res.set('Pragma', 'no-cache');
  res.set('Cache-Control', 'no-cache, no-store');
  next();
}

expressApp.get('/interaction/:uid', setNoCache, async (req, res, next) => {
  try {
    const details = await oidc.interactionDetails(req, res);
    console.log('see what else is available to you for interaction views', details);
    const { uid, prompt, params } = details;

    const client = await oidc.Client.find(params.client_id);

    if (prompt.name === 'login') {
      return res.render('login', {
        client,
        uid,
        details: prompt.details,
        params,
        title: 'Sign-in',
        flash: undefined,
      });
    }

    return res.render('interaction', {
      client,
      uid,
      details: prompt.details,
      params,
      title: 'Authorize',
    });
  } catch (err) {
    return next(err);
  }
});

expressApp.post('/interaction/:uid/login', setNoCache, parse, async (req, res, next) => {
  try {
    const { uid, prompt, params } = await oidc.interactionDetails(req, res);
    const client = await oidc.Client.find(params.client_id);

    const accountId = await Account.authenticate(req.body.email, req.body.password);

    if (!accountId) {
      res.render('login', {
        client,
        uid,
        details: prompt.details,
        params: {
          ...params,
          login_hint: req.body.email,
        },
        title: 'Sign-in',
        flash: 'Invalid email or password.',
      });
      return;
    }

    const result = {
      login: {
        account: accountId,
      },
    };

    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
  } catch (err) {
    next(err);
  }
});

expressApp.post('/interaction/:uid/confirm', setNoCache, parse, async (req, res, next) => {
  try {
    const result = {
      consent: {
        // rejectedScopes: [], // < uncomment and add rejections here
        // rejectedClaims: [], // < uncomment and add rejections here
      },
    };
    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
  } catch (err) {
    next(err);
  }
});

expressApp.get('/interaction/:uid/abort', setNoCache, async (req, res, next) => {
  try {
    const result = {
      error: 'access_denied',
      error_description: 'End-User aborted interaction',
    };
    await oidc.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
  } catch (err) {
    next(err);
  }
});

// leave the rest of the requests to be handled by oidc-provider, there's a catch all 404 there
expressApp.use(oidc.callback);

// express listen
expressApp.listen(process.env.PORT);
<|endoftext|>


<commit_before>/**
 * @description Container for app.
 */

import React from 'react';
import { connect } from 'react-redux';
import MusicItemList from '../components/music-item-list-component';

const mapStateToProps = (state, ownProps) => (
  {
    songs: [
      {
        id: 1,
        songName: 'もっと',
        albumName: 'もっと',
        artistName: 'aiko',
      },
      {
        id: 2,
        songName: 'カブトムシ',
        albumName: '桜の木の下',
        artistName: 'aiko',
      },
    ]
  }
);

const MusicList = connect(mapStateToProps)(MusicItemList);

export default MusicList;
<commit_msg>modify: fix to get data from store<commit_after>/**
 * @description Container for app.
 */

import React from 'react';
import { connect } from 'react-redux';
import MusicItemList from '../components/music-item-list-component';

const mapStateToProps = (state, ownProps) => ({ songs: state.musics });

const MusicList = connect(mapStateToProps)(MusicItemList);

export default MusicList;
<|endoftext|>


<commit_before>function debug(/**/) {
	var args = arguments;
	var msg = ""
	for(var i = 0; i < args.length; i++){
		if(typeof args[i] === "object") {
			msg += " " + JSON.stringify(args[i])
		} else {
			msg += " " + args[i]
		}
	}

	postData({call:"debug", args:[msg]})
	document.getElementById("debug").innerHTML += "<br>" + msg
}

// Helper function for generating pseudo callbacks and sending data to the QML part of the application
function postData(data, cb) {
	data._seed = Math.floor(Math.random() * 1000000)
	if(cb) {
		eth._callbacks[data._seed] = cb;
	}

	if(data.args === undefined) {
		data.args = [];
	}

	navigator.qt.postMessage(JSON.stringify(data));
}

navigator.qt.onmessage = function(ev) {
	var data = JSON.parse(ev.data)

	if(data._event !== undefined) {
		eth.trigger(data._event, data.data);
	} else {
		if(data._seed) {
			var cb = eth._callbacks[data._seed];
			if(cb) {
				// Figure out whether the returned data was an array
				// array means multiple return arguments (multiple params)
				if(data.data instanceof Array) {
					cb.apply(this, data.data)
				} else {
					cb.call(this, data.data)
				}

				// Remove the "trigger" callback
				delete eth._callbacks[ev._seed];
			}
		}
	}
}

window.onerror = function(message, file, lineNumber, column, errorObj) {
	debug(file, message, lineNumber+":"+column, errorObj);

	return false;
}
<commit_msg>Removed old debug method<commit_after>// Helper function for generating pseudo callbacks and sending data to the QML part of the application
function postData(data, cb) {
	data._seed = Math.floor(Math.random() * 1000000)
	if(cb) {
		eth._callbacks[data._seed] = cb;
	}

	if(data.args === undefined) {
		data.args = [];
	}

	navigator.qt.postMessage(JSON.stringify(data));
}

navigator.qt.onmessage = function(ev) {
	var data = JSON.parse(ev.data)

	if(data._event !== undefined) {
		eth.trigger(data._event, data.data);
	} else {
		if(data._seed) {
			var cb = eth._callbacks[data._seed];
			if(cb) {
				// Figure out whether the returned data was an array
				// array means multiple return arguments (multiple params)
				if(data.data instanceof Array) {
					cb.apply(this, data.data)
				} else {
					cb.call(this, data.data)
				}

				// Remove the "trigger" callback
				delete eth._callbacks[ev._seed];
			}
		}
	}
}
<|endoftext|>


<commit_before>/*jslint indent: 2, plusplus: true*/
"use strict";

var
  clock = require('./clock.js'),
  config = require('./config.js').streamer;

/*
 * Streamer will send stream directions to clients.
 *  sendHandler must dispatch message to all clients
 *  it will loop over fragments defined in config.
 */
exports.Streamer = function (media, chunkHandler, songEndedHandler) {

  // all clocks here are in milliseconds
  var
    chunkDuration = config.chunkDuration * 1000,
    sendAhead = config.sendAhead * 1000,
    checkInterval = config.checkInterval * 1000,
    overlapTime = config.overlapTime * 1000,
    chunkToSendPlayTime,
    currentChunkIndex,
    server,
    chunkURLs,
    timer;

  // scheduling technique from
  // http://chimera.labs.oreilly.com/books/1234000001552/ch02.html
  //
  function checkSchedule() {
    if (chunkToSendPlayTime < clock.clock() + sendAhead) {
      chunkHandler({
        url   : chunkURLs[currentChunkIndex],
        start : chunkToSendPlayTime
      });
      if (++currentChunkIndex === chunkURLs.length) {
        timer = setTimeout((chunkToSendPlayTime + chunkDuration) - clock.clock(), songEndedHandler);
      } else {
        chunkToSendPlayTime += chunkDuration - overlapTime;
        checkSchedule();
      }
    } else {
      timer = setTimeout(checkSchedule, checkInterval);
    }
  }

  // start song without sending multiple chunks at once
  this.play = function (playlistItem, done) {
    if (!playlistItem) {
      return;
    }
    media.getChunks(playlistItem.server, playlistItem.id, function (chunks, err) {
      if (err) {
        done(null, err);
      } else {
        server = playlistItem.server;
        chunkURLs = chunks;
        currentChunkIndex = 0;
        chunkToSendPlayTime = clock.clock() + sendAhead;
        checkSchedule();
      }
    });
  }

  function stop() {
    if (timer) {
      clearTimeout(timer);
    }
  }
};
<commit_msg>stop when play.<commit_after>/*jslint indent: 2, plusplus: true*/
"use strict";

var
  clock = require('./clock.js'),
  config = require('./config.js').streamer;

/*
 * Streamer will send stream directions to clients.
 *  sendHandler must dispatch message to all clients
 *  it will loop over fragments defined in config.
 */
exports.Streamer = function (media, chunkHandler, songEndedHandler) {

  // all clocks here are in milliseconds
  var
    chunkDuration = config.chunkDuration * 1000,
    sendAhead = config.sendAhead * 1000,
    checkInterval = config.checkInterval * 1000,
    overlapTime = config.overlapTime * 1000,
    chunkToSendPlayTime,
    currentChunkIndex,
    server,
    chunkURLs,
    timer;

  // scheduling technique from
  // http://chimera.labs.oreilly.com/books/1234000001552/ch02.html
  //
  function checkSchedule() {
    if (chunkToSendPlayTime < clock.clock() + sendAhead) {
      chunkHandler({
        url   : chunkURLs[currentChunkIndex],
        start : chunkToSendPlayTime
      });
      if (++currentChunkIndex === chunkURLs.length) {
        timer = setTimeout((chunkToSendPlayTime + chunkDuration) - clock.clock(), songEndedHandler);
      } else {
        chunkToSendPlayTime += chunkDuration - overlapTime;
        checkSchedule();
      }
    } else {
      timer = setTimeout(checkSchedule, checkInterval);
    }
  }

  // start song without sending multiple chunks at once
  this.play = function (playlistItem, done) {
    stop();
    if (!playlistItem) {
      return;
    }
    media.getChunks(playlistItem.server, playlistItem.id, function (chunks, err) {
      if (err) {
        done(null, err);
      } else {
        server = playlistItem.server;
        chunkURLs = chunks;
        currentChunkIndex = 0;
        chunkToSendPlayTime = clock.clock() + sendAhead;
        checkSchedule();
      }
    });
  }

  function stop() {
    if (timer) {
      clearTimeout(timer);
    }
  }
};
<|endoftext|>


<commit_before>// Implement LRU Cache in JavaScript

// LRU which is short for least recently used is a popular algorithm in cache.
// The basic idea is always put your items in order of used time, and when you insert new item into the fullfilled cache,
// remove the least recently used item in your memory.

// It should support the following operations: get and put.

// get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
// put(key, value) - Set or insert the value if the key is not already present.
//                   When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

// Follow up:
// Could you do both operations in O(1) time complexity?

// Example:

// LRUCache cache = new LRUCache( 2 /* capacity */ );

// cache.put(1, 1);
// cache.put(2, 2);
// cache.get(1);       // returns 1
// cache.put(3, 3);    // evicts key 2
// cache.get(2);       // returns -1 (not found)
// cache.put(4, 4);    // evicts key 1
// cache.get(1);       // returns -1 (not found)
// cache.get(3);       // returns 3
// cache.get(4);       // returns 4

// O(n)

// constructor
class LRUCache {
  constructor(capacity) {
    // save all key-value pairs in this hashtable
    this.cache = {};
    // save all keys in the stack with the order of last used time
    this.keys = [];
    this.capacity = capacity;
    this.size = 0;
  }

  updateKey(key) {
    // update the position of this key in keys
    const keyIndex = this.keys.indexOf(key);
    this.keys[keyIndex] = undefined;
    // update the key to the head of the stack
    this.keys.push(key);
  }

  get(key) {
    if (this.cache[key]) {
      this.updateKey(key);
      return this.cache[key];
    } else {
      return -1;
    }
  }

  set(key, value) {
    if (this.capacity <= 0) return "no memory to save 1 item";
    // update exist item
    if (this.cache[key]) {
      this.cache[key] = value;
      this.updateKey(key);
      return;
    }

    // if the cache is fullfilled, remove the least recently used item
    if (this.size >= this.capacity) {
      let removedKey = this.keys.shift();
      while (!removedKey) {
        // if the dKey is undefined, shift() again
        removedKey = this.keys.shift();
      }
      delete this.cache[removedKey];
    }

    // add new item and update the size
    this.cache[key] = value;
    this.keys.push(key);
    this.size += 1;
  }
}

// Logic

// O(1)

// If we reconstruct the LRUCache with a map and a bunch of nodes, we can implement the LRUCache with O(1).
// Since we can save the key and value in the node, and save all key-node pairs in the hashtable,
// and now we can get the value by key using hashtable,
// and we also have a list of nodes with order of last used time and the update on a linked list can be O(1).

/*
 * Illustration of the design:
 *
 *       entry             entry             entry             entry
 *       ______            ______            ______            ______
 *      | tail |.newer => |      |.newer => |      |.newer => | head |
 *      |  A   |          |  B   |          |  C   |          |  D   |
 *      |______| <= older.|______| <= older.|______| <= older.|______|
 *
 */

class Node {
  constructor(key, value) {
    this.k = key;
    this.val = value;
    this.newer = null;
    this.older = null;
  }
}

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.size = 0;
    this.map = {};
    // save the head and tail to can update easily
    this.head = null;
    this.tail = null;
  }

  get(key) {
    if (this.map[key]) {
      this.updateKey(key);
      return this.map[key].val;
    } else {
      return -1;
    }
  }

  updateKey(key) {
    const node = this.map[key];
    // break the chain and reconnect with newer and older
    node.newer ? node.newer.older = node.older : this.head = node.older;

    node.older ? node.older.newer = node.newer : this.tail = node.newer;

    // replace the node into head - newest
    node.older = this.head;
    node.newer = null;
    if (this.head) this.head.newer = node;
    this.head = node

    // if no items in the bucket, set the tail to node too.
    if (!this.tail) this.tail = node;
  }

  put(key, value) {
    const node = new Node(key,value);
    // update the value for exist entries
    if (this.map[key]) {
      this.map[key].val = value;
      this.updateKey(key);
      return;
    }
    if (this.size >= this.capacity) {
      // remove the least recently used item
      const deletedKey = this.tail.key;
      this.tail = this.tail.newer;
      if (this.tail) this.tail.older = null;
      delete this.map[deletedKey];
      this.size -= 1;
    }

    // insert node into the head
    node.older = this.head;
    // if have head, we need re-connect node with other nodes older than head
    if (this.head) this.head.newer = node;
    this.head = node;

    // if no tail which means first insert, set the tail to node too
    if(!this.tail) this.tail = node;
    this.map[key] = node;
    this.size += 1;
  }
}

// reference: http://taoalpha.github.io/blog/2015/11/02/tech-javascript-lru-cache/
<commit_msg>add lruCache logic<commit_after>// Implement LRU Cache in JavaScript

// LRU which is short for least recently used is a popular algorithm in cache.
// The basic idea is always put your items in order of used time, and when you insert new item into the fullfilled cache,
// remove the least recently used item in your memory.

// It should support the following operations: get and put.

// get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
// put(key, value) - Set or insert the value if the key is not already present.
//                   When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

// Follow up:
// Could you do both operations in O(1) time complexity?

// Example:

// LRUCache cache = new LRUCache( 2 /* capacity */ );

// cache.put(1, 1);
// cache.put(2, 2);
// cache.get(1);       // returns 1
// cache.put(3, 3);    // evicts key 2
// cache.get(2);       // returns -1 (not found)
// cache.put(4, 4);    // evicts key 1
// cache.get(1);       // returns -1 (not found)
// cache.get(3);       // returns 3
// cache.get(4);       // returns 4

// SOLUTIONS --------------------------------------------------------

// Hash / Stack of keys approach - Logic - O(n)

// LRUCache constructor
  // capacity - lets us know how many items we can have in our cache at a time
  // cache - hash used to keep track of key value pairs
  // keys - array that keeps track of existing keys in cache and keeps order
  // size - integer used to keep track of the amount of keys that have gone through our cache
  //        size property is made to let us know when capacity is met

// updateKey function
  // Everytime our LRUcache get() and put() functions are called we run updateKey
  // updateKey takes in a key parameter
  // with the key parameter we can find at what index our key is located in our keys property
  // using the index location, the current location of key is set to undefined in keys array
  // key is then pushed to the end of the keys array
  // being at the end of keys array means it was the last key used

// get function
  // get function takes a key paramter and is intended to return the value of our key IF EXISTS
  // if key exists in cache,
    // call updateKey function on key to declare it the most recent used
    // once key is updated, return value of key in cache
  // if key doesnt not exist
    // return -1

// put function
  // put function takes a key and value parameter and
    // is intended to create a new key and value pair in cache, as well as delete oldest pair
  // if key that's passed exists in cache
    // set the parameter value as the value of the key in cache
    // once value is stored, call updateKey function on key to declare it the most recent used
    // we can now return out of the function because we dont need to create a new key
  // if key that's passed DOES NOT exist in cache
    // check to if capacity has been met
      // if capacity has been met we can remove our oldest key by calling shift on keys array
      // (last used key always gets pushed to tend of array so oldest key is in the front)
        // if item returns undefined we keep shifting until we get a key
      // once key is returned we use it to delete key and value pair from cache
      // once key and value pair from cache are deleted we set new pair in cache
      // push the newest key into key array
      // increase size since key is new

class LRUCacheOne {
  constructor(capacity) {
    // save all key-value pairs in this hashtable
    this.cache = {};
    // save all keys in the stack with the order of last used time
    this.keys = [];
    this.capacity = capacity;
    this.size = 0;
  }

  updateKey(key) {
    // update the position of this key in keys
    const keyIndex = this.keys.indexOf(key);
    this.keys[keyIndex] = undefined;
    // update the key to the head of the stack
    this.keys.push(key);
  }

  get(key) {
    if (this.cache[key]) {
      this.updateKey(key);
      return this.cache[key];
    } else {
      return -1;
    }
  }

  put(key, value) {
    // update exist item
    if (this.cache[key]) {
      this.cache[key] = value;
      this.updateKey(key);
      return;
    }

    // if the cache is fullfilled, remove the least recently used item
    if (this.size >= this.capacity) {
      let removedKey = this.keys.shift();
      while (!removedKey) {
        // if the dKey is undefined, shift() again
        // ex. [ undefined, undefined, undefined, 4, 3 ]
        removedKey = this.keys.shift();
      }
      delete this.cache[removedKey];
    }
    // add new item and update the size
    this.cache[key] = value;
    this.keys.push(key);
    this.size += 1;
  }
}

const cache = new LRUCacheOne( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
// cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
// cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
// cache.put(4, 4);
// cache.put(4, 4);
cache.get(4);
cache.put(4, 4);
cache.put(4, 4);
cache.put(4, 4);
// cache.get(1);       // returns -1 (not found)
// cache.get(3);       // returns 3
// cache.get(4);       // returns 4

console.log(cache);

// Logic

// O(1)

// If we reconstruct the LRUCache with a map and a bunch of nodes, we can implement the LRUCache with O(1).
// Since we can save the key and value in the node, and save all key-node pairs in the hashtable,
// and now we can get the value by key using hashtable,
// and we also have a list of nodes with order of last used time and the update on a linked list can be O(1).

/*
 * Illustration of the design:
 *
 *       entry             entry             entry             entry
 *       ______            ______            ______            ______
 *      | tail |.newer => |      |.newer => |      |.newer => | head |
 *      |  A   |          |  B   |          |  C   |          |  D   |
 *      |______| <= older.|______| <= older.|______| <= older.|______|
 *
 */

class Node {
  constructor(key, value) {
    this.k = key;
    this.val = value;
    this.newer = null;
    this.older = null;
  }
}

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.size = 0;
    this.map = {};
    // save the head and tail to can update easily
    this.head = null;
    this.tail = null;
  }

  get(key) {
    if (this.map[key]) {
      this.updateKey(key);
      return this.map[key].val;
    } else {
      return -1;
    }
  }

  updateKey(key) {
    const node = this.map[key];
    // break the chain and reconnect with newer and older
    node.newer ? node.newer.older = node.older : this.head = node.older;

    node.older ? node.older.newer = node.newer : this.tail = node.newer;

    // replace the node into head - newest
    node.older = this.head;
    node.newer = null;
    if (this.head) this.head.newer = node;
    this.head = node

    // if no items in the bucket, set the tail to node too.
    if (!this.tail) this.tail = node;
  }

  put(key, value) {
    const node = new Node(key,value);
    // update the value for exist entries
    if (this.map[key]) {
      this.map[key].val = value;
      this.updateKey(key);
      return;
    }
    if (this.size >= this.capacity) {
      // remove the least recently used item
      const deletedKey = this.tail.key;
      this.tail = this.tail.newer;
      if (this.tail) this.tail.older = null;
      delete this.map[deletedKey];
      this.size -= 1;
    }

    // insert node into the head
    node.older = this.head;
    // if have head, we need re-connect node with other nodes older than head
    if (this.head) this.head.newer = node;
    this.head = node;

    // if no tail which means first insert, set the tail to node too
    if(!this.tail) this.tail = node;
    this.map[key] = node;
    this.size += 1;
  }
}

// reference: http://taoalpha.github.io/blog/2015/11/02/tech-javascript-lru-cache/
<|endoftext|>


<commit_before>import Component from './component';
import { offset, outerWidth, outerHeight, append } from 'dolla';

/*
    Requirements
    ---
    content:        html|node
    anchor:         node

    Options
    ---
    align:          [left|right|center|#px] [top|center|bottom|#px] | default: 'center bottom'
    zIndex:         # | default: unset
    offset:         {left: 0, top: 0}
    container:      element to append popover to. default: document
*/
export default class Popover extends Component {
    initialize (options) {
        this.showing = false;
        options = options || {};
        this.options = {
            zIndex: 3,
            container: document.body,
            align: 'center bottom',
            anchor: document.body,
            content: 'needs content',
            offset: {left: 0, top: 0}
        };
        Object.assign(this.options, this.pick(options, Object.keys(this.options)));
        this.el.removeAttribute('content');

        this.options.anchor.popover = this
        this.listenTo(document, 'click', this.checkFocus);
        this.listenTo(document, 'focusin', this.checkFocus);
        this.listenTo(document, 'keyup', this.checkEscape);
        this.listenTo(window, 'resize', () => {
          this.resize.bind(this)()
        });

        if(typeof this.options.container == "string"){
          this.options.container = this.options.anchor.closest(this.options.container)
        }
        this.options.container = this.options.container || document.body

    }

    render () {
        this.showing = true;
        this.el.style.position = 'absolute';
        this.el.style.zIndex = this.options.zIndex;

        append(this.el, this.options.content)

        this.options.container.appendChild(this.el);
        this.resize();
        this.trigger('shown');

        return this;
    }

    resize () {
        this.setPosition();
        let bounds = this.el.getBoundingClientRect();
        const body_bounds = document.body.getBoundingClientRect();
        const window_bounds = {
          top: 0,
          bottom: window.innerHeight,
          left: 0,
          right: window.innerWidth
        };

        var reposition = false;
        if (bounds.bottom > Math.max(body_bounds.bottom, window_bounds.bottom)) {
          var [leftAlign, topAlign] = this.options.align.split(" ");
          this.setPosition(`${leftAlign} top`);
          bounds = this.el.getBoundingClientRect()
          if(bounds.top < 0) {
            this.setPosition(`${leftAlign} bottom`);
          }
          bounds = this.el.getBoundingClientRect()
        }
        if (bounds.top < body_bounds.top) {
            const difference = body_bounds.top - bounds.top
            if(this.el.style.top != null) this.el.style.top = parseInt(this.el.style.top) + difference + "px"
            if(this.el.style.bottom != null) this.el.style.bottom = parseInt(this.el.style.bottom) - difference + "px"
        }
        if (bounds.left < body_bounds.left) {
            const difference = body_bounds.left - bounds.left
            if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
            if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
            bounds = this.el.getBoundingClientRect()
        }
        if (bounds.right > body_bounds.right) {
            const difference = body_bounds.right - bounds.right
            if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
            if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
        }
    }

    setPosition(align){
        align = align || this.options.align;
        var [leftAlign, topAlign] = align.split(" ");
        leftAlign = leftAlign || "bottom";

        var anchorOffset = offset(this.options.anchor);
        var container = this.options.container;
        if(getComputedStyle(container)['position'] == "static") container = container.offsetParent;
        if(!container) container = document.body;

        var containerOffset = offset(container);
        anchorOffset = {
            top: anchorOffset.top - containerOffset.top,
            left: anchorOffset.left - containerOffset.left
        }

        var position = {};
        if(leftAlign == 'left'){
            position.right = outerWidth(container) - anchorOffset.left;
        } else if(leftAlign == 'center'){
            position.left = anchorOffset.left + outerWidth(this.options.anchor) / 2 - outerWidth(this.el) / 2;
            position.transform = "translateX(-50%)";
        } else if (leftAlign == 'right'){
            position.left = anchorOffset.left + outerWidth(this.options.anchor);
        } else if (leftAlign.includes("px")){
            position.left = anchorOffset.left + parseInt(leftAlign);
        }

        if(topAlign == 'top'){
            let height = outerHeight(container);
            if(container == document.body && getComputedStyle(container)['position'] == "static"){
              height = window.innerHeight;
            } else if (container == document.body) {
              height = Math.max(height, document.body.clientHeight);
            }
            position.bottom = height - anchorOffset.top;
        } else if(topAlign == 'center'){
            position.top = anchorOffset.top + outerHeight(this.options.anchor) / 2;
            position.transform = "translateY(-50%)";
        } else if (topAlign == 'bottom'){
            position.top = anchorOffset.top + outerHeight(this.options.anchor);
        } else if (topAlign.includes("px")){
            position.top = anchorOffset.top + parseInt(topAlign);
        }

        if(this.options.offset.left) position.left += parseInt(this.options.offset.left);
        if(this.options.offset.left) position.right -= parseInt(this.options.offset.left);
        if(this.options.offset.top) position.top += parseInt(this.options.offset.top);
        if(this.options.offset.top) position.bottom -= parseInt(this.options.offset.top);

        this.el.style.left = null;
        this.el.style.right = null;
        this.el.style.top = null;
        this.el.style.bottom = null;
        this.el.style.transform = null;
        this.el.classList.remove('popover-left', 'popover-right', 'popover-center', 'popover-top', 'popover-bottom');
        this.el.classList.add('popover-' + topAlign);
        this.el.classList.add('popover-' + leftAlign);
        Object.keys(position).forEach(function(key){
            this.el.style[key] = position[key] + (key != "transform" ? "px" : "");
        }, this);
    }

    checkFocus (e) {
        if (e.defaultPrevented)             return;
        if (!this.showing)                return;
        if (e.target === this.el)           return;
        if (e.target == this.options.anchor)        return;
        if (this.el.contains(e.target))     return;
        if (this.options.anchor.contains(e.target)) return;
        if (this.persisting) return;
        this.hide();
    }

    checkEscape (e) {
        if(e.which != 27) return;
        if (this.persisting) return;
        this.hide();
    }

    isHidden() {
        return !this.showing;
    }

    hide (options) {
        options = options || {};
        if(!this.showing) return;
        this.el.style.display = 'none';
        this.showing = false;
        if(options.silent !== true) {
          this.trigger('hidden');
        }
    }

    show () {
        if(this.showing) return;
        this.el.style.display = 'block';
        this.showing = true;
        this.trigger('shown');
    }

    toggle(flag) {
        flag = flag || this.showing;
        if(flag) this.hide(); else this.show();
    }

    persist() {
      this.persisting = true;
    }

    unpersist() {
      this.persisting = false;
    }
}<commit_msg>fix how popover renders center<commit_after>import Component from './component';
import { offset, outerWidth, outerHeight, append } from 'dolla';

/*
    Requirements
    ---
    content:        html|node
    anchor:         node

    Options
    ---
    align:          [left|right|center|#px] [top|center|bottom|#px] | default: 'center bottom'
    zIndex:         # | default: unset
    offset:         {left: 0, top: 0}
    container:      element to append popover to. default: document
*/
export default class Popover extends Component {
    initialize (options) {
        this.showing = false;
        options = options || {};
        this.options = {
            zIndex: 3,
            container: document.body,
            align: 'center bottom',
            anchor: document.body,
            content: 'needs content',
            offset: {left: 0, top: 0}
        };
        Object.assign(this.options, this.pick(options, Object.keys(this.options)));
        this.el.removeAttribute('content');

        this.options.anchor.popover = this
        this.listenTo(document, 'click', this.checkFocus);
        this.listenTo(document, 'focusin', this.checkFocus);
        this.listenTo(document, 'keyup', this.checkEscape);
        this.listenTo(window, 'resize', () => {
          this.resize.bind(this)()
        });

        if(typeof this.options.container == "string"){
          this.options.container = this.options.anchor.closest(this.options.container)
        }
        this.options.container = this.options.container || document.body

    }

    render () {
        this.showing = true;
        this.el.style.position = 'absolute';
        this.el.style.zIndex = this.options.zIndex;

        append(this.el, this.options.content)

        this.options.container.appendChild(this.el);
        this.resize();
        this.trigger('shown');

        return this;
    }

    resize () {
        this.setPosition();
        let bounds = this.el.getBoundingClientRect();
        const body_bounds = document.body.getBoundingClientRect();
        const window_bounds = {
          top: 0,
          bottom: window.innerHeight,
          left: 0,
          right: window.innerWidth
        };

        var reposition = false;
        if (bounds.bottom > Math.max(body_bounds.bottom, window_bounds.bottom)) {
          var [leftAlign, topAlign] = this.options.align.split(" ");
          this.setPosition(`${leftAlign} top`);
          bounds = this.el.getBoundingClientRect()
          if(bounds.top < 0) {
            this.setPosition(`${leftAlign} bottom`);
          }
          bounds = this.el.getBoundingClientRect()
        }
        if (bounds.top < body_bounds.top) {
            const difference = body_bounds.top - bounds.top
            if(this.el.style.top != null) this.el.style.top = parseInt(this.el.style.top) + difference + "px"
            if(this.el.style.bottom != null) this.el.style.bottom = parseInt(this.el.style.bottom) - difference + "px"
        }
        if (bounds.left < body_bounds.left) {
            const difference = body_bounds.left - bounds.left
            if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
            if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
            bounds = this.el.getBoundingClientRect()
        }
        if (bounds.right > body_bounds.right) {
            const difference = body_bounds.right - bounds.right
            if(this.el.style.left != null) this.el.style.left = parseInt(this.el.style.left) + difference + "px"
            if(this.el.style.right != null) this.el.style.right = parseInt(this.el.style.right) - difference + "px"
        }
    }

    setPosition(align){
        align = align || this.options.align;
        var [leftAlign, topAlign] = align.split(" ");
        leftAlign = leftAlign || "bottom";

        var anchorOffset = offset(this.options.anchor);
        var container = this.options.container;
        if(getComputedStyle(container)['position'] == "static") container = container.offsetParent;
        if(!container) container = document.body;

        var containerOffset = offset(container);
        anchorOffset = {
            top: anchorOffset.top - containerOffset.top,
            left: anchorOffset.left - containerOffset.left
        }

        var position = {};
        if(leftAlign == 'left'){
            position.right = outerWidth(container) - anchorOffset.left;
        } else if(leftAlign == 'center'){
            position.left = anchorOffset.left + outerWidth(this.options.anchor) / 2;
            position.transform = "translateX(-50%)";
        } else if (leftAlign == 'right'){
            position.left = anchorOffset.left + outerWidth(this.options.anchor);
        } else if (leftAlign.includes("px")){
            position.left = anchorOffset.left + parseInt(leftAlign);
        }

        if(topAlign == 'top'){
            let height = outerHeight(container);
            if(container == document.body && getComputedStyle(container)['position'] == "static"){
              height = window.innerHeight;
            } else if (container == document.body) {
              height = Math.max(height, document.body.clientHeight);
            }
            position.bottom = height - anchorOffset.top;
        } else if(topAlign == 'center'){
            position.top = anchorOffset.top + outerHeight(this.options.anchor) / 2;
            position.transform = "translateY(-50%)";
        } else if (topAlign == 'bottom'){
            position.top = anchorOffset.top + outerHeight(this.options.anchor);
        } else if (topAlign.includes("px")){
            position.top = anchorOffset.top + parseInt(topAlign);
        }

        if(this.options.offset.left) position.left += parseInt(this.options.offset.left);
        if(this.options.offset.left) position.right -= parseInt(this.options.offset.left);
        if(this.options.offset.top) position.top += parseInt(this.options.offset.top);
        if(this.options.offset.top) position.bottom -= parseInt(this.options.offset.top);

        this.el.style.left = null;
        this.el.style.right = null;
        this.el.style.top = null;
        this.el.style.bottom = null;
        this.el.style.transform = null;
        this.el.classList.remove('popover-left', 'popover-right', 'popover-center', 'popover-top', 'popover-bottom');
        this.el.classList.add('popover-' + topAlign);
        this.el.classList.add('popover-' + leftAlign);
        Object.keys(position).forEach(function(key){
            this.el.style[key] = position[key] + (key != "transform" ? "px" : "");
        }, this);
    }

    checkFocus (e) {
        if (e.defaultPrevented)             return;
        if (!this.showing)                return;
        if (e.target === this.el)           return;
        if (e.target == this.options.anchor)        return;
        if (this.el.contains(e.target))     return;
        if (this.options.anchor.contains(e.target)) return;
        if (this.persisting) return;
        this.hide();
    }

    checkEscape (e) {
        if(e.which != 27) return;
        if (this.persisting) return;
        this.hide();
    }

    isHidden() {
        return !this.showing;
    }

    hide (options) {
        options = options || {};
        if(!this.showing) return;
        this.el.style.display = 'none';
        this.showing = false;
        if(options.silent !== true) {
          this.trigger('hidden');
        }
    }

    show () {
        if(this.showing) return;
        this.el.style.display = 'block';
        this.showing = true;
        this.trigger('shown');
    }

    toggle(flag) {
        flag = flag || this.showing;
        if(flag) this.hide(); else this.show();
    }

    persist() {
      this.persisting = true;
    }

    unpersist() {
      this.persisting = false;
    }
}<|endoftext|>


<commit_before>/*
This file is part of Archivematica.

Copyright 2010-2012 Artefactual Systems Inc. <http://artefactual.com>

Archivematica is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Archivematica is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Archivematica.  If not, see <http://www.gnu.org/licenses/>.
*/

$(function()
  {
    window.Sip = Sip.extend({
      methodUrl: {
        delete: '/transfer/uuid/delete/'
      }
    });

    window.SipCollection = Backbone.Collection.extend({

      model: Sip,

      url: '/transfer/status/',

      initialize: function()
        {

        },

      comparator: function(sip)
        {
          return -1 * sip.get('timestamp');
        }

    });

    window.SipView = BaseSipView.extend({

      template: _.template($('#sip-template').html()),

      initialize: function()
        {
          _.bindAll(this, 'render', 'update', 'updateIcon');
          this.model.view = this;
          this.model.bind('change:timestamp', this.update);
        },

      openPanel: function(event)
        {
          event.preventDefault();

          window.location = '/transfer/' + this.model.get('uuid') + '/';
        },

      remove: function(event)
        {
          event.preventDefault();
          event.stopPropagation();

          $(this.el).addClass('sip-removing');

          var self = this;

          $('
' +
              '
Are you sure you want to remove this transfer from the dashboard? Note that this does not delete the transfer or related entities.
' +
              '
Directory: ' + this.model.get('directory') + '
UUID: ' + this.model.get('uuid') + '
Status: ' + $(this.el).find('.sip-detail-icon-status > img').attr('title') + '
' +
            '
').dialog(
            {
              modal: true,
              resizable: false,
              draggable: false,
              title: 'Remove SIP',
              width: 480,
              close: function(event, ui)
                {
                  if (event.which !== undefined)
                  {
                    $(self.el).removeClass('sip-removing');
                  }
                },
              buttons: [
                  {
                    text: 'Confirm',
                    click: function() {

                      var $dialog = $(this);

                      self.model.destroy({
                        success: function (model, response)
                          {
                            $dialog.dialog('close');

                            setTimeout(function()
                              {
                                $(self.el).hide('blind', function()
                                  {
                                    $(this).remove();
                                  });
                              }, 250);
                          },
                        error: function(model, response)
                          {
                            $dialog.dialog('close');
                            $(self.el).removeClass('sip-removing');
                          }

                      });
                    }
                  },
                  {
                    text: 'Cancel',
                    click: function() {
                        $(this).dialog('close');
                        $(self.el).removeClass('sip-removing');
                      }
                  }]
            });
        }
    });

    window.JobView = BaseJobView.extend({

      className: 'job',

      events: {
        'click .btn_browse_job': 'browseJob',
        'click .btn_approve_job': 'approveJob',
        'click .btn_reject_job': 'rejectJob',
        'click .btn_show_tasks': 'showTasks',
        'change select': 'action'
      },

      template: _.template($('#job-template').html()),

      render: function()
        {
          $(this.el).html(this.template(this.model.toJSON()));

          $(this.el).css(
            'background-color',
            this.getStatusColor(this.model.get('currentstep'))
          );

          // Micro-services requiring approval
          if (1 === this.model.get('status'))
          {
            this.$('.job-detail-actions')
              .append('Browse')
              .append('Approve')
              .append('Reject');
          }
          else
          {
            // ...
          }

          choices = this.model.get('choices');

         if (choices)
          {
            var $select = $('<select />').append('<option>Actions</option>')
              , numberOfChoices = Object.keys(choices).length;

            // use pop-up action selector for long choice lists
            if (numberOfChoices >= 10)
            {
              var statusObject = {};
              this.activateEnhancedActionSelect($select, statusObject);
            }

            for (var code in choices)
            {
              $select.append('<option value="' + code + '">- ' + choices[code] + '</option>');
            }

            this.$('.job-detail-actions').append($select);
          }

          this.$('.job-detail-microservice > a').tooltip();

          this.$('.job-detail-actions > a').twipsy();

          return this;
        },

      action: function(event)
        {
          var value = $(event.target).val();

          $.ajax({
            context: this,
            data: { uuid: this.model.get('uuid'), choice: value },
            type: 'POST',
            success: function(data)
              {
                this.model.set({
                  'currentstep': 'Executing command(s)',
                  'status': 0
                });

                this.model.sip.view.updateIcon();
              },
            url: '/mcp/execute/'
          });
        }

    });

    window.DirectoryBrowserView = BaseDirectoryBrowserView.extend({
      template: _.template($('#directory-browser-template').html())
    });

    window.AppView = BaseAppView.extend({
      el: $('#sip-container')
    });

    $.fn.tooltip = tooltipPlugin;
    window.onresize = optimizeWidth;
    window.onload = optimizeWidth;
  }
);
<commit_msg>Added logic to get rid of job action select element after selection.<commit_after>/*
This file is part of Archivematica.

Copyright 2010-2012 Artefactual Systems Inc. <http://artefactual.com>

Archivematica is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Archivematica is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Archivematica.  If not, see <http://www.gnu.org/licenses/>.
*/

$(function()
  {
    window.Sip = Sip.extend({
      methodUrl: {
        delete: '/transfer/uuid/delete/'
      }
    });

    window.SipCollection = Backbone.Collection.extend({

      model: Sip,

      url: '/transfer/status/',

      initialize: function()
        {

        },

      comparator: function(sip)
        {
          return -1 * sip.get('timestamp');
        }

    });

    window.SipView = BaseSipView.extend({

      template: _.template($('#sip-template').html()),

      initialize: function()
        {
          _.bindAll(this, 'render', 'update', 'updateIcon');
          this.model.view = this;
          this.model.bind('change:timestamp', this.update);
        },

      openPanel: function(event)
        {
          event.preventDefault();

          window.location = '/transfer/' + this.model.get('uuid') + '/';
        },

      remove: function(event)
        {
          event.preventDefault();
          event.stopPropagation();

          $(this.el).addClass('sip-removing');

          var self = this;

          $('
' +
              '
Are you sure you want to remove this transfer from the dashboard? Note that this does not delete the transfer or related entities.
' +
              '
Directory: ' + this.model.get('directory') + '
UUID: ' + this.model.get('uuid') + '
Status: ' + $(this.el).find('.sip-detail-icon-status > img').attr('title') + '
' +
            '
').dialog(
            {
              modal: true,
              resizable: false,
              draggable: false,
              title: 'Remove SIP',
              width: 480,
              close: function(event, ui)
                {
                  if (event.which !== undefined)
                  {
                    $(self.el).removeClass('sip-removing');
                  }
                },
              buttons: [
                  {
                    text: 'Confirm',
                    click: function() {

                      var $dialog = $(this);

                      self.model.destroy({
                        success: function (model, response)
                          {
                            $dialog.dialog('close');

                            setTimeout(function()
                              {
                                $(self.el).hide('blind', function()
                                  {
                                    $(this).remove();
                                  });
                              }, 250);
                          },
                        error: function(model, response)
                          {
                            $dialog.dialog('close');
                            $(self.el).removeClass('sip-removing');
                          }

                      });
                    }
                  },
                  {
                    text: 'Cancel',
                    click: function() {
                        $(this).dialog('close');
                        $(self.el).removeClass('sip-removing');
                      }
                  }]
            });
        }
    });

    window.JobView = BaseJobView.extend({

      className: 'job',

      events: {
        'click .btn_browse_job': 'browseJob',
        'click .btn_approve_job': 'approveJob',
        'click .btn_reject_job': 'rejectJob',
        'click .btn_show_tasks': 'showTasks',
        'change select': 'action'
      },

      template: _.template($('#job-template').html()),

      render: function()
        {
          $(this.el).html(this.template(this.model.toJSON()));

          $(this.el).css(
            'background-color',
            this.getStatusColor(this.model.get('currentstep'))
          );

          // Micro-services requiring approval
          if (1 === this.model.get('status'))
          {
            this.$('.job-detail-actions')
              .append('Browse')
              .append('Approve')
              .append('Reject');
          }
          else
          {
            // ...
          }

          choices = this.model.get('choices');

         if (choices)
          {
            var $select = $('<select />').append('<option>Actions</option>')
              , numberOfChoices = Object.keys(choices).length;

            // use pop-up action selector for long choice lists
            if (numberOfChoices >= 10)
            {
              var statusObject = {};
              this.activateEnhancedActionSelect($select, statusObject);
            }

            for (var code in choices)
            {
              $select.append('<option value="' + code + '">- ' + choices[code] + '</option>');
            }

            this.$('.job-detail-actions').append($select);
          }

          this.$('.job-detail-microservice > a').tooltip();

          this.$('.job-detail-actions > a').twipsy();

          return this;
        },

      action: function(event)
        {
          var value = $(event.target).val()
            , self = this;

          $.ajax({
            context: this,
            data: { uuid: this.model.get('uuid'), choice: value },
            type: 'POST',
            success: function(data)
              {
                this.model.set({
                  'currentstep': 'Executing command(s)',
                  'status': 0
                });

                this.model.sip.view.updateIcon();

                // get rid of select, etc.
                self.$('.job-detail-actions').html('');
              },
            url: '/mcp/execute/'
          });
        }

    });

    window.DirectoryBrowserView = BaseDirectoryBrowserView.extend({
      template: _.template($('#directory-browser-template').html())
    });

    window.AppView = BaseAppView.extend({
      el: $('#sip-container')
    });

    $.fn.tooltip = tooltipPlugin;
    window.onresize = optimizeWidth;
    window.onload = optimizeWidth;
  }
);
<|endoftext|>