<commit_before>/* global describe, spyOn, inject, beforeEach, afterEach, it, expect, jasmine, module */

import angular from 'angular';
import ngModule from '../resource';
import Tester from 'tester';

describe('AvApiResourceProvider', () => {

  Tester.matchers();

  describe('AvApiResource', () => {

    let $httpBackend;
    let $q;
    let AvApiResource;
    let callback;

    const responseData = [{a: 1, b: 2}, {a: 1, b: 2}];

    beforeEach( () => {

      callback = jasmine.createSpy('done');

      angular.mock.module(ngModule.name);

      angular.mock.module( $provide => {

        const pollingService = {
          response(response) {
            return response || $q.when(response);
          }
        };

        $provide.value('avPollingService', pollingService);

      });

      inject( (_$httpBackend_, _$q_, _AvApiResource_) => {
        $httpBackend = _$httpBackend_;
        $q = _$q_;
        AvApiResource = _AvApiResource_;
      });

    });

    afterEach(function() {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });

    it('should not allow options to be null', () => {
      expect( () => { AvApiResource.create() }).toThrow(new Error('[options] cannot be null or undefined'));
    });

    describe('urls', () => {

      it('should build url with resource name', () => {

        const users = new AvApiResource({name: 'users'});

        expect(users).toBeObject();
        expect(users.getUrl()).toBe('/api/v1/users');

      });

      it('should build url with resource name and forward slash', () => {

        const users = new AvApiResource({name: '/users'});

        expect(users.getUrl()).toBe('/api/v1/users');

      });

      it('should build url with resource name and path', () => {

        const users = new AvApiResource({
          path: '/internal/api',
          name: '/users'
        });

        expect(users.getUrl()).toBe('/internal/api/v1/users');

      });

      it('should build url with with resource name, version and path', () => {

        const users = new AvApiResource({
          version: '/v2',
          name: '/bar',
          path: '/api/foo'
        });

        expect(users.getUrl()).toBe('/api/foo/v2/bar');

      });

      it('should build url with missing slash "/" character', () => {

        const api = new AvApiResource({
          version: 'v1',
          name: 'bar',
          path: 'api/foo'
        });

        expect(api.getUrl()).toBe('/api/foo/v1/bar');

      });

      it('should build url', () => {

        const users = new AvApiResource({
          url: '/availity/login',
          api: false
        });

        expect(users.getUrl()).toBe('/availity/login');

      });
    });

    describe('rest', () => {

      let users;

      beforeEach(function() {
        users = new AvApiResource({name: 'users'});
      });

      describe('query()', () => {

        it('should get list of resources', () => {

          // expectGET to make sure this is called once.
          $httpBackend.expectGET('/api/v1/users').respond(200, responseData);

          users.query().success(function(data) {
            expect(data).toBeEqual(responseData);
          });

          $httpBackend.flush();
        });

        it('should get list of resources with params', () => {

          // expectGET to make sure this is called once.
          $httpBackend.expectGET('/api/v1/users?a=1&b=2').respond(200, responseData);

          users.query({params: {a: 1, b: 2}}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a cacheBust parameter when cacheBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?cacheBust=\d+/).respond(200, responseData);

          users.query({cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();
        });

        it('should have a cacheBust parameter with other parameters', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?a=1&b=2&cacheBust=\d+/).respond(200, responseData);

          users.query({params: {a: 1, b: 2}, cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a sessionBust parameter when sessionBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?sessionBust=\d+/).respond(200, responseData);

          users.query({sessionBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();
        });

      });

      describe('get()', () => {

        it('should get a single resource by id', () => {

          $httpBackend.expectGET('/api/v1/users/1').respond(200, responseData);

          users.get(1).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a cacheBust parameter when cacheBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\/1\?cacheBust=\d+/).respond(200, responseData);

          users.get(1, {cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });
      });

      describe('update()', function() {

        const updateResponseData = {id: 1, foo: 'bar'};

        it('should update a single resource by id', function() {

          $httpBackend.expectPUT('/api/v1/users/1').respond(200, updateResponseData);

          users.update(1, {}).success( data => {
            expect(data).toBeEqual(updateResponseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should update a single resource WITHOUT an id', function() {

          $httpBackend.expectPUT('/api/v1/users').respond(200, updateResponseData);

          const request = spyOn(users, 'request').and.callThrough();

          const data = { id: 1 };
          let config = { headers: { 'MyHeader': 'MyValue' } };
          users.update(data, config).success( result => {
            expect(result).toBeEqual(updateResponseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

          config = users.config(config);
          config.method = 'PUT';
          config.url = users.getUrl();
          config.data = data;

          expect(request).toHaveBeenCalled();
          expect(request).toHaveBeenCalledWith(config, jasmine.any(Function));

        });

      });

      describe('remove()', () => {

        it('should remove resource WITHOUT an id', () => {

          $httpBackend.expectDELETE('/api/v1/users').respond({});

          const request = spyOn(users, 'request').and.callThrough();

          const data = { id: 1 };
          let config = { headers: { 'MyHeader': 'MyValue' } };
          users.remove(data, config);

          $httpBackend.flush();

          config = users.config(config);
          config.method = 'DELETE';
          config.url = users.getUrl();
          config.data = data;

          expect(request).toHaveBeenCalled();
          expect(request).toHaveBeenCalledWith(config, jasmine.any(Function));

        });
      });

    });

    describe('callbacks', () => {

      beforeEach(function() {

      });

      it('should call afterGet() on get()', () => {

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterGet(response) {
            response.data[0].a = 2;
            return response;
          }

        }

        const users = new Users();
        spyOn(users, 'afterGet').and.callThrough();
        $httpBackend.expectGET('/api/v1/users/1').respond(200, responseData);

        users.get(1).success( data => expect(data).toBeEqual([{a: 2, b: 2 }, {a: 1, b: 2 }]));
        $httpBackend.flush();

        expect(users.afterGet).toHaveBeenCalled();
        expect(users.afterGet.calls.count()).toEqual(1);

      });

      it('should call afterQuery() on query()', () => {

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterQuery(response) {
            return response;
          }

        }

        const users = new Users();
        const queryCallback = spyOn(users, 'afterQuery').and.callThrough();
        $httpBackend.expectGET('/api/v1/users').respond(200, responseData);

        users.query();
        $httpBackend.flush();

        expect(queryCallback).toHaveBeenCalled();
        expect(queryCallback.calls.count()).toEqual(1);

      });

      it('should call beforeCreate() and afterCreate() on create()', () => {

        $httpBackend.expectPOST('/api/v1/users').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          beforeCreate(data) {
            return data;
          }

          afterCreate(response) {
            return response;
          }

        }

        const users = new Users();
        const beforeCreateCallback = spyOn(users, 'beforeCreate').and.callThrough();
        const afterCreateCallback = spyOn(users, 'afterCreate').and.callThrough();

        const data = {
          'firstName': 'Rob',
          'lastName': 'McGuinness'
        };

        users.create(data);
        $httpBackend.flush();

        expect(beforeCreateCallback).toHaveBeenCalled();
        expect(beforeCreateCallback).toHaveBeenCalledWith(data);
        expect(beforeCreateCallback.calls.count()).toEqual(1);

        expect(afterCreateCallback).toHaveBeenCalled();
        expect(afterCreateCallback.calls.argsFor(0).length).toEqual(2); //
        expect(afterCreateCallback.calls.argsFor(0)[1]).toEqual(data); //
        expect(afterCreateCallback.calls.count()).toEqual(1);

      });

      it('should call afterRemove() on remove()', () => {

        $httpBackend.expectDELETE('/api/v1/users/1').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterRemove(response) {
            return response;
          }

        }

        const users = new Users();
        const afterRemoveCallback = spyOn(users, 'afterRemove').and.callThrough();

        users.remove(1);
        $httpBackend.flush();

        expect(afterRemoveCallback).toHaveBeenCalled();
        expect(afterRemoveCallback.calls.count()).toEqual(1);
      });

      it('should call beforeUpdate() and afterUpdate() on update()', () => {

        $httpBackend.whenPUT('/api/v1/users/1').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          beforeUpdate(data) {
            return data;
          }

          afterUpdate(response) {
            return response;
          }

        }

        const users = new Users();
        const beforeUpdateCallback = spyOn(users, 'beforeUpdate').and.callThrough();
        const afterUpdateCallback = spyOn(users, 'afterUpdate').and.callThrough();

        const data = {
          'firstName': 'Rob',
          'lastName': 'McGuinness'
        };

        users.update(1, data);

        $httpBackend.flush();

        expect(beforeUpdateCallback).toHaveBeenCalled();
        expect(beforeUpdateCallback).toHaveBeenCalledWith(data);
        expect(beforeUpdateCallback.calls.count()).toEqual(1);

        expect(afterUpdateCallback).toHaveBeenCalled();
        expect(afterUpdateCallback.calls.count()).toEqual(1);
      });

    });
  });

});

<commit_msg>change syntax<commit_after>/* global describe, spyOn, inject, beforeEach, afterEach, it, expect, jasmine, module */

import angular from 'angular';
import ngModule from '../resource';
import Tester from 'tester';

describe('AvApiResourceProvider', () => {

  Tester.matchers();

  describe('AvApiResource', () => {

    let $httpBackend;
    let $q;
    let AvApiResource;
    let callback;

    const responseData = [{a: 1, b: 2}, {a: 1, b: 2}];

    beforeEach( () => {

      callback = jasmine.createSpy('done');

      angular.mock.module(ngModule.name);

      angular.mock.module( $provide => {

        const pollingService = {
          response(response) {
            return response || $q.when(response);
          }
        };

        $provide.value('avPollingService', pollingService);

      });

      inject( (_$httpBackend_, _$q_, _AvApiResource_) => {
        $httpBackend = _$httpBackend_;
        $q = _$q_;
        AvApiResource = _AvApiResource_;
      });

    });

    afterEach( () => {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });

    it('should not allow options to be null', () => {
      expect( () => { AvApiResource.create() }).toThrow(new Error('[options] cannot be null or undefined'));
    });

    describe('urls', () => {

      it('should build url with resource name', () => {

        const users = new AvApiResource({name: 'users'});

        expect(users).toBeObject();
        expect(users.getUrl()).toBe('/api/v1/users');

      });

      it('should build url with resource name and forward slash', () => {

        const users = new AvApiResource({name: '/users'});

        expect(users.getUrl()).toBe('/api/v1/users');

      });

      it('should build url with resource name and path', () => {

        const users = new AvApiResource({
          path: '/internal/api',
          name: '/users'
        });

        expect(users.getUrl()).toBe('/internal/api/v1/users');

      });

      it('should build url with with resource name, version and path', () => {

        const users = new AvApiResource({
          version: '/v2',
          name: '/bar',
          path: '/api/foo'
        });

        expect(users.getUrl()).toBe('/api/foo/v2/bar');

      });

      it('should build url with missing slash "/" character', () => {

        const api = new AvApiResource({
          version: 'v1',
          name: 'bar',
          path: 'api/foo'
        });

        expect(api.getUrl()).toBe('/api/foo/v1/bar');

      });

      it('should build url', () => {

        const users = new AvApiResource({
          url: '/availity/login',
          api: false
        });

        expect(users.getUrl()).toBe('/availity/login');

      });
    });

    describe('rest', () => {

      let users;

      beforeEach(function() {
        users = new AvApiResource({name: 'users'});
      });

      describe('query()', () => {

        it('should get list of resources', () => {

          // expectGET to make sure this is called once.
          $httpBackend.expectGET('/api/v1/users').respond(200, responseData);

          users.query().success(function(data) {
            expect(data).toBeEqual(responseData);
          });

          $httpBackend.flush();
        });

        it('should get list of resources with params', () => {

          // expectGET to make sure this is called once.
          $httpBackend.expectGET('/api/v1/users?a=1&b=2').respond(200, responseData);

          users.query({params: {a: 1, b: 2}}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a cacheBust parameter when cacheBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?cacheBust=\d+/).respond(200, responseData);

          users.query({cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();
        });

        it('should have a cacheBust parameter with other parameters', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?a=1&b=2&cacheBust=\d+/).respond(200, responseData);

          users.query({params: {a: 1, b: 2}, cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a sessionBust parameter when sessionBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\?sessionBust=\d+/).respond(200, responseData);

          users.query({sessionBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();
        });

      });

      describe('get()', () => {

        it('should get a single resource by id', () => {

          $httpBackend.expectGET('/api/v1/users/1').respond(200, responseData);

          users.get(1).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should have a cacheBust parameter when cacheBust is true', () => {

          $httpBackend.expectGET(/\/api\/v1\/users\/1\?cacheBust=\d+/).respond(200, responseData);

          users.get(1, {cacheBust: true}).success(function(data) {
            expect(data).toBeEqual(responseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });
      });

      describe('update()', function() {

        const updateResponseData = {id: 1, foo: 'bar'};

        it('should update a single resource by id', function() {

          $httpBackend.expectPUT('/api/v1/users/1').respond(200, updateResponseData);

          users.update(1, {}).success( data => {
            expect(data).toBeEqual(updateResponseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

        });

        it('should update a single resource WITHOUT an id', function() {

          $httpBackend.expectPUT('/api/v1/users').respond(200, updateResponseData);

          const request = spyOn(users, 'request').and.callThrough();

          const data = { id: 1 };
          let config = { headers: { 'MyHeader': 'MyValue' } };
          users.update(data, config).success( result => {
            expect(result).toBeEqual(updateResponseData);
            callback();
          });

          $httpBackend.flush();
          expect(callback).toHaveBeenCalled();

          config = users.config(config);
          config.method = 'PUT';
          config.url = users.getUrl();
          config.data = data;

          expect(request).toHaveBeenCalled();
          expect(request).toHaveBeenCalledWith(config, jasmine.any(Function));

        });

      });

      describe('remove()', () => {

        it('should remove resource WITHOUT an id', () => {

          $httpBackend.expectDELETE('/api/v1/users').respond({});

          const request = spyOn(users, 'request').and.callThrough();

          const data = { id: 1 };
          let config = { headers: { 'MyHeader': 'MyValue' } };
          users.remove(data, config);

          $httpBackend.flush();

          config = users.config(config);
          config.method = 'DELETE';
          config.url = users.getUrl();
          config.data = data;

          expect(request).toHaveBeenCalled();
          expect(request).toHaveBeenCalledWith(config, jasmine.any(Function));

        });
      });

    });

    describe('callbacks', () => {

      beforeEach(function() {

      });

      it('should call afterGet() on get()', () => {

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterGet(response) {
            response.data[0].a = 2;
            return response;
          }

        }

        const users = new Users();
        spyOn(users, 'afterGet').and.callThrough();
        $httpBackend.expectGET('/api/v1/users/1').respond(200, responseData);

        users.get(1).success( data => expect(data).toBeEqual([{a: 2, b: 2 }, {a: 1, b: 2 }]));
        $httpBackend.flush();

        expect(users.afterGet).toHaveBeenCalled();
        expect(users.afterGet.calls.count()).toEqual(1);

      });

      it('should call afterQuery() on query()', () => {

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterQuery(response) {
            return response;
          }

        }

        const users = new Users();
        const queryCallback = spyOn(users, 'afterQuery').and.callThrough();
        $httpBackend.expectGET('/api/v1/users').respond(200, responseData);

        users.query();
        $httpBackend.flush();

        expect(queryCallback).toHaveBeenCalled();
        expect(queryCallback.calls.count()).toEqual(1);

      });

      it('should call beforeCreate() and afterCreate() on create()', () => {

        $httpBackend.expectPOST('/api/v1/users').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          beforeCreate(data) {
            return data;
          }

          afterCreate(response) {
            return response;
          }

        }

        const users = new Users();
        const beforeCreateCallback = spyOn(users, 'beforeCreate').and.callThrough();
        const afterCreateCallback = spyOn(users, 'afterCreate').and.callThrough();

        const data = {
          'firstName': 'Rob',
          'lastName': 'McGuinness'
        };

        users.create(data);
        $httpBackend.flush();

        expect(beforeCreateCallback).toHaveBeenCalled();
        expect(beforeCreateCallback).toHaveBeenCalledWith(data);
        expect(beforeCreateCallback.calls.count()).toEqual(1);

        expect(afterCreateCallback).toHaveBeenCalled();
        expect(afterCreateCallback.calls.argsFor(0).length).toEqual(2); //
        expect(afterCreateCallback.calls.argsFor(0)[1]).toEqual(data); //
        expect(afterCreateCallback.calls.count()).toEqual(1);

      });

      it('should call afterRemove() on remove()', () => {

        $httpBackend.expectDELETE('/api/v1/users/1').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          afterRemove(response) {
            return response;
          }

        }

        const users = new Users();
        const afterRemoveCallback = spyOn(users, 'afterRemove').and.callThrough();

        users.remove(1);
        $httpBackend.flush();

        expect(afterRemoveCallback).toHaveBeenCalled();
        expect(afterRemoveCallback.calls.count()).toEqual(1);
      });

      it('should call beforeUpdate() and afterUpdate() on update()', () => {

        $httpBackend.whenPUT('/api/v1/users/1').respond(200, responseData);

        class Users extends AvApiResource {

          constructor() {
            super({name: '/users'});
          }

          beforeUpdate(data) {
            return data;
          }

          afterUpdate(response) {
            return response;
          }

        }

        const users = new Users();
        const beforeUpdateCallback = spyOn(users, 'beforeUpdate').and.callThrough();
        const afterUpdateCallback = spyOn(users, 'afterUpdate').and.callThrough();

        const data = {
          'firstName': 'Rob',
          'lastName': 'McGuinness'
        };

        users.update(1, data);

        $httpBackend.flush();

        expect(beforeUpdateCallback).toHaveBeenCalled();
        expect(beforeUpdateCallback).toHaveBeenCalledWith(data);
        expect(beforeUpdateCallback.calls.count()).toEqual(1);

        expect(afterUpdateCallback).toHaveBeenCalled();
        expect(afterUpdateCallback.calls.count()).toEqual(1);
      });

    });
  });

});

<|endoftext|>


<commit_before>function operateAudioPlayer (audioId, pageId, type, data) {
  UniServiceJSBridge.publishHandler(pageId + '-audio-' + audioId, {
    audioId,
    type,
    data
  }, pageId)
}

class AudioContext {
  constructor (id, pageId) {
    this.id = id
    this.pageId = pageId
  }

  setSrc (src) {
    operateAudioPlayer(this.id, this.pageId, 'setSrc', {
      src
    })
  }

  play () {
    operateAudioPlayer(this.id, this.pageId, 'play')
  }

  pause () {
    operateAudioPlayer(this.id, this.pageId, 'pause')
  }

  stop () {
    operateAudioPlayer(this.id, this.pageId, 'stop')
  }

  seek (position) {
    operateAudioPlayer(this.id, this.pageId, 'seek', {
      position
    })
  }
}

export function createAudioContext (id, context) {
  if (context) {
    return new AudioContext(id, context.$page.id)
  }
  const app = getApp()
  if (app.$route && app.$route.params.__id__) {
    return new AudioContext(id, app.$route.params.__id__)
  } else {
    UniServiceJSBridge.emit('onError', 'createAudioContext:fail')
  }
}
<commit_msg>fix: AudioContext 移除 stop 方法<commit_after>function operateAudioPlayer (audioId, pageId, type, data) {
  UniServiceJSBridge.publishHandler(pageId + '-audio-' + audioId, {
    audioId,
    type,
    data
  }, pageId)
}

class AudioContext {
  constructor (id, pageId) {
    this.id = id
    this.pageId = pageId
  }

  setSrc (src) {
    operateAudioPlayer(this.id, this.pageId, 'setSrc', {
      src
    })
  }

  play () {
    operateAudioPlayer(this.id, this.pageId, 'play')
  }

  pause () {
    operateAudioPlayer(this.id, this.pageId, 'pause')
  }

  seek (position) {
    operateAudioPlayer(this.id, this.pageId, 'seek', {
      position
    })
  }
}

export function createAudioContext (id, context) {
  if (context) {
    return new AudioContext(id, context.$page.id)
  }
  const app = getApp()
  if (app.$route && app.$route.params.__id__) {
    return new AudioContext(id, app.$route.params.__id__)
  } else {
    UniServiceJSBridge.emit('onError', 'createAudioContext:fail')
  }
}
<|endoftext|>


<commit_before>
// @require core/type_checking.js
// @require ./is_css_variable.js

const numericProps = {
  columnCount: true,
  flexGrow: true,
  flexShrink: true,
  fontWeight: true,
  lineHeight: true,
  opacity: true,
  order: true,
  orphans: true,
  widows: true,
  zIndex: true
};

function getSuffixedValue ( prop, value, isVariable = isCSSVariable ( prop ) ) {

  return !isVariable && !numericProps[prop] && isNumeric ( value ) ? `${value}px` : value;

}
<commit_msg>getSuffixedValue: added back support for `animationIterationCount`<commit_after>
// @require core/type_checking.js
// @require ./is_css_variable.js

const numericProps = {
  animationIterationCount: true,
  columnCount: true,
  flexGrow: true,
  flexShrink: true,
  fontWeight: true,
  lineHeight: true,
  opacity: true,
  order: true,
  orphans: true,
  widows: true,
  zIndex: true
};

function getSuffixedValue ( prop, value, isVariable = isCSSVariable ( prop ) ) {

  return !isVariable && !numericProps[prop] && isNumeric ( value ) ? `${value}px` : value;

}
<|endoftext|>


<commit_before>import T                          from "../constants/ActionTypes";
import GeoLocation                from "../GeoLocation";
import mapConst                   from "../constants/Map";
import appConst                   from "../constants/App";
import serverActions              from "./server";

const Actions = {

  setSearchText: (txt) => ({
    type: T.SET_SEARCH_TEXT,
    payload: txt
  }),

  setCitySearchText: (txt) => ({
    type: T.SET_CITY_SEARCH_TEXT,
    payload: txt
  }),

  finishCitySearch: () => ({
    type: T.FINISH_CITY_SEARCH
  }),

  toggleSearchCategory: (category) => {
    return {
      type: T.TOGGLE_SEARCH_CATEGORY,
      payload: category
    };
  },

  toggleMenu          : () => ({ type: T.TOGGLE_MENU            }),
  showMenu            : () => ({ type: T.SHOW_MENU              }),
  showNewEntry        : () => ({ type: T.SHOW_NEW_ENTRY         }),
  showSearchResults   : () => ({ type: T.SHOW_SEARCH_RESULTS    }),
  toggleLandingPage   : () => ({ type: T.TOGGLE_MENU            }),
  showImprint         : () => ({ type: T.SHOW_IMPRINT           }),
  cancelNew           : () => ({ type: T.CANCEL_NEW             }),
  cancelEdit          : () => ({ type: T.CANCEL_EDIT            }),
  cancelRating        : () => ({ type: T.CANCEL_RATING          }),
  cancelWait          : () => ({ type: T.CANCEL_WAIT_IO         }),
  closeIoErrorMessage : () => ({ type: T.CLOSE_IO_ERROR_MESSAGE }),
  showLeftPanel       : () => ({ type: T.SHOW_LEFT_PANEL }),
  hideLeftPanel       : () => ({ type: T.HIDE_LEFT_PANEL }),

  showAllEntries: () =>
    (dispatch, getState) => {
      dispatch({
        type: T.FETCH_ALL_ENTRIES,
      });
      const allIDs = [];
      if(Array.isArray(getState().search.result)){
        allIDs.push(allIDs, getState().search.result.map(e => e.id));
      }
      if(Array.isArray(getState().search.invisible)){
        allIDs.push(allIDs, getState().search.invisible.map(e => e.id));
      }
      dispatch(serverActions.getEntries(allIDs));
    },

  showNewRating: (id) => ({
    type: T.SHOW_NEW_RATING,
    payload: id
  }),

  showInfo: (key) => ({
    type: T.SHOW_INFO,
    payload: key
  }),

  showSubscribeToBbox: () => ({
    type: T.SHOW_SUBSCRIBE_TO_BBOX
  }),

  logout: () => ({
    type: T.LOGOUT
  }),

  setCenter: (centerOrEntryId) => {
    return {
      type: T.SET_MAP_CENTER,
      payload: centerOrEntryId
    };
  },

  setZoom: (zoom) => {
    return {
      type: T.SET_ZOOM,
      payload: parseFloat(zoom)
    };
  },

  setBbox: (bbox) => {
    return {
      type: T.SET_BBOX,
      payload: bbox
    };
  },

  setCurrentEntry: (id, center) =>
    (dispatch, getState) => {
      dispatch(Actions.highlight(id ? [id] : []));
      dispatch({
        type: T.SET_CURRENT_ENTRY,
        payload: id,
      });
      if(id && center){
        dispatch(Actions.setZoom(mapConst.ENTRY_DEFAULT_ZOOM));
        dispatch(Actions.setCenter(center));
      }
    },

  updateStateFromURL: (hash) => {
    return {
      type: T.UPDATE_STATE_FROM_URL,
      payload: hash
    }
  },

  setCenterInUrl: (center) => {
    return {
      type: T.SET_CENTER_IN_URL,
      payload: center
    }
  },

  highlight: (id) => {
    if (id == null) {
      id = [];
    }
    if (!Array.isArray(id)) {
      id = [id];
    }
    return {
      type: T.HIGHLIGHT_ENTRIES,
      payload: id
    };
  },

  showOwnPosition: () =>
    (dispatch) => {
      dispatch({
        type: T.SHOW_OWN_POSITION
      });
      GeoLocation.getLocation((position) => {
        dispatch({
          type: T.OWN_POSITION_RESULT,
          payload: position
        });
      });
    },

  showOwnPosition15minutes: () =>
    (dispatch) => {
      dispatch({
        type: T.SHOW_OWN_POSITION
      });
      GeoLocation.getLocation(((position) => {
        dispatch({
          type: T.OWN_POSITION_RESULT,
          payload: position
        });
      }), 900000);
    },

  cancelOwnPosition: () => {
    return {
      type: T.CANCEL_OWN_POSITION
    };
  },

  showFeatureToDonate: (feat) => {
    return {
      type: T.SHOW_FEATURE_TO_DONATE,
      payload: feat
    };
  },

  showMap: () => {
    return {
      type: T.SHOW_MAP
    }
  },

  explainRatingContext: (context) => {
    return {
      type: T.EXPLAIN_RATING_CONTEXT,
      payload: context
    }
  },

  onMoveend: (coordinates, mapCenter) =>
    (dispatch, getState) => {
      dispatch({
        type: T.SET_MOVEEND_TIME,
        payload: Date.now()
      });
      setTimeout(() => {
        if(getState().timedActions.moveendLastTriggered && (Date.now() - getState().timedActions.moveendLastTriggered > appConst.MOVEEND_DELAY)){
          if(mapCenter.lat.toFixed(4) != coordinates.center.lat && mapCenter.lng.toFixed(4) != coordinates.center.lng){
            dispatch(Actions.setCenter({
              lat: coordinates.center.lat,
              lng: coordinates.center.lng
            }));
          }
          dispatch(Actions.setBbox(coordinates.bbox));
          dispatch(serverActions.Actions.search());
          dispatch({
            type: T.SET_MOVEEND_TIME,
            payload: null
          });
        }
      }, appConst.MOVEEND_DELAY);
    }
};

module.exports = Actions;
<commit_msg>fix(Actions): fix reference to server actions)<commit_after>import T                          from "../constants/ActionTypes";
import GeoLocation                from "../GeoLocation";
import mapConst                   from "../constants/Map";
import appConst                   from "../constants/App";
import serverActions              from "./server";

const Actions = {

  setSearchText: (txt) => ({
    type: T.SET_SEARCH_TEXT,
    payload: txt
  }),

  setCitySearchText: (txt) => ({
    type: T.SET_CITY_SEARCH_TEXT,
    payload: txt
  }),

  finishCitySearch: () => ({
    type: T.FINISH_CITY_SEARCH
  }),

  toggleSearchCategory: (category) => {
    return {
      type: T.TOGGLE_SEARCH_CATEGORY,
      payload: category
    };
  },

  toggleMenu          : () => ({ type: T.TOGGLE_MENU            }),
  showMenu            : () => ({ type: T.SHOW_MENU              }),
  showNewEntry        : () => ({ type: T.SHOW_NEW_ENTRY         }),
  showSearchResults   : () => ({ type: T.SHOW_SEARCH_RESULTS    }),
  toggleLandingPage   : () => ({ type: T.TOGGLE_MENU            }),
  showImprint         : () => ({ type: T.SHOW_IMPRINT           }),
  cancelNew           : () => ({ type: T.CANCEL_NEW             }),
  cancelEdit          : () => ({ type: T.CANCEL_EDIT            }),
  cancelRating        : () => ({ type: T.CANCEL_RATING          }),
  cancelWait          : () => ({ type: T.CANCEL_WAIT_IO         }),
  closeIoErrorMessage : () => ({ type: T.CLOSE_IO_ERROR_MESSAGE }),
  showLeftPanel       : () => ({ type: T.SHOW_LEFT_PANEL }),
  hideLeftPanel       : () => ({ type: T.HIDE_LEFT_PANEL }),

  showAllEntries: () =>
    (dispatch, getState) => {
      dispatch({
        type: T.FETCH_ALL_ENTRIES,
      });
      const allIDs = [];
      if(Array.isArray(getState().search.result)){
        allIDs.push(allIDs, getState().search.result.map(e => e.id));
      }
      if(Array.isArray(getState().search.invisible)){
        allIDs.push(allIDs, getState().search.invisible.map(e => e.id));
      }
      dispatch(serverActions.Actions.getEntries(allIDs));
    },

  showNewRating: (id) => ({
    type: T.SHOW_NEW_RATING,
    payload: id
  }),

  showInfo: (key) => ({
    type: T.SHOW_INFO,
    payload: key
  }),

  showSubscribeToBbox: () => ({
    type: T.SHOW_SUBSCRIBE_TO_BBOX
  }),

  logout: () => ({
    type: T.LOGOUT
  }),

  setCenter: (centerOrEntryId) => {
    return {
      type: T.SET_MAP_CENTER,
      payload: centerOrEntryId
    };
  },

  setZoom: (zoom) => {
    return {
      type: T.SET_ZOOM,
      payload: parseFloat(zoom)
    };
  },

  setBbox: (bbox) => {
    return {
      type: T.SET_BBOX,
      payload: bbox
    };
  },

  setCurrentEntry: (id, center) =>
    (dispatch, getState) => {
      dispatch(Actions.highlight(id ? [id] : []));
      dispatch({
        type: T.SET_CURRENT_ENTRY,
        payload: id,
      });
      if(id && center){
        dispatch(Actions.setZoom(mapConst.ENTRY_DEFAULT_ZOOM));
        dispatch(Actions.setCenter(center));
      }
    },

  updateStateFromURL: (hash) => {
    return {
      type: T.UPDATE_STATE_FROM_URL,
      payload: hash
    }
  },

  setCenterInUrl: (center) => {
    return {
      type: T.SET_CENTER_IN_URL,
      payload: center
    }
  },

  highlight: (id) => {
    if (id == null) {
      id = [];
    }
    if (!Array.isArray(id)) {
      id = [id];
    }
    return {
      type: T.HIGHLIGHT_ENTRIES,
      payload: id
    };
  },

  showOwnPosition: () =>
    (dispatch) => {
      dispatch({
        type: T.SHOW_OWN_POSITION
      });
      GeoLocation.getLocation((position) => {
        dispatch({
          type: T.OWN_POSITION_RESULT,
          payload: position
        });
      });
    },

  showOwnPosition15minutes: () =>
    (dispatch) => {
      dispatch({
        type: T.SHOW_OWN_POSITION
      });
      GeoLocation.getLocation(((position) => {
        dispatch({
          type: T.OWN_POSITION_RESULT,
          payload: position
        });
      }), 900000);
    },

  cancelOwnPosition: () => {
    return {
      type: T.CANCEL_OWN_POSITION
    };
  },

  showFeatureToDonate: (feat) => {
    return {
      type: T.SHOW_FEATURE_TO_DONATE,
      payload: feat
    };
  },

  showMap: () => {
    return {
      type: T.SHOW_MAP
    }
  },

  explainRatingContext: (context) => {
    return {
      type: T.EXPLAIN_RATING_CONTEXT,
      payload: context
    }
  },

  onMoveend: (coordinates, mapCenter) =>
    (dispatch, getState) => {
      dispatch({
        type: T.SET_MOVEEND_TIME,
        payload: Date.now()
      });
      setTimeout(() => {
        if(getState().timedActions.moveendLastTriggered && (Date.now() - getState().timedActions.moveendLastTriggered > appConst.MOVEEND_DELAY)){
          if(mapCenter.lat.toFixed(4) != coordinates.center.lat && mapCenter.lng.toFixed(4) != coordinates.center.lng){
            dispatch(Actions.setCenter({
              lat: coordinates.center.lat,
              lng: coordinates.center.lng
            }));
          }
          dispatch(Actions.setBbox(coordinates.bbox));
          dispatch(serverActions.Actions.search());
          dispatch({
            type: T.SET_MOVEEND_TIME,
            payload: null
          });
        }
      }, appConst.MOVEEND_DELAY);
    }
};

module.exports = Actions;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
 *
 * @licstart
 * The JavaScript code in this page is free software: you can redistribute it
 * and/or modify it under the terms of the GNU Affero General Public License
 * (GNU AGPL) as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.  The code is distributed
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU AGPL for more details.
 *
 * As additional permission under GNU AGPL version 3 section 7, you
 * may distribute non-source (e.g., minimized or compacted) forms of
 * that code without the copy of the GNU GPL normally required by
 * section 4, provided you include this license notice and a URL
 * through which recipients can access the Corresponding Source.
 *
 * As a special exception to the AGPL, any HTML file which merely makes function
 * calls to this code, and for that purpose includes it by reference shall be
 * deemed a separate work for copyright law purposes. In addition, the copyright
 * holders of this code give you permission to combine this code with free
 * software libraries that are released under the GNU LGPL. You may copy and
 * distribute such a system following the terms of the GNU AGPL for this code
 * and the LGPL for the libraries. If you modify this code, you may extend this
 * exception to your version of the code, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 *
 * This license applies to this entire compilation.
 * @licend
 * @source: http://www.webodf.org/
 * @source: http://gitorious.org/webodf/webodf/
 */

/*global document, PDFJS, console, TextLayerBuilder*/

function PDFViewerPlugin() {
    "use strict";

    function init(callback) {
        var pdfLib, textLayerLib, pluginCSS;

        pdfLib = document.createElement('script');
        pdfLib.async = false;
        pdfLib.src = './pdf.js';
        pdfLib.type = 'text/javascript';
        pdfLib.onload = function () {
            textLayerLib = document.createElement('script');
            textLayerLib.async = false;
            textLayerLib.src = './TextLayerBuilder.js';
            textLayerLib.type = 'text/javascript';
            textLayerLib.onload = callback;
            document.getElementsByTagName('head')[0].appendChild(textLayerLib);
        };
        document.getElementsByTagName('head')[0].appendChild(pdfLib);

        pluginCSS = document.createElement('link');
        pluginCSS.setAttribute("rel", "stylesheet");
        pluginCSS.setAttribute("type", "text/css");
        pluginCSS.setAttribute("href", "./PDFViewerPlugin.css");
        document.head.appendChild(pluginCSS);
    }

    var self = this,
        pages = [],
        domPages = [],
        pageText = [],
        renderingStates = [],
        RENDERING = {
            BLANK: 0,
            RUNNING: 1,
            FINISHED: 2
        },
        startedTextExtraction = false,
        container = null,
        initialized = false,
        pdfDocument = null,
        pageViewScroll = null,
        isPresentationMode = false,
        scale = 1,
        currentPage = 1,
        pageWidth,
        pageHeight,
        createdPageCount = 0;

    function isScrolledIntoView(elem) {
        var docViewTop = container.scrollTop,
            docViewBottom = docViewTop + container.clientHeight,
            elemTop = elem.offsetTop,
            elemBottom = elemTop + elem.clientHeight;

        // Is in view if either the top or the bottom of the page is between the
        // document viewport bounds,
        // or if the top is above the viewport and the bottom is below it.
        return (elemTop >= docViewTop && elemTop < docViewBottom)
                || (elemBottom >= docViewTop && elemBottom < docViewBottom)
                || (elemTop < docViewTop && elemBottom >= docViewBottom);
    }

    function getDomPage(page) {
        return domPages[page.pageInfo.pageIndex];
    }
    function getPageText(page) {
        return pageText[page.pageInfo.pageIndex];
    }
    function getRenderingStatus(page) {
        return renderingStates[page.pageInfo.pageIndex];
    }
    function setRenderingStatus(page, renderStatus) {
        renderingStates[page.pageInfo.pageIndex] = renderStatus;
    }

    function updatePageDimensions(page, width, height) {
        var domPage = getDomPage(page),
            canvas = domPage.getElementsByTagName('canvas')[0],
            textLayer = domPage.getElementsByTagName('div')[0];

        domPage.style.width = width;
        domPage.style.height = height;

        canvas.width = width;
        canvas.height = height;

        textLayer.style.width = width;
        textLayer.style.height = height;

        // Once the page dimension is updated, the rendering state is blank.
        setRenderingStatus(page, RENDERING.BLANK);
    }

    function renderPage(page) {
        var domPage = getDomPage(page),
            textLayer = getPageText(page),
            canvas = domPage.getElementsByTagName('canvas')[0];

        if (getRenderingStatus(page) === RENDERING.BLANK) {
            setRenderingStatus(page, RENDERING.RUNNING);
            page.render({
                canvasContext: canvas.getContext('2d'),
                textLayer: textLayer,
                viewport: page.getViewport(scale)
            }).then(function () {
                setRenderingStatus(page, RENDERING.FINISHED);
            });
        }
    }

    function createPage(page) {
        var pageNumber,
            textLayerDiv,
            textLayer,
            canvas,
            domPage,
            viewport;

        pageNumber = page.pageInfo.pageIndex + 1;

        viewport = page.getViewport(scale);

        domPage = document.createElement('div');
        domPage.id = 'pageContainer' + pageNumber;
        domPage.className = 'page';

        canvas = document.createElement('canvas');
        canvas.id = 'canvas' + pageNumber;

        textLayerDiv = document.createElement('div');
        textLayerDiv.className = 'textLayer';
        textLayerDiv.id = 'textLayer' + pageNumber;

        container.appendChild(domPage);
        domPage.appendChild(canvas);
        domPage.appendChild(textLayerDiv);

        pages.push(page);
        domPages.push(domPage);
        renderingStates.push(RENDERING.BLANK);

        updatePageDimensions(page, viewport.width, viewport.height);
        pageWidth = viewport.width;
        pageHeight = viewport.height;

        textLayer = new TextLayerBuilder({
            textLayerDiv: textLayerDiv,
            pageIndex: pageNumber - 1
        });
        page.getTextContent().then(function (textContent) {
            textLayer.setTextContent(textContent);
        });
        pageText.push(textLayer);

        createdPageCount += 1;
        if (createdPageCount === (pdfDocument.numPages)) {
            self.onLoad();
        }
    }

    this.initialize = function (viewContainer, location) {
        var self = this,
            i,
            pluginCSS;

        init(function () {
            PDFJS.disableWorker = true;
            PDFJS.getDocument(location).then(function loadPDF(doc) {
                pdfDocument = doc;
                container = viewContainer;

                for (i = 0; i < pdfDocument.numPages; i += 1) {
                    pdfDocument.getPage(i + 1).then(createPage);
                }

                initialized = true;
            });
        });
    };

    this.isSlideshow = function () {
        // A very simple but generally true guess - if the width is greater than the height, treat it as a slideshow
        return pageWidth > pageHeight;
    };

    this.onLoad = function () {};

    this.getPages = function () {
        return domPages;
    };

    this.getWidth = function () {
        return pageWidth;
    };

    this.getHeight = function () {
        return pageHeight;
    };

    this.fitToWidth = function (width) {
        var zoomLevel;

        if (self.getWidth() === width) {
            return;
        }
        zoomLevel = width / pageWidth;
        self.setZoomLevel(zoomLevel);
    };

    this.fitToHeight = function (height) {
        var zoomLevel;

        if (self.getHeight() === height) {
            return;
        }
        zoomLevel = height / pageHeight;
        self.setZoomLevel(zoomLevel);
    };

    this.fitToPage = function (width, height) {
        var zoomLevel = width / pageWidth;
        if (height / pageHeight < zoomLevel) {
            zoomLevel = height / pageHeight;
        }
        self.setZoomLevel(zoomLevel);
    };

    this.fitSmart = function (width, height) {
        var zoomLevel = width / pageWidth;
        if (height && (height / pageHeight) < zoomLevel) {
            zoomLevel = height / pageHeight;
        }
        zoomLevel = Math.min(1.0, zoomLevel);
        self.setZoomLevel(zoomLevel);
    };

    this.setZoomLevel = function (zoomLevel) {
        var i;

        if (scale !== zoomLevel) {
            scale = zoomLevel;

            for (i = 0; i < pages.length; i += 1) {
                updatePageDimensions(pages[i], pageWidth * scale, pageHeight * scale);
            }
        }
    };

    this.getZoomLevel = function () {
        return scale;
    };

    this.onScroll = function () {
        var i;

        for (i = 0; i < domPages.length; i += 1) {
            if (isScrolledIntoView(domPages[i])) {
                if (getRenderingStatus(pages[i]) === RENDERING.BLANK) {
                    renderPage(pages[i]);
                }
            }
        }
    };

    this.getPageInView = function () {
        var i;
        for (i = 0; i < domPages.length; i += 1) {
            if (isScrolledIntoView(domPages[i])) {
                return i + 1;
            }
        }
    };

    this.showPage = function (n) {
        domPages[n - 1].scrollIntoView();
    };
}
<commit_msg>In the PDF viewer plugin, use offsetTop to scroll pages instead of window.scrollIntoView, because that can also scroll the parent of the iframe the viewer is in.<commit_after>/**
 * @license
 * Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
 *
 * @licstart
 * The JavaScript code in this page is free software: you can redistribute it
 * and/or modify it under the terms of the GNU Affero General Public License
 * (GNU AGPL) as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.  The code is distributed
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU AGPL for more details.
 *
 * As additional permission under GNU AGPL version 3 section 7, you
 * may distribute non-source (e.g., minimized or compacted) forms of
 * that code without the copy of the GNU GPL normally required by
 * section 4, provided you include this license notice and a URL
 * through which recipients can access the Corresponding Source.
 *
 * As a special exception to the AGPL, any HTML file which merely makes function
 * calls to this code, and for that purpose includes it by reference shall be
 * deemed a separate work for copyright law purposes. In addition, the copyright
 * holders of this code give you permission to combine this code with free
 * software libraries that are released under the GNU LGPL. You may copy and
 * distribute such a system following the terms of the GNU AGPL for this code
 * and the LGPL for the libraries. If you modify this code, you may extend this
 * exception to your version of the code, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 *
 * This license applies to this entire compilation.
 * @licend
 * @source: http://www.webodf.org/
 * @source: http://gitorious.org/webodf/webodf/
 */

/*global document, PDFJS, console, TextLayerBuilder*/

function PDFViewerPlugin() {
    "use strict";

    function init(callback) {
        var pdfLib, textLayerLib, pluginCSS;

        pdfLib = document.createElement('script');
        pdfLib.async = false;
        pdfLib.src = './pdf.js';
        pdfLib.type = 'text/javascript';
        pdfLib.onload = function () {
            textLayerLib = document.createElement('script');
            textLayerLib.async = false;
            textLayerLib.src = './TextLayerBuilder.js';
            textLayerLib.type = 'text/javascript';
            textLayerLib.onload = callback;
            document.getElementsByTagName('head')[0].appendChild(textLayerLib);
        };
        document.getElementsByTagName('head')[0].appendChild(pdfLib);

        pluginCSS = document.createElement('link');
        pluginCSS.setAttribute("rel", "stylesheet");
        pluginCSS.setAttribute("type", "text/css");
        pluginCSS.setAttribute("href", "./PDFViewerPlugin.css");
        document.head.appendChild(pluginCSS);
    }

    var self = this,
        pages = [],
        domPages = [],
        pageText = [],
        renderingStates = [],
        RENDERING = {
            BLANK: 0,
            RUNNING: 1,
            FINISHED: 2
        },
        startedTextExtraction = false,
        container = null,
        initialized = false,
        pdfDocument = null,
        pageViewScroll = null,
        isPresentationMode = false,
        scale = 1,
        currentPage = 1,
        pageWidth,
        pageHeight,
        createdPageCount = 0;

    function scrollIntoView(elem) {
        elem.parentNode.scrollTop = elem.offsetTop;
    }

    function isScrolledIntoView(elem) {
        var docViewTop = container.scrollTop,
            docViewBottom = docViewTop + container.clientHeight,
            elemTop = elem.offsetTop,
            elemBottom = elemTop + elem.clientHeight;

        // Is in view if either the top or the bottom of the page is between the
        // document viewport bounds,
        // or if the top is above the viewport and the bottom is below it.
        return (elemTop >= docViewTop && elemTop < docViewBottom)
                || (elemBottom >= docViewTop && elemBottom < docViewBottom)
                || (elemTop < docViewTop && elemBottom >= docViewBottom);
    }

    function getDomPage(page) {
        return domPages[page.pageInfo.pageIndex];
    }
    function getPageText(page) {
        return pageText[page.pageInfo.pageIndex];
    }
    function getRenderingStatus(page) {
        return renderingStates[page.pageInfo.pageIndex];
    }
    function setRenderingStatus(page, renderStatus) {
        renderingStates[page.pageInfo.pageIndex] = renderStatus;
    }

    function updatePageDimensions(page, width, height) {
        var domPage = getDomPage(page),
            canvas = domPage.getElementsByTagName('canvas')[0],
            textLayer = domPage.getElementsByTagName('div')[0];

        domPage.style.width = width;
        domPage.style.height = height;

        canvas.width = width;
        canvas.height = height;

        textLayer.style.width = width;
        textLayer.style.height = height;

        // Once the page dimension is updated, the rendering state is blank.
        setRenderingStatus(page, RENDERING.BLANK);
    }

    function renderPage(page) {
        var domPage = getDomPage(page),
            textLayer = getPageText(page),
            canvas = domPage.getElementsByTagName('canvas')[0];

        if (getRenderingStatus(page) === RENDERING.BLANK) {
            setRenderingStatus(page, RENDERING.RUNNING);
            page.render({
                canvasContext: canvas.getContext('2d'),
                textLayer: textLayer,
                viewport: page.getViewport(scale)
            }).then(function () {
                setRenderingStatus(page, RENDERING.FINISHED);
            });
        }
    }

    function createPage(page) {
        var pageNumber,
            textLayerDiv,
            textLayer,
            canvas,
            domPage,
            viewport;

        pageNumber = page.pageInfo.pageIndex + 1;

        viewport = page.getViewport(scale);

        domPage = document.createElement('div');
        domPage.id = 'pageContainer' + pageNumber;
        domPage.className = 'page';

        canvas = document.createElement('canvas');
        canvas.id = 'canvas' + pageNumber;

        textLayerDiv = document.createElement('div');
        textLayerDiv.className = 'textLayer';
        textLayerDiv.id = 'textLayer' + pageNumber;

        container.appendChild(domPage);
        domPage.appendChild(canvas);
        domPage.appendChild(textLayerDiv);

        pages.push(page);
        domPages.push(domPage);
        renderingStates.push(RENDERING.BLANK);

        updatePageDimensions(page, viewport.width, viewport.height);
        pageWidth = viewport.width;
        pageHeight = viewport.height;

        textLayer = new TextLayerBuilder({
            textLayerDiv: textLayerDiv,
            pageIndex: pageNumber - 1
        });
        page.getTextContent().then(function (textContent) {
            textLayer.setTextContent(textContent);
        });
        pageText.push(textLayer);

        createdPageCount += 1;
        if (createdPageCount === (pdfDocument.numPages)) {
            self.onLoad();
        }
    }

    this.initialize = function (viewContainer, location) {
        var self = this,
            i,
            pluginCSS;

        init(function () {
            PDFJS.disableWorker = true;
            PDFJS.getDocument(location).then(function loadPDF(doc) {
                pdfDocument = doc;
                container = viewContainer;

                for (i = 0; i < pdfDocument.numPages; i += 1) {
                    pdfDocument.getPage(i + 1).then(createPage);
                }

                initialized = true;
            });
        });
    };

    this.isSlideshow = function () {
        // A very simple but generally true guess - if the width is greater than the height, treat it as a slideshow
        return pageWidth > pageHeight;
    };

    this.onLoad = function () {};

    this.getPages = function () {
        return domPages;
    };

    this.getWidth = function () {
        return pageWidth;
    };

    this.getHeight = function () {
        return pageHeight;
    };

    this.fitToWidth = function (width) {
        var zoomLevel;

        if (self.getWidth() === width) {
            return;
        }
        zoomLevel = width / pageWidth;
        self.setZoomLevel(zoomLevel);
    };

    this.fitToHeight = function (height) {
        var zoomLevel;

        if (self.getHeight() === height) {
            return;
        }
        zoomLevel = height / pageHeight;
        self.setZoomLevel(zoomLevel);
    };

    this.fitToPage = function (width, height) {
        var zoomLevel = width / pageWidth;
        if (height / pageHeight < zoomLevel) {
            zoomLevel = height / pageHeight;
        }
        self.setZoomLevel(zoomLevel);
    };

    this.fitSmart = function (width, height) {
        var zoomLevel = width / pageWidth;
        if (height && (height / pageHeight) < zoomLevel) {
            zoomLevel = height / pageHeight;
        }
        zoomLevel = Math.min(1.0, zoomLevel);
        self.setZoomLevel(zoomLevel);
    };

    this.setZoomLevel = function (zoomLevel) {
        var i;

        if (scale !== zoomLevel) {
            scale = zoomLevel;

            for (i = 0; i < pages.length; i += 1) {
                updatePageDimensions(pages[i], pageWidth * scale, pageHeight * scale);
            }
        }
    };

    this.getZoomLevel = function () {
        return scale;
    };

    this.onScroll = function () {
        var i;

        for (i = 0; i < domPages.length; i += 1) {
            if (isScrolledIntoView(domPages[i])) {
                if (getRenderingStatus(pages[i]) === RENDERING.BLANK) {
                    renderPage(pages[i]);
                }
            }
        }
    };

    this.getPageInView = function () {
        var i;
        for (i = 0; i < domPages.length; i += 1) {
            if (isScrolledIntoView(domPages[i])) {
                return i + 1;
            }
        }
    };

    this.showPage = function (n) {
        scrollIntoView(domPages[n - 1]);
    };
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * Implements `document.register`
 * @module CustomElements
*/

/**
 * Polyfilled extensions to the `document` object.
 * @class Document
*/

(function() {

/**
 * Registers a custom tag name with the document.
 *
 * When a registered element is created, a `readyCallback` method is called
 * in the scope of the element. The `readyCallback` method can be specified on
 * either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
 * precedence.
 *
 * @method register
 * @param {String} inName The tag name to register. Must include a dash ('-'),
 *    for example 'x-component'.
 * @param {Object} inOptions
 *    @param {String} [inOptions.extends]
 *      (_off spec_) Tag name of an element to extend (or blank for a new
 *      element). This parameter is not part of the specification, but instead
 *      is a hint for the polyfill because the extendee is difficult to infer.
 *      Remember that the input prototype must chain to the extended element's
 *      prototype (or HTMLElement.prototype) regardless of the value of
 *      `extends`.
 *    @param {Object} inOptions.prototype The prototype to use for the new
 *      element. The prototype must inherit from HTMLElement.
 *    @param {Object} [inOptions.lifecycle]
 *      Callbacks that fire at important phases in the life of the custom
 *      element.
 *
 * @example
 *      FancyButton = document.register("fancy-button", {
 *        extends: 'button',
 *        prototype: Object.create(HTMLButtonElement.prototype, {
 *          readyCallback: {
 *            value: function() {
 *              console.log("a fancy-button was created",
 *            }
 *          }
 *        })
 *      });
 * @return {Function} Constructor for the newly registered type.
 */
function register(inName, inOptions) {
  //console.warn('document.register("' + inName + '", ', inOptions, ')');
  // construct a defintion out of options
  // TODO(sjmiles): probably should clone inOptions instead of mutating it
  var definition = inOptions || {};
  if (!inName) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Name argument must not be empty');
  }
  // record name
  definition.name = inName;
  // must have a prototype, default to an extension of HTMLElement
  // TODO(sjmiles): probably should throw if no prototype, check spec
  if (!definition.prototype) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Options missing required prototype property');
  }
  // ensure a lifecycle object so we don't have to null test it
  definition.lifecycle = definition.lifecycle || {};
  // build a list of ancestral custom elements (for native base detection)
  // TODO(sjmiles): we used to need to store this, but current code only
  // uses it in 'resolveTagName': it should probably be inlined
  definition.ancestry = ancestry(definition.extends);
  // extensions of native specializations of HTMLElement require localName
  // to remain native, and use secondary 'is' specifier for extension type
  resolveTagName(definition);
  // some platforms require modifications to the user-supplied prototype
  // chain
  resolvePrototypeChain(definition);
  // 7.1.5: Register the DEFINITION with DOCUMENT
  registerDefinition(inName, definition);
  // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
  // 7.1.8. Return the output of the previous step.
  definition.ctor = generateConstructor(definition);
  definition.ctor.prototype = definition.prototype;
  // blanket upgrade (?)
  document.upgradeElements();
  return definition.ctor;
}

function ancestry(inExtends) {
  var extendee = registry[inExtends];
  if (extendee) {
    return ancestry(extendee.extends).concat([extendee]);
  }
  return [];
}

function resolveTagName(inDefinition) {
  // if we are explicitly extending something, that thing is our
  // baseTag, unless it represents a custom component
  var baseTag = inDefinition.extends;
  // if our ancestry includes custom components, we only have a
  // baseTag if one of them does
  for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
    baseTag = a.is && a.tag;
  }
  // our tag is our baseTag, if it exists, and otherwise just our name
  inDefinition.tag = baseTag || inDefinition.name;
  if (baseTag) {
    // if there is a base tag, use secondary 'is' specifier
    inDefinition.is = inDefinition.name;
  }
}

function resolvePrototypeChain(inDefinition) {
  // TODO(sjmiles): contains ShadowDOM polyfill pollution
  // if we don't support __proto__ we need to locate the native level
  // prototype for precise mixing in
  // we also need native prototype so we can replace it with a wrapper
  // prototype if necessary
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)
        || !Object.__proto__) {
    if (inDefinition.is) {
      // for non-trivial extensions, work out both prototypes
      var inst = domCreateElement(inDefinition.tag);
      var wrapperNative = Object.getPrototypeOf(inst);
      if (window.WrapperElement) {
        inst = unwrap(inst);
      }
      var native = Object.getPrototypeOf(inst);
    } else {
      // otherwise, use the defaults
      native = HTMLElement.prototype;
      if (window.WrapperElement) {
        wrapperNative = WrapperHTMLUnknownElement.prototype;
      }
    }
  }
  // cache this in case of mixin
  inDefinition.native = native;
  // under ShadowDOM polyfill we need to use Wrapper prototype chain instead
  // of native DOM
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)) {
    if (Object.__proto__) {
      // search and replace 'native' protoype with 'wrapperNative'
      var p = inDefinition.prototype, pp;
      while (true) {
        pp = Object.getPrototypeOf(p);
        if (pp === native || pp == HTMLUnknownElement.prototype) {
          break;
        }
        p = pp;
      }
      p.__proto__ = wrapperNative;
      //console.log(native, wrapperNative, p);
    } else {
      // TODO(sjmiles): support IE
    }
  }
}

// SECTION 4

function instantiate(inDefinition) {
  // 4.a.1. Create a new object that implements PROTOTYPE
  // 4.a.2. Let ELEMENT by this new object
  //
  // the custom element instantiation algorithm must also ensure that the
  // output is a valid DOM element with the proper wrapper in place.
  //
  return upgrade(domCreateElement(inDefinition.tag), inDefinition);
}

function upgrade(inElement, inDefinition) {
  // make 'element' implement inDefinition.prototype
  implement(inElement, inDefinition);
  // some definitions specify an 'is' attribute
  if (inDefinition.is) {
    inElement.setAttribute('is', inDefinition.is);
  }
  // flag as upgraded
  inElement.__upgraded__ = true;
  // invoke lifecycle.created callbacks
  created(inElement, inDefinition);
  // OUTPUT
  return inElement;
}

function implement(inElement, inDefinition) {
  // prototype swizzling is best
  if (Object.__proto__) {
    inElement.__proto__ = inDefinition.prototype;
  }
  // TODO(sjmiles): below here is feature detected, but really it's
  // just for IE
  else {
    // where above we can re-acquire inPrototype via
    // getPrototypeOf(Element), we cannot do so when
    // we use mixin, so we install a magic reference
    if (!Object.__proto__) {
      inElement.__proto__ = inDefinition.prototype;
    }
    customMixin(inElement, inDefinition.prototype, inDefinition.native);
  }
}

if (!console.group) {
  console.group = function(m) {console.log("[group] " + m);};
  console.groupEnd = function() {console.log("[end]");};
}

function customMixin(inTarget, inSrc, inNative) {
  //console.group(inTarget.localName);
  // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
  // any property. This set should be precalculated. We also need to
  // consider this for supporting 'super'.
  var used = {};
  // start with inSrc
  var p = inSrc;
  // sometimes the default is HTMLUnknownElement.prototype instead of
  // HTMLElement.prototype, so we add a test
  // the idea is to avoid mixing in native prototypes, so adding
  // the second test is WLOG
  while (p !== inNative && p !== HTMLUnknownElement.prototype) {
    //console.group('proto');
    var keys = Object.getOwnPropertyNames(p);
    for (var i=0, k; k=keys[i]; i++) {
      if (!used[k]) {
        //console.log(k);
        Object.defineProperty(inTarget, k,
            Object.getOwnPropertyDescriptor(p, k));
        used[k] = 1;
      }
    }
    //console.groupEnd();
    p = Object.getPrototypeOf(p);
  }
  //console.groupEnd();
}

function created(inElement, inDefinition) {
  var readyCallback = inDefinition.lifecycle.readyCallback ||
      inElement.readyCallback;
  if (readyCallback) {
    readyCallback.call(inElement);
  }
}

var registry = {};
var registrySlctr = '';

function registerDefinition(inName, inDefinition) {
  registry[inName] = inDefinition;
  registrySlctr += (registrySlctr ? ',' : '');
  if (inDefinition.extends) {
    registrySlctr += inDefinition.tag + '[is=' + inDefinition.is + '],';
  }
  registrySlctr += inName;
}

function generateConstructor(inDefinition) {
  return function() {
    return instantiate(inDefinition);
  };
}

function createElement(inTag) {
  var definition = registry[inTag];
  if (definition) {
    return new definition.ctor();
  }
  return domCreateElement(inTag);
}

/**
 * Upgrade an element to a custom element. Upgrading an element
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElement
 * @param {Element} inElement The element to upgrade.
 * @return {Element} The upgraded element.
 */
function upgradeElement(inElement) {
  if (!inElement.__upgraded__) {
    var type = inElement.getAttribute('is') || inElement.localName;
    var definition = registry[type];
    return definition && upgrade(inElement, definition);
  }
}

/**
 * Upgrade all elements under `inRoot` that match selector `inSlctr`.
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElements
 * @param {Node} inRoot The root of the DOM subtree in which elements are to
 *  be upgraded.
 * @param {String} [inSlctr] An optional selector for matching specific
 * elements, otherwise all register element types are upgraded.
 */
function upgradeElements(inRoot, inSlctr) {
  var slctr = inSlctr || registrySlctr;
  if (slctr) {
    var root = inRoot || document;
    forEach(root.querySelectorAll(slctr), upgradeElement);
  }
}

// utilities

var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);

// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
  var obj = inObj || {};
  for (var i = 1; i < arguments.length; i++) {
    var p = arguments[i];
    // TODO(sjmiles): on IE we are using mixin
    // to generate custom element instances, as we have
    // no way to alter element prototypes after creation
    // (nor a way to create an element with a custom prototype)
    // however, prototype sources (inSource) are ultimately
    // chained to a native prototype (HTMLElement or inheritor)
    // and trying to copy HTMLElement properties to itself throwss
    // in IE
    // we don't actually want to copy those properties anyway, but I
    // can't find a way to determine if a prototype is a native
    // or custom inheritor of HTMLElement
    // ad hoc solution is to simply stop at the first exception
    // an alternative exists if we have a tagName hint: then we can
    // work out where the native objects are in the prototype chain
    try {
      for (var n in p) {
        copyProperty(n, p, obj);
      }
    } catch(x) {
      //console.log(x);
    }
  }
  return obj;
}

// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
  var pd = getPropertyDescriptor(inSource, inName);
  Object.defineProperty(inTarget, inName, pd);
}

// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
  if (inObject) {
    var pd = Object.getOwnPropertyDescriptor(inObject, inName);
    return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
  }
}

function watchDOM(inRoot) {
  var mo = window.MutationObserver || window.WebKitMutationObserver;
  if (mo) {
    var observer = new mo(function(mutations) {
      mutations.forEach(function(mx) {
        if (mx.type == 'childList') {
          forEach(mx.addedNodes, function(n) {
            // this node may need upgrade (if so, subtree is upgraded here)
            if (!upgradeElement(n)) {
              // or maybe not, but then maybe the subtree needs upgrade
              upgradeElements(n);
            }
          });
        }
      })
    });
    observer.observe(inRoot, {childList: true});
    return observer;
  }
}

// capture native createElement before we override it
var domCreateElement = document.createElement.bind(document);

// exports

document.register = document.webkitRegister || document.register;

if (!document.register || flags.register !== 'native') {
  document.register = register;
  document.createElement = createElement; // override
  document.upgradeElement = upgradeElement;
  document.upgradeElements = upgradeElements;
  document.watchDOM = watchDOM;
} else {
  var nop = function() {};
  document.upgradeElement = nop;
  document.upgradeElements = nop;
  document.watchDOM = nop;
}

// TODO(sjmiles): temporary, control scope better
window.mixin = mixin;

})();
<commit_msg>enforce invariant that child nodes are upgraded before ready<commit_after>/*
 * Copyright 2013 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * Implements `document.register`
 * @module CustomElements
*/

/**
 * Polyfilled extensions to the `document` object.
 * @class Document
*/

(function() {

/**
 * Registers a custom tag name with the document.
 *
 * When a registered element is created, a `readyCallback` method is called
 * in the scope of the element. The `readyCallback` method can be specified on
 * either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
 * precedence.
 *
 * @method register
 * @param {String} inName The tag name to register. Must include a dash ('-'),
 *    for example 'x-component'.
 * @param {Object} inOptions
 *    @param {String} [inOptions.extends]
 *      (_off spec_) Tag name of an element to extend (or blank for a new
 *      element). This parameter is not part of the specification, but instead
 *      is a hint for the polyfill because the extendee is difficult to infer.
 *      Remember that the input prototype must chain to the extended element's
 *      prototype (or HTMLElement.prototype) regardless of the value of
 *      `extends`.
 *    @param {Object} inOptions.prototype The prototype to use for the new
 *      element. The prototype must inherit from HTMLElement.
 *    @param {Object} [inOptions.lifecycle]
 *      Callbacks that fire at important phases in the life of the custom
 *      element.
 *
 * @example
 *      FancyButton = document.register("fancy-button", {
 *        extends: 'button',
 *        prototype: Object.create(HTMLButtonElement.prototype, {
 *          readyCallback: {
 *            value: function() {
 *              console.log("a fancy-button was created",
 *            }
 *          }
 *        })
 *      });
 * @return {Function} Constructor for the newly registered type.
 */
function register(inName, inOptions) {
  //console.warn('document.register("' + inName + '", ', inOptions, ')');
  // construct a defintion out of options
  // TODO(sjmiles): probably should clone inOptions instead of mutating it
  var definition = inOptions || {};
  if (!inName) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Name argument must not be empty');
  }
  // record name
  definition.name = inName;
  // must have a prototype, default to an extension of HTMLElement
  // TODO(sjmiles): probably should throw if no prototype, check spec
  if (!definition.prototype) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Options missing required prototype property');
  }
  // ensure a lifecycle object so we don't have to null test it
  definition.lifecycle = definition.lifecycle || {};
  // build a list of ancestral custom elements (for native base detection)
  // TODO(sjmiles): we used to need to store this, but current code only
  // uses it in 'resolveTagName': it should probably be inlined
  definition.ancestry = ancestry(definition.extends);
  // extensions of native specializations of HTMLElement require localName
  // to remain native, and use secondary 'is' specifier for extension type
  resolveTagName(definition);
  // some platforms require modifications to the user-supplied prototype
  // chain
  resolvePrototypeChain(definition);
  // 7.1.5: Register the DEFINITION with DOCUMENT
  registerDefinition(inName, definition);
  // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
  // 7.1.8. Return the output of the previous step.
  definition.ctor = generateConstructor(definition);
  definition.ctor.prototype = definition.prototype;
  // blanket upgrade (?)
  document.upgradeElements();
  return definition.ctor;
}

function ancestry(inExtends) {
  var extendee = registry[inExtends];
  if (extendee) {
    return ancestry(extendee.extends).concat([extendee]);
  }
  return [];
}

function resolveTagName(inDefinition) {
  // if we are explicitly extending something, that thing is our
  // baseTag, unless it represents a custom component
  var baseTag = inDefinition.extends;
  // if our ancestry includes custom components, we only have a
  // baseTag if one of them does
  for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
    baseTag = a.is && a.tag;
  }
  // our tag is our baseTag, if it exists, and otherwise just our name
  inDefinition.tag = baseTag || inDefinition.name;
  if (baseTag) {
    // if there is a base tag, use secondary 'is' specifier
    inDefinition.is = inDefinition.name;
  }
}

function resolvePrototypeChain(inDefinition) {
  // TODO(sjmiles): contains ShadowDOM polyfill pollution
  // if we don't support __proto__ we need to locate the native level
  // prototype for precise mixing in
  // we also need native prototype so we can replace it with a wrapper
  // prototype if necessary
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)
        || !Object.__proto__) {
    if (inDefinition.is) {
      // for non-trivial extensions, work out both prototypes
      var inst = domCreateElement(inDefinition.tag);
      var wrapperNative = Object.getPrototypeOf(inst);
      if (window.WrapperElement) {
        inst = unwrap(inst);
      }
      var native = Object.getPrototypeOf(inst);
    } else {
      // otherwise, use the defaults
      native = HTMLElement.prototype;
      if (window.WrapperElement) {
        wrapperNative = WrapperHTMLUnknownElement.prototype;
      }
    }
  }
  // cache this in case of mixin
  inDefinition.native = native;
  // under ShadowDOM polyfill we need to use Wrapper prototype chain instead
  // of native DOM
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)) {
    if (Object.__proto__) {
      // search and replace 'native' protoype with 'wrapperNative'
      var p = inDefinition.prototype, pp;
      while (true) {
        pp = Object.getPrototypeOf(p);
        if (pp === native || pp == HTMLUnknownElement.prototype) {
          break;
        }
        p = pp;
      }
      p.__proto__ = wrapperNative;
      //console.log(native, wrapperNative, p);
    } else {
      // TODO(sjmiles): support IE
    }
  }
}

// SECTION 4

function instantiate(inDefinition) {
  // 4.a.1. Create a new object that implements PROTOTYPE
  // 4.a.2. Let ELEMENT by this new object
  //
  // the custom element instantiation algorithm must also ensure that the
  // output is a valid DOM element with the proper wrapper in place.
  //
  return upgrade(domCreateElement(inDefinition.tag), inDefinition);
}

function upgrade(inElement, inDefinition) {
  // make 'element' implement inDefinition.prototype
  implement(inElement, inDefinition);
  // some definitions specify an 'is' attribute
  if (inDefinition.is) {
    inElement.setAttribute('is', inDefinition.is);
  }
  // flag as upgraded
  inElement.__upgraded__ = true;
  // invoke lifecycle.created callbacks
  created(inElement, inDefinition);
  // we require child nodes be upgraded before ready
  upgradeElements(inElement);
  // OUTPUT
  return inElement;
}

function implement(inElement, inDefinition) {
  // prototype swizzling is best
  if (Object.__proto__) {
    inElement.__proto__ = inDefinition.prototype;
  }
  // TODO(sjmiles): below here is feature detected, but really it's
  // just for IE
  else {
    // where above we can re-acquire inPrototype via
    // getPrototypeOf(Element), we cannot do so when
    // we use mixin, so we install a magic reference
    if (!Object.__proto__) {
      inElement.__proto__ = inDefinition.prototype;
    }
    customMixin(inElement, inDefinition.prototype, inDefinition.native);
  }
}

if (!console.group) {
  console.group = function(m) {console.log("[group] " + m);};
  console.groupEnd = function() {console.log("[end]");};
}

function customMixin(inTarget, inSrc, inNative) {
  //console.group(inTarget.localName);
  // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
  // any property. This set should be precalculated. We also need to
  // consider this for supporting 'super'.
  var used = {};
  // start with inSrc
  var p = inSrc;
  // sometimes the default is HTMLUnknownElement.prototype instead of
  // HTMLElement.prototype, so we add a test
  // the idea is to avoid mixing in native prototypes, so adding
  // the second test is WLOG
  while (p !== inNative && p !== HTMLUnknownElement.prototype) {
    //console.group('proto');
    var keys = Object.getOwnPropertyNames(p);
    for (var i=0, k; k=keys[i]; i++) {
      if (!used[k]) {
        //console.log(k);
        Object.defineProperty(inTarget, k,
            Object.getOwnPropertyDescriptor(p, k));
        used[k] = 1;
      }
    }
    //console.groupEnd();
    p = Object.getPrototypeOf(p);
  }
  //console.groupEnd();
}

function created(inElement, inDefinition) {
  var readyCallback = inDefinition.lifecycle.readyCallback ||
      inElement.readyCallback;
  if (readyCallback) {
    readyCallback.call(inElement);
  }
}

var registry = {};
var registrySlctr = '';

function registerDefinition(inName, inDefinition) {
  registry[inName] = inDefinition;
  registrySlctr += (registrySlctr ? ',' : '');
  if (inDefinition.extends) {
    registrySlctr += inDefinition.tag + '[is=' + inDefinition.is + '],';
  }
  registrySlctr += inName;
}

function generateConstructor(inDefinition) {
  return function() {
    return instantiate(inDefinition);
  };
}

function createElement(inTag) {
  var definition = registry[inTag];
  if (definition) {
    return new definition.ctor();
  }
  return domCreateElement(inTag);
}

/**
 * Upgrade an element to a custom element. Upgrading an element
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElement
 * @param {Element} inElement The element to upgrade.
 * @return {Element} The upgraded element.
 */
function upgradeElement(inElement) {
  if (!inElement.__upgraded__) {
    var type = inElement.getAttribute('is') || inElement.localName;
    var definition = registry[type];
    return definition && upgrade(inElement, definition);
  }
}

/**
 * Upgrade all elements under `inRoot` that match selector `inSlctr`.
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElements
 * @param {Node} inRoot The root of the DOM subtree in which elements are to
 *  be upgraded.
 * @param {String} [inSlctr] An optional selector for matching specific
 * elements, otherwise all register element types are upgraded.
 */
function upgradeElements(inRoot, inSlctr) {
  var slctr = inSlctr || registrySlctr;
  if (slctr) {
    var root = inRoot || document;
    forEach(root.querySelectorAll(slctr), upgradeElement);
  }
}

// utilities

var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);

// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
  var obj = inObj || {};
  for (var i = 1; i < arguments.length; i++) {
    var p = arguments[i];
    // TODO(sjmiles): on IE we are using mixin
    // to generate custom element instances, as we have
    // no way to alter element prototypes after creation
    // (nor a way to create an element with a custom prototype)
    // however, prototype sources (inSource) are ultimately
    // chained to a native prototype (HTMLElement or inheritor)
    // and trying to copy HTMLElement properties to itself throwss
    // in IE
    // we don't actually want to copy those properties anyway, but I
    // can't find a way to determine if a prototype is a native
    // or custom inheritor of HTMLElement
    // ad hoc solution is to simply stop at the first exception
    // an alternative exists if we have a tagName hint: then we can
    // work out where the native objects are in the prototype chain
    try {
      for (var n in p) {
        copyProperty(n, p, obj);
      }
    } catch(x) {
      //console.log(x);
    }
  }
  return obj;
}

// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
  var pd = getPropertyDescriptor(inSource, inName);
  Object.defineProperty(inTarget, inName, pd);
}

// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
  if (inObject) {
    var pd = Object.getOwnPropertyDescriptor(inObject, inName);
    return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
  }
}

function watchDOM(inRoot) {
  var mo = window.MutationObserver || window.WebKitMutationObserver;
  if (mo) {
    var observer = new mo(function(mutations) {
      mutations.forEach(function(mx) {
        if (mx.type == 'childList') {
          forEach(mx.addedNodes, function(n) {
            // this node may need upgrade (if so, subtree is upgraded here)
            if (!upgradeElement(n)) {
              // or maybe not, but then maybe the subtree needs upgrade
              upgradeElements(n);
            }
          });
        }
      })
    });
    observer.observe(inRoot, {childList: true});
    return observer;
  }
}

// capture native createElement before we override it
var domCreateElement = document.createElement.bind(document);

// exports

document.register = document.webkitRegister || document.register;

if (!document.register || flags.register !== 'native') {
  document.register = register;
  document.createElement = createElement; // override
  document.upgradeElement = upgradeElement;
  document.upgradeElements = upgradeElements;
  document.watchDOM = watchDOM;
} else {
  var nop = function() {};
  document.upgradeElement = nop;
  document.upgradeElements = nop;
  document.watchDOM = nop;
}

// TODO(sjmiles): temporary, control scope better
window.mixin = mixin;

})();
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import {
  Animated,
  TouchableOpacity
} from 'react-native';

import InnerButtonView from './InnerButtonView';

class AwesomeButton extends Component {
  static propTypes = {
    states: PropTypes.object.isRequired,
    buttonState: PropTypes.string,
    transitionDuration: PropTypes.number,
    spinnerColor: PropTypes.string,
    activeOpacity: PropTypes.number,
    labelStyle: PropTypes.object,
    backgroundStyle: PropTypes.object,
  }

  constructor(props) {
    super(props);
    this.state = {
      backgroundColor: new Animated.Value(0),
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      backgroundColor: new Animated.Value(0),
      startColor: this.props.states[this.props.buttonState].backgroundColor,
      endColor: this.props.states[nextProps.buttonState].backgroundColor
    });
  }

  componentDidUpdate() {
    this.startAnimation();
  }

  startAnimation() {
    Animated.timing(this.state.backgroundColor,
      {
        toValue: 1,
        duration: this.props.transitionDuration
      }
    ).start();
  }

  render() {
    const bgColor = this.state.backgroundColor.interpolate({
      inputRange: [0, 1],
      outputRange: [this.state.startColor, this.state.endColor]
    });

    const AnimatedView =
      (<Animated.View style={[this.props.backgroundStyle, { backgroundColor: bgColor }]}>
        <InnerButtonView
          currentStateObject={this.props.states[this.props.buttonState]}
          labelStyle={this.props.labelStyle}
          spinnerColor={this.props.spinnerColor}
        />
      </Animated.View>);

    if (this.props.states[this.props.buttonState].onPress) {
      return (
        <TouchableOpacity
          style={{ flex: 1 }}
          activeOpacity={this.props.activeOpacity}
          onPress={this.props.states[this.props.buttonState].onPress}
        >
          { AnimatedView }
        </TouchableOpacity>
      );
    }
    return { AnimatedView };
  }
}

AwesomeButton.defaultProps = {
  buttonState: 'default',
  backgroundStyle: {
    flex: 1,
    height: 40,
    backgroundColor: 'rgb(255, 0, 0)',
    borderRadius: 20
  },
  labelStyle: {
    color: '#FFFFFF'
  },
  spinnerColor: '#FFFFFF',
  transitionDuration: 250,
  activeOpacity: 0.92
};

export default AwesomeButton;

<commit_msg>Refactor<commit_after>import React, { Component, PropTypes } from 'react';
import {
  Animated,
  TouchableOpacity
} from 'react-native';

import InnerButtonView from './InnerButtonView';

class AwesomeButton extends Component {
  static propTypes = {
    states: PropTypes.object.isRequired,
    buttonState: PropTypes.string,
    transitionDuration: PropTypes.number,
    spinnerColor: PropTypes.string,
    activeOpacity: PropTypes.number,
    labelStyle: PropTypes.object,
    backgroundStyle: PropTypes.object,
  }

  constructor(props) {
    super(props);
    this.state = {
      backgroundColor: new Animated.Value(0),
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      backgroundColor: new Animated.Value(0),
      startColor: this.props.states[this.props.buttonState].backgroundColor,
      endColor: this.props.states[nextProps.buttonState].backgroundColor
    });
  }

  componentDidUpdate() {
    this.startAnimation();
  }

  startAnimation() {
    Animated.timing(this.state.backgroundColor,
      {
        toValue: 1,
        duration: this.props.transitionDuration
      }
    ).start();
  }

  render() {
    const bgColor = this.state.backgroundColor.interpolate({
      inputRange: [0, 1],
      outputRange: [this.state.startColor, this.state.endColor]
    });

    bgColor = 'rgb(232, 123, 123)';

    const AnimatedView =
      (<Animated.View style={[this.props.backgroundStyle, { backgroundColor: bgColor }]}>
        <InnerButtonView
          currentStateObject={this.props.states[this.props.buttonState]}
          labelStyle={this.props.labelStyle}
          spinnerColor={this.props.spinnerColor}
        />
      </Animated.View>);

    if (this.props.states[this.props.buttonState].onPress) {
      return (
        <TouchableOpacity
          style={{ flex: 1 }}
          activeOpacity={this.props.activeOpacity}
          onPress={this.props.states[this.props.buttonState].onPress}
        >
          { AnimatedView }
        </TouchableOpacity>
      );
    }
    return { AnimatedView };
  }
}

AwesomeButton.defaultProps = {
  buttonState: 'default',
  backgroundStyle: {
    flex: 1,
    height: 40,
    backgroundColor: 'rgb(255, 0, 0)',
    borderRadius: 20
  },
  labelStyle: {
    color: '#FFFFFF'
  },
  spinnerColor: '#FFFFFF',
  transitionDuration: 250,
  activeOpacity: 0.92
};

export default AwesomeButton;

<|endoftext|>


<commit_before>(function(root, factory) {
    if (typeof define === "function" && define.amd) {
        define([], factory);
    } else if (typeof module === "object" && module.exports) {
        module.exports = factory();
    } else {
        root.BaseViewModel = factory();
    }
}(this, function() {

    var uniqueId = 0,
        bodyEl = document.getElementsByTagName("body")[0],
        headEl = document.getElementsByTagName("head")[0];
    BaseViewModel = function(options) {
        return function() {

        };
        var moduleName = options.moduleName || 'module' + uniqueId++,
            controller = options.controller,
            div = document.createElement('div'),
            elements;

        // getting DOM
        div.innerHTML = options.html || '';
        elements = controller.prototype.elements = div.childNodes.length > 0 ? div.childNodes || div;

        return controller;
    };

    return BaseViewModel;
}));
<commit_msg>some changes<commit_after>(function(root, factory) {
    if (typeof define === "function" && define.amd) {
        define([], factory);
    } else if (typeof module === "object" && module.exports) {
        module.exports = factory();
    } else {
        root.Component = factory();
    }
}(this, function() {

    var uniqueId = 0,
        bodyEl = document.getElementsByTagName("body")[0],
        headEl = document.getElementsByTagName("head")[0];

    BaseViewModel = function(options) {
        var componentName = options.name || 'component-' + uniqueId++,

            controller = options.controller;

        return function() {

            var scope = this,
                div = document.createElement('div');

            // getting DOM
            div.innerHTML = options.html || '';

            var elements = scope.$elements = div.childNodes.length > 0 ? div.childNodes || div;

        };
    };

    return BaseViewModel;
}));
<|endoftext|>