<commit_before>require('../../../src/common/iServer/Route');

describe('testRoute', function () {
    var originalTimeout;
    beforeEach(function () {
        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
        routeLocatorEventArgsSystem = null,
            serviceFailedEventArgsSystem = null;
    });
    afterEach(function () {
        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
    });

    it('init', function () {
        var lineString = new SuperMap.Geometry.LineString();
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "LINESTRING"
        };
        var route = new SuperMap.Route([lineString], options);
        expect(route).not.toBeNull();
        expect(route.id).toBe(1);
        expect(route.length).toBe(1024);
        expect(route.maxM).toBe(1000);
        expect(route.minM).toBe(100);
        expect(route.type).toBe("LINESTRING");
    });

    it('toJson', function () {
        var lineString = new SuperMap.Geometry.LineString();
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "style": "",
            "center": 5000,
            "type": "LINESTRING",
            "parts": [1]
        };
        var route = new SuperMap.Route([lineString], options);
        var tojson = route.toJson();
        expect(tojson).not.toBeNull();
        expect(typeof (tojson)).toBe('string');
        expect(tojson).toBe('{"id":1,"center":5000,"style":,"length":1024,"maxM":1000,"minM":100,"type":"LINESTRING","parts":[1],"points":[]}');
    });

    it('toJson_withComponents', function (done) {
        try {
            var points = [new SuperMap.Geometry.Point(4933.319287022352, -3337.3849141502124),
                new SuperMap.Geometry.Point(4960.9674060199022, -3349.3316322355736),
                new SuperMap.Geometry.Point(5006.0235999418364, -3358.8890067038628),
                new SuperMap.Geometry.Point(5075.3145648369318, -3378.0037556404409),
                new SuperMap.Geometry.Point(5305.19551436013, -3376.9669111768926)];
            var roadLine = new SuperMap.Geometry.LineString(points);
            var options = {
                "id": 1,
                "length": 1024,
                "maxM": 1000,
                "minM": 100,
                "type": "LINESTRING",
                "parts": [1]
            };
            var route = new SuperMap.Route([roadLine], options);
            var tojson = route.toJson();
            expect(tojson).not.toBeNull();
            expect(typeof (tojson)).toBe('string');
        } catch (e) {
            expect(false).toBeFalsy();
            console.log("toJson_withEmptyComponents" + e.name + ":" + e.message);
            done();
        }
    });

    it('destroy', function () {
        var points = [new SuperMap.Geometry.Point(4933.319287022352, -3337.3849141502124),
            new SuperMap.Geometry.Point(4960.9674060199022, -3349.3316322355736),
            new SuperMap.Geometry.Point(5006.0235999418364, -3358.8890067038628),
            new SuperMap.Geometry.Point(5075.3145648369318, -3378.0037556404409),
            new SuperMap.Geometry.Point(5305.19551436013, -3376.9669111768926)];
        var roadLine = new SuperMap.Geometry.LineString(points);
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "LINESTRING",
            "parts": [1]
        };
        var route = new SuperMap.Route([roadLine], options);
        route.destroy();
        expect(route.id).toBeNull();
        expect(route.center).toBeNull();
        expect(route.style).toBeNull();
        expect(route.length).toBe(0);
        expect(route.maxM).toBeNull();
        expect(route.minM).toBeNull();
        expect(route.type).toBeNull();
        expect(route.parts).toBeNull();
        expect(route.components).toBeNull();
        expect(route.componentTypes).toBeNull();
    });

    it('fromJson', function () {
        var jsonobj = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "POINT",
            "points": [{
                "measure": 0,
                "y": -6674.466867067764,
                "x": 3817.3527876130133
            }],
            "parts": [1]
        };
        var result = SuperMap.Route.fromJson(jsonobj);
        expect(result).not.toBeNull();
        expect(result instanceof SuperMap.Route).toBeTruthy();
        expect(result.id).toBe(1);
        expect(result.length).toBe(1024);
        expect(result.maxM).toBe(1000);
        expect(result.minM).toBe(100);
        expect(result.type).toBe("POINT");
        expect(result.parts).toBe(jsonobj.parts);
    });

});<commit_msg>fixed CI reviewBy zhurc<commit_after>require('../../../src/common/iServer/Route');

describe('testRoute', function () {
    var originalTimeout;
    beforeEach(function () {
        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
        routeLocatorEventArgsSystem = null,
            serviceFailedEventArgsSystem = null;
    });
    afterEach(function () {
        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
    });

    it('init', function () {
        var lineString = new SuperMap.Geometry.LineString();
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "LINESTRING"
        };
        var route = new SuperMap.Route([lineString], options);
        expect(route).not.toBeNull();
        expect(route.id).toBe(1);
        expect(route.length).toBe(1024);
        expect(route.maxM).toBe(1000);
        expect(route.minM).toBe(100);
        expect(route.type).toBe("LINESTRING");
    });

    it('toJson', function () {
        var lineString = new SuperMap.Geometry.LineString();
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "style": "",
            "center": 5000,
            "type": "LINESTRING",
            "parts": [1]
        };
        var route = new SuperMap.Route([lineString], options);
        var tojson = route.toJson();
        expect(tojson).not.toBeNull();
        expect(typeof (tojson)).toBe('string');
        expect(tojson).toBe('{"id":1,"center":5000,"style":,"length":1024,"maxM":1000,"minM":100,"type":"LINESTRING","parts":[1],"points":[]}');
    });

    it('toJson_withComponents', function (done) {
        try {
            var points = [new SuperMap.Geometry.Point(4933.319287022352, -3337.3849141502124),
                new SuperMap.Geometry.Point(4960.9674060199022, -3349.3316322355736),
                new SuperMap.Geometry.Point(5006.0235999418364, -3358.8890067038628),
                new SuperMap.Geometry.Point(5075.3145648369318, -3378.0037556404409),
                new SuperMap.Geometry.Point(5305.19551436013, -3376.9669111768926)];
            var roadLine = new SuperMap.Geometry.LineString(points);
            var options = {
                "id": 1,
                "length": 1024,
                "maxM": 1000,
                "minM": 100,
                "type": "LINESTRING",
                "parts": [1]
            };
            var route = new SuperMap.Route([roadLine], options);
            var tojson = route.toJson();
            expect(tojson).not.toBeNull();
            expect(typeof (tojson)).toBe('string');
        } catch (e) {
            expect(false).toBeFalsy();
            console.log("toJson_withEmptyComponents" + e.name + ":" + e.message);
            done();
        }
    });

    it('destroy', function () {
        var points = [new SuperMap.Geometry.Point(4933.319287022352, -3337.3849141502124),
            new SuperMap.Geometry.Point(4960.9674060199022, -3349.3316322355736),
            new SuperMap.Geometry.Point(5006.0235999418364, -3358.8890067038628),
            new SuperMap.Geometry.Point(5075.3145648369318, -3378.0037556404409),
            new SuperMap.Geometry.Point(5305.19551436013, -3376.9669111768926)];
        var roadLine = new SuperMap.Geometry.LineString(points);
        var options = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "LINESTRING",
            "parts": [1]
        };
        var route = new SuperMap.Route([roadLine], options);
        route.destroy();
        expect(route.id).toBeNull();
        expect(route.center).toBeNull();
        expect(route.style).toBeNull();
        expect(route.length).toBeNull();
        expect(route.maxM).toBeNull();
        expect(route.minM).toBeNull();
        expect(route.type).toBeNull();
        expect(route.parts).toBeNull();
        expect(route.components).toBeNull();
        expect(route.componentTypes).toBeNull();
    });

    it('fromJson', function () {
        var jsonobj = {
            "id": 1,
            "length": 1024,
            "maxM": 1000,
            "minM": 100,
            "type": "POINT",
            "points": [{
                "measure": 0,
                "y": -6674.466867067764,
                "x": 3817.3527876130133
            }],
            "parts": [1]
        };
        var result = SuperMap.Route.fromJson(jsonobj);
        expect(result).not.toBeNull();
        expect(result instanceof SuperMap.Route).toBeTruthy();
        expect(result.id).toBe(1);
        expect(result.length).toBe(1024);
        expect(result.maxM).toBe(1000);
        expect(result.minM).toBe(100);
        expect(result.type).toBe("POINT");
        expect(result.parts).toBe(jsonobj.parts);
    });

});<|endoftext|>


<commit_before>const chai = require('chai')
chai.use(require('chai-as-promised'))
const expect = chai.expect
const inquirer = require('inquirer-test')
const run = inquirer
const ENTER = inquirer.ENTER
const DOWN = inquirer.DOWN
const fs = require('fs')
const path = require('path')
const dateFormat = require('dateformat')
const outputFile = 'OWASP_Juice_Shop.' + dateFormat(new Date(), 'yyyy-mm-dd') + '.zip'
const desiredCTFdOutputFile = './output.zip'
const desiredFBCTFOutputFile = './output.json'
const configFile = 'config.yml'
const util = require('util')
const execFile = util.promisify(require('child_process').execFile)

const juiceShopCtfCli = [path.join(__dirname, '../../bin/juice-shop-ctf.js')]

function cleanup () {
  if (fs.existsSync(outputFile)) {
    fs.unlinkSync(outputFile)
  }
  if (fs.existsSync(configFile)) {
    fs.unlinkSync(configFile)
  }
  if (fs.existsSync(desiredCTFdOutputFile)) {
    fs.unlinkSync(desiredCTFdOutputFile)
  }
  if (fs.existsSync(desiredFBCTFOutputFile)) {
    fs.unlinkSync(desiredFBCTFOutputFile)
  }
}

describe('juice-shop-ctf', () => {
  beforeEach(cleanup)
  after(cleanup)

  it('should accept defaults for all input questions', function () {
    this.timeout(20000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, ENTER], 2000)).to
      .eventually.match(/ZIP-archive written to /i).and
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? No text hints/i).and
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? No hint URLs/i)
  })

  it('should insert free hints when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, DOWN, ENTER, ENTER], 1500)).to
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? Free text hints/i)
  })

  it('should insert paid hints when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, DOWN, DOWN, ENTER, ENTER], 1500)).to
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? Paid text hints/i)
  })

  it('should insert free hint URLs when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, DOWN, ENTER], 1500)).to
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? Free hint URLs/i)
  })

  it('should insert paid hint URLs when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, DOWN, DOWN, ENTER], 1500)).to
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? Paid hint URLs/i)
  })

  it('should fail on invalid Juice Shop URL', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, 'localhorst', ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to fetch challenges from API!/i)
  })

  it('should fail on invalid ctf.key URL', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, 'httpx://invalid/ctf-key', ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to fetch secret key from URL!/i)
  })

  it('should generate a fbctf export when choosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [DOWN, ENTER, ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/CTF Framework the generated files should be for\? FBCTF/i)
  })

  it('should fail when output file cannot be written', function () {
    this.timeout(15000)
    fs.openSync(outputFile, 'w', 0)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to write output to file!/i)
  })

  it('should accept a config file', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/ZIP-archive written to /i)
  })

  it('should fail when the config file is unparsable', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/can not read /i)
  })

  it('should fail when the config file contains invalid values', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: invalidValue`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/"insertHintUrls" must be one of /i)
  })

  it('should write the output file to the specified location', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile, '--output', desiredCTFdOutputFile])
      .then(() => fs.existsSync(desiredCTFdOutputFile))).to
      .eventually.equal(true)
  })

  it.only('should be possible to create a fbctf export with a config file', function () {
    fs.writeFileSync(configFile, `
ctfFramework: FBCTF
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
countryMapping: https://raw.githubusercontent.com/bkimminich/juice-shop/master/config/fbctf.yml
insertHints: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile, '--output', desiredFBCTFOutputFile])
      .then(() => fs.existsSync(desiredFBCTFOutputFile))).to
      .eventually.equal(true)
  })
})
<commit_msg>Fixed standard formatting<commit_after>const chai = require('chai')
chai.use(require('chai-as-promised'))
const expect = chai.expect
const inquirer = require('inquirer-test')
const run = inquirer
const ENTER = inquirer.ENTER
const DOWN = inquirer.DOWN
const fs = require('fs')
const path = require('path')
const dateFormat = require('dateformat')
const outputFile = 'OWASP_Juice_Shop.' + dateFormat(new Date(), 'yyyy-mm-dd') + '.zip'
const desiredCTFdOutputFile = './output.zip'
const desiredFBCTFOutputFile = './output.json'
const configFile = 'config.yml'
const util = require('util')
const execFile = util.promisify(require('child_process').execFile)

const juiceShopCtfCli = [path.join(__dirname, '../../bin/juice-shop-ctf.js')]

function cleanup () {
  if (fs.existsSync(outputFile)) {
    fs.unlinkSync(outputFile)
  }
  if (fs.existsSync(configFile)) {
    fs.unlinkSync(configFile)
  }
  if (fs.existsSync(desiredCTFdOutputFile)) {
    fs.unlinkSync(desiredCTFdOutputFile)
  }
  if (fs.existsSync(desiredFBCTFOutputFile)) {
    fs.unlinkSync(desiredFBCTFOutputFile)
  }
}

describe('juice-shop-ctf', () => {
  beforeEach(cleanup)
  after(cleanup)

  it('should accept defaults for all input questions', function () {
    this.timeout(20000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, ENTER], 2000)).to
      .eventually.match(/ZIP-archive written to /i).and
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? No text hints/i).and
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? No hint URLs/i)
  })

  it('should insert free hints when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, DOWN, ENTER, ENTER], 1500)).to
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? Free text hints/i)
  })

  it('should insert paid hints when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, DOWN, DOWN, ENTER, ENTER], 1500)).to
      .eventually.match(/Insert a text hint along with each CTFd Challenge\? Paid text hints/i)
  })

  it('should insert free hint URLs when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, DOWN, ENTER], 1500)).to
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? Free hint URLs/i)
  })

  it('should insert paid hint URLs when chosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, DOWN, DOWN, ENTER], 1500)).to
      .eventually.match(/Insert a hint URL along with each CTFd Challenge\? Paid hint URLs/i)
  })

  it('should fail on invalid Juice Shop URL', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, 'localhorst', ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to fetch challenges from API!/i)
  })

  it('should fail on invalid ctf.key URL', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, 'httpx://invalid/ctf-key', ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to fetch secret key from URL!/i)
  })

  it('should generate a fbctf export when choosen', function () {
    this.timeout(15000)
    return expect(run(juiceShopCtfCli, [DOWN, ENTER, ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/CTF Framework the generated files should be for\? FBCTF/i)
  })

  it('should fail when output file cannot be written', function () {
    this.timeout(15000)
    fs.openSync(outputFile, 'w', 0)
    return expect(run(juiceShopCtfCli, [ENTER, ENTER, ENTER, ENTER, ENTER], 1500)).to
      .eventually.match(/Failed to write output to file!/i)
  })

  it('should accept a config file', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/ZIP-archive written to /i)
  })

  it('should fail when the config file is unparsable', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/can not read /i)
  })

  it('should fail when the config file contains invalid values', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: invalidValue`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile]).then(obj => obj.stdout)).to
      .eventually.match(/"insertHintUrls" must be one of /i)
  })

  it('should write the output file to the specified location', function () {
    fs.writeFileSync(configFile, `
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
insertHints: paid
insertHintUrls: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile, '--output', desiredCTFdOutputFile])
      .then(() => fs.existsSync(desiredCTFdOutputFile))).to
      .eventually.equal(true)
  })

  it.only('should be possible to create a fbctf export with a config file', function () {
    fs.writeFileSync(configFile, `
ctfFramework: FBCTF
juiceShopUrl: https://juice-shop.herokuapp.com
ctfKey: https://raw.githubusercontent.com/bkimminich/juice-shop/master/ctf.key
countryMapping: https://raw.githubusercontent.com/bkimminich/juice-shop/master/config/fbctf.yml
insertHints: paid`)

    this.timeout(15000)
    return expect(execFile('npx', [juiceShopCtfCli[0], '--config', configFile, '--output', desiredFBCTFOutputFile])
      .then(() => fs.existsSync(desiredFBCTFOutputFile))).to
      .eventually.equal(true)
  })
})
<|endoftext|>


<commit_before>import {SetterObserver} from '../src/property-observation';
import {DirtyCheckProperty} from '../src/dirty-checking';
import {
  ValueAttributeObserver,
  XLinkAttributeObserver,
  DataAttributeObserver,
  StyleObserver
} from '../src/element-observation';
import {
  createElement,
  checkDelay,
  createObserverLocator,
  executeSharedPropertyObserverTests
} from './shared';
import {initialize} from 'aurelia-pal-browser';

function createSvgUseElement() {
  var html = [
    '<svg>',
    '  <defs>',
    '    <g id="shape1">',
    '      <rect x="50" y="50" width="50" height="50" />',
    '    </g>',
    '    <g id="shape2">',
    '      <circle cx="50" cy="50" r="50" />',
    '    </g>',
    '  </defs>',
    '  <use xlink:href="#shape1" x="50" y="50" foo:bar="baz" />',
    '</svg>'].join('\n');
  return createElement(html).lastElementChild;
}

describe('element observation', () => {
  var locator;
  beforeAll(() => {
    initialize();
    locator = createObserverLocator();
  });

  it('observes xlink namespaced attributes', () => {
    var el = createSvgUseElement(),
        observer = locator.getObserver(el, 'xlink:href');
    expect(observer instanceof XLinkAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "xlink:href" property is not supported.'));
    expect(observer.getValue()).toBe('#shape1');
    observer.setValue('#shape2');
    expect(observer.getValue()).toBe('#shape2');
  });

  it('observes non-xlink namespaced attributes', () => {
    var el = createSvgUseElement(),
        observer = locator.getObserver(el, 'foo:bar');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "foo:bar" property is not supported.'));
    expect(observer.getValue()).toBe('baz');
    observer.setValue('qux');
    expect(observer.getValue()).toBe('qux');
  });

  it('observes data-* attributes on Elements', () => {
    var el = createElement('

'),
        observer = locator.getObserver(el, 'data-foo');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "data-foo" property is not supported.'));
    expect(observer.getValue()).toBe('bar');
    observer.setValue('baz');
    expect(observer.getValue()).toBe('baz');
  });

  it('observes aria-* attributes on Elements', () => {
    var el = createElement('

'),
        observer = locator.getObserver(el, 'aria-hidden');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "aria-hidden" property is not supported.'));
    expect(observer.getValue()).toBe('true');
    observer.setValue('false');
    expect(observer.getValue()).toBe('false');
  });

  it('observes data-* attributes on SVGElements', () => {
    var el = createElement('<svg data-foo="bar"></svg>'),
        observer = locator.getObserver(el, 'data-foo');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "data-foo" property is not supported.'));
    expect(observer.getValue()).toBe('bar');
    observer.setValue('baz');
    expect(observer.getValue()).toBe('baz');
  });

  it('observes aria-* attributes on SVGElements', () => {
    var el = createElement('<svg aria-hidden="true"></svg>'),
        observer = locator.getObserver(el, 'aria-hidden');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "aria-hidden" property is not supported.'));
    expect(observer.getValue()).toBe('true');
    observer.setValue('false');
    expect(observer.getValue()).toBe('false');
  });

  it('value attributes', (done) => {
    var cases = [
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: 'bar' },
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: undefined, expected: '' },
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: null, expected: '' },

        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: 'bar' },
        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: undefined, expected: '' },
        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: null, expected: '' },
      ],
      remaining = cases.length;
    cases.forEach(test => {
      var el = createElement(test.tag),
          observer = locator.getObserver(el, test.attr),
          callback = jasmine.createSpy('callback'),
          expected = test.hasOwnProperty('expected') ? test.expected : test.new;
      observer.subscribe(callback);
      expect(observer instanceof ValueAttributeObserver).toBe(true);
      expect(observer.getValue()).toBe(test.old);
      observer.setValue(test.new);
      expect(observer.getValue()).toBe(expected);
      setTimeout(() => {
        expect(callback).toHaveBeenCalledWith(expected, test.old);
        observer.unsubscribe(callback);
        remaining--;
        if (remaining === 0) {
          done();
        }
      }, 0);
    });
  });

  it('native properties of Elements and SVGElements', () => {
    var cases = [
      { tag: '<input type="text" />', attr: 'type', old: 'text', new: 'checkbox' },
      { tag: '<input id="foo" />', attr: 'id', old: 'foo', new: 'bar' },
      { tag: '<svg width="100"></svg>', attr: 'width', old: '100', new: '200' },
      { tag: '<svg viewBox="0 0 100 100"></svg>', attr: 'viewBox', old: '0 0 100 100', new: '0 0 200 200' },
    ];
    cases.forEach(test => {
      var el = createElement(test.tag),
          observer = locator.getObserver(el, test.attr);
      expect(observer instanceof DirtyCheckProperty || observer instanceof DataAttributeObserver).toBe(true);
      expect(observer.getValue()).toBe(test.old);
      observer.setValue(test.new);
      expect(observer.getValue()).toBe(test.new);
    });
  });

  it('style attribute', () => {
    var el = createElement('

'),
        attrs = ['style', 'css'],
        i, observer;

    for(i = 0; i < attrs.length; i++) {
      observer = locator.getObserver(el, attrs[i]);
      expect(observer instanceof StyleObserver).toBe(true);
      expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "DIV" element\'s "' + attrs[i] + '" property is not supported.'));

      observer.setValue('width: 30px; height: 20px; background-color: red;');
      expect(observer.getValue()).toBe('width: 30px; height: 20px; background-color: red;');
      expect(el.style.height).toBe('20px');
      expect(el.style.width).toBe('30px');
      expect(el.style.backgroundColor).toBe('red');

      observer.setValue('');
      expect(el.style.height).toBe('');
      expect(el.style.width).toBe('');
      expect(el.style.backgroundColor).toBe('');

      observer.setValue({ width: '50px', height: '40px', 'background-color': 'blue' });
      expect(observer.getValue()).toBe('width: 50px; height: 40px; background-color: blue;');
      expect(el.style.height).toBe('40px');
      expect(el.style.width).toBe('50px');
      expect(el.style.backgroundColor).toBe('blue');

      observer.setValue({});
      expect(el.style.height).toBe('');
      expect(el.style.width).toBe('');
      expect(el.style.backgroundColor).toBe('');
    }
  });

  describe('ad-hoc properites on Elements', () => {
    var obj, observer;

    beforeAll(() => {
      var locator = createObserverLocator();
      obj = createElement('<foobar></foobar>');
      obj.foo = 'bar';
      observer = locator.getObserver(obj, 'foo');
    });

    it('should be a SetterObserver', () => {
      expect(observer instanceof SetterObserver).toBe(true);
    });

    it('implements the property observer api', done => {
      executeSharedPropertyObserverTests(obj, observer, done);
    });
  });
});
<commit_msg>test(element-observation): style observer url test<commit_after>import {SetterObserver} from '../src/property-observation';
import {DirtyCheckProperty} from '../src/dirty-checking';
import {
  ValueAttributeObserver,
  XLinkAttributeObserver,
  DataAttributeObserver,
  StyleObserver
} from '../src/element-observation';
import {
  createElement,
  checkDelay,
  createObserverLocator,
  executeSharedPropertyObserverTests
} from './shared';
import {initialize} from 'aurelia-pal-browser';

function createSvgUseElement() {
  var html = [
    '<svg>',
    '  <defs>',
    '    <g id="shape1">',
    '      <rect x="50" y="50" width="50" height="50" />',
    '    </g>',
    '    <g id="shape2">',
    '      <circle cx="50" cy="50" r="50" />',
    '    </g>',
    '  </defs>',
    '  <use xlink:href="#shape1" x="50" y="50" foo:bar="baz" />',
    '</svg>'].join('\n');
  return createElement(html).lastElementChild;
}

describe('element observation', () => {
  var locator;
  beforeAll(() => {
    initialize();
    locator = createObserverLocator();
  });

  it('observes xlink namespaced attributes', () => {
    var el = createSvgUseElement(),
        observer = locator.getObserver(el, 'xlink:href');
    expect(observer instanceof XLinkAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "xlink:href" property is not supported.'));
    expect(observer.getValue()).toBe('#shape1');
    observer.setValue('#shape2');
    expect(observer.getValue()).toBe('#shape2');
  });

  it('observes non-xlink namespaced attributes', () => {
    var el = createSvgUseElement(),
        observer = locator.getObserver(el, 'foo:bar');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "use" element\'s "foo:bar" property is not supported.'));
    expect(observer.getValue()).toBe('baz');
    observer.setValue('qux');
    expect(observer.getValue()).toBe('qux');
  });

  it('observes data-* attributes on Elements', () => {
    var el = createElement('

'),
        observer = locator.getObserver(el, 'data-foo');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "data-foo" property is not supported.'));
    expect(observer.getValue()).toBe('bar');
    observer.setValue('baz');
    expect(observer.getValue()).toBe('baz');
  });

  it('observes aria-* attributes on Elements', () => {
    var el = createElement('

'),
        observer = locator.getObserver(el, 'aria-hidden');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "H1" element\'s "aria-hidden" property is not supported.'));
    expect(observer.getValue()).toBe('true');
    observer.setValue('false');
    expect(observer.getValue()).toBe('false');
  });

  it('observes data-* attributes on SVGElements', () => {
    var el = createElement('<svg data-foo="bar"></svg>'),
        observer = locator.getObserver(el, 'data-foo');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "data-foo" property is not supported.'));
    expect(observer.getValue()).toBe('bar');
    observer.setValue('baz');
    expect(observer.getValue()).toBe('baz');
  });

  it('observes aria-* attributes on SVGElements', () => {
    var el = createElement('<svg aria-hidden="true"></svg>'),
        observer = locator.getObserver(el, 'aria-hidden');
    expect(observer instanceof DataAttributeObserver).toBe(true);
    expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "svg" element\'s "aria-hidden" property is not supported.'));
    expect(observer.getValue()).toBe('true');
    observer.setValue('false');
    expect(observer.getValue()).toBe('false');
  });

  it('value attributes', (done) => {
    var cases = [
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: 'bar' },
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: undefined, expected: '' },
        { tag: '<input type="text" value="foo" />', attr: 'value', old: 'foo', new: null, expected: '' },

        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: 'bar' },
        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: undefined, expected: '' },
        { tag: '<textarea>foo</textarea>', attr: 'value', old: 'foo', new: null, expected: '' },
      ],
      remaining = cases.length;
    cases.forEach(test => {
      var el = createElement(test.tag),
          observer = locator.getObserver(el, test.attr),
          callback = jasmine.createSpy('callback'),
          expected = test.hasOwnProperty('expected') ? test.expected : test.new;
      observer.subscribe(callback);
      expect(observer instanceof ValueAttributeObserver).toBe(true);
      expect(observer.getValue()).toBe(test.old);
      observer.setValue(test.new);
      expect(observer.getValue()).toBe(expected);
      setTimeout(() => {
        expect(callback).toHaveBeenCalledWith(expected, test.old);
        observer.unsubscribe(callback);
        remaining--;
        if (remaining === 0) {
          done();
        }
      }, 0);
    });
  });

  it('native properties of Elements and SVGElements', () => {
    var cases = [
      { tag: '<input type="text" />', attr: 'type', old: 'text', new: 'checkbox' },
      { tag: '<input id="foo" />', attr: 'id', old: 'foo', new: 'bar' },
      { tag: '<svg width="100"></svg>', attr: 'width', old: '100', new: '200' },
      { tag: '<svg viewBox="0 0 100 100"></svg>', attr: 'viewBox', old: '0 0 100 100', new: '0 0 200 200' },
    ];
    cases.forEach(test => {
      var el = createElement(test.tag),
          observer = locator.getObserver(el, test.attr);
      expect(observer instanceof DirtyCheckProperty || observer instanceof DataAttributeObserver).toBe(true);
      expect(observer.getValue()).toBe(test.old);
      observer.setValue(test.new);
      expect(observer.getValue()).toBe(test.new);
    });
  });

  it('style attribute', () => {
    var el = createElement('

'),
        attrs = ['style', 'css'],
        i, observer;

    for(i = 0; i < attrs.length; i++) {
      observer = locator.getObserver(el, attrs[i]);
      expect(observer instanceof StyleObserver).toBe(true);
      expect(() => observer.subscribe(() => {})).toThrow(new Error('Observation of a "DIV" element\'s "' + attrs[i] + '" property is not supported.'));

      observer.setValue('width: 30px; height:20px; background-color: red;background-image: url("http://aurelia.io/test.png");');
      expect(observer.getValue()).toBe('width: 30px; height: 20px; background-image: url("http://aurelia.io/test.png"); background-color: red;');
      expect(el.style.height).toBe('20px');
      expect(el.style.width).toBe('30px');
      expect(el.style.backgroundColor).toBe('red');
      expect(el.style.backgroundImage).toBe('url("http://aurelia.io/test.png")');

      observer.setValue('');
      expect(el.style.height).toBe('');
      expect(el.style.width).toBe('');
      expect(el.style.backgroundColor).toBe('');
      expect(el.style.backgroundImage).toBe('');

      observer.setValue({ width: '50px', height: '40px', 'background-color': 'blue', 'background-image': 'url("http://aurelia.io/test2.png")' });
      expect(observer.getValue()).toBe('width: 50px; height: 40px; background-image: url("http://aurelia.io/test2.png"); background-color: blue;');
      expect(el.style.height).toBe('40px');
      expect(el.style.width).toBe('50px');
      expect(el.style.backgroundColor).toBe('blue');
      expect(el.style.backgroundImage).toBe('url("http://aurelia.io/test2.png")')

      observer.setValue({});
      expect(el.style.height).toBe('');
      expect(el.style.width).toBe('');
      expect(el.style.backgroundColor).toBe('');
      expect(el.style.backgroundImage).toBe('');
    }
  });

  describe('ad-hoc properites on Elements', () => {
    var obj, observer;

    beforeAll(() => {
      var locator = createObserverLocator();
      obj = createElement('<foobar></foobar>');
      obj.foo = 'bar';
      observer = locator.getObserver(obj, 'foo');
    });

    it('should be a SetterObserver', () => {
      expect(observer instanceof SetterObserver).toBe(true);
    });

    it('implements the property observer api', done => {
      executeSharedPropertyObserverTests(obj, observer, done);
    });
  });
});
<|endoftext|>


<commit_before>var vows = require('vows')
    , _ = require('underscore')
    , assert = require('assert')
    , schema = require('../../index.js')

// Create a Test Suite
vows.describe('Testing if errors() precisely informs about the cause').addBatch({
    'Object': {
        topic:function(){
            return {
                input_invalid_boolean: {test: false},
                input_invalid_function: {test: function () {}},
                input_invalid_object: {test: {}},
                input_invalid_number: {test: 190},
                input_invalid_string: {test: "hello world"}
            }
        },
        'Number': {
            topic:function(common){
                return _.defaults({
                    schema: schema({test: Number.min(0).max(10)}),
                    input_invalid_string: {test: "hello world"},
                    input_invalid_number: {test: 190},
                    input_valid: {test: 5}
                }, common);
            },

            'Object { test : Number }  : String was passed instead Number': function (t) {
                var instance = t.input_invalid_string;
                assert(_.isString(instance.test), "Input is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is String")

                var validation = false === t.schema(instance);
                assert(validation, 'Validation is broken. Incorrect object '+vows.inspect(instance)+' should NOT be validated.');

                var errors = t.schema.errors(instance);
                validation = /hello world is not Number/.test(errors.test);

                assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
            },
            'Object { test : Number }  : Too big Number was passed ': function (t) {
                var instance = t.input_invalid_number;
                assert(_.isNumber(instance.test), "Input is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is Number")

                var validation = false === t.schema(instance);
                assert(validation, 'Validation is broken. Incorrect object should NOT be validated.');

                var errors = t.schema.errors(instance);
                validation = /number = 190 is bigger than required maximum = 10/.test(errors.test);

                assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
            }
        },
        'String': {
            topic: function(common){
                return _.defaults({
                    schema: schema({test: String}),
                    input_valid: {test: "some string"}
                }, common);
            },
            'Object { test : String }  : Boolean | Function | Object | Number was passed instead String': function (t) {
                [t.input_invalid_boolean, t.input_invalid_function, t.input_invalid_number, t.input_invalid_object].forEach(function (instance, index, array) {
                    var validation = false === t.schema(instance);
                    assert(!_.isString(instance.test), "Input nr "+index+" is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is not String")
                    assert(validation, 'Validation is broken. Received input '+vows.inspect(t.input_invalid_string)+' should NOT be validated.');

                    var errors = t.schema.errors(instance);
                    validation = /(.*) is not a String/.test(errors.test);

                    assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
                })

            }
        }
    }

}).export(module)
<commit_msg>tests : added test for example from docs : testing duck<commit_after>var vows = require('vows')
    , _ = require('underscore')
    , assert = require('assert')
    , schema = require('../../index.js')

// Create a Test Suite
vows.describe('Testing if errors() precisely informs about the cause').addBatch({
    'Object : ': {
        topic:function(){
            return {
                input_invalid_boolean: {test: false},
                input_invalid_function: {test: function () {}},
                input_invalid_object: {test: {}},
                input_invalid_number: {test: 190},
                input_invalid_string: {test: "hello world"}
            }
        },
        'Examples from docs':{
            'Testing Duck':function(t) {

                var Duck = schema({              // A duck
                    swim: Function,               //  - can swim
                    quack: Function,              //  - can quack
                    age: Number.min(0).max(5),    //  - is 0 to 5 years old
                    color: ['yellow', 'brown']    //  - has either yellow or brown color
                });
                var instance = {
                    swim: function () {                },
                    quack: function () {                 },
                    age: 6,
                    color: 'green'
                }

                assert(instance.age === 6, "Input is broken. Test assumes that instance.age = 6. Instance.age equal to " + instance.age)
                assert(instance.color === 'green', "Input is broken. Test assumes that instance.color = 'green'. Instance.test equal to " + instance.color)

                var validation = false === Duck(instance);
                assert(validation, 'Validation is broken. Incorrect object '+vows.inspect(instance)+' should NOT be validated.');

                var errors = Duck.errors(instance);
                validation = /number = 6 is bigger than required maximum = 5/.test(errors.age);
                errors.color.forEach(function(msg, index,arr){
                    assert(/string = green is not reference to (.*)/.test(errors.color[index]), "Incorrect message was returned = " + errors.color[index] );
                })

                validation = /number = 6 is bigger than required maximum = 5/.test(errors.color);

            }

        },
        'Number': {
            topic:function(common){
                return _.defaults({
                    schema: schema({test: Number.min(0).max(10)}),
                    input_invalid_string: {test: "hello world"},
                    input_invalid_number: {test: 190},
                    input_valid: {test: 5}
                }, common);
            },

            'Object { test : Number }  : String was passed instead Number': function (t) {
                var instance = t.input_invalid_string;
                assert(_.isString(instance.test), "Input is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is String")

                var validation = false === t.schema(instance);
                assert(validation, 'Validation is broken. Incorrect object '+vows.inspect(instance)+' should NOT be validated.');

                var errors = t.schema.errors(instance);
                validation = /hello world is not Number/.test(errors.test);

                assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
            },
            'Object { test : Number }  : Too big Number was passed ': function (t) {
                var instance = t.input_invalid_number;
                assert(_.isNumber(instance.test), "Input is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is Number")

                var validation = false === t.schema(instance);
                assert(validation, 'Validation is broken. Incorrect object should NOT be validated.');

                var errors = t.schema.errors(instance);
                validation = /number = 190 is bigger than required maximum = 10/.test(errors.test);

                assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
            }
        },
        'String': {
            topic: function(common){
                return _.defaults({
                    schema: schema({test: String}),
                    input_valid: {test: "some string"}
                }, common);
            },
            'Object { test : String }  : Boolean | Function | Object | Number was passed instead String': function (t) {
                [t.input_invalid_boolean, t.input_invalid_function, t.input_invalid_number, t.input_invalid_object].forEach(function (instance, index, array) {
                    var validation = false === t.schema(instance);
                    assert(!_.isString(instance.test), "Input nr "+index+" is broken ("+vows.inspect(instance)+").  Test assumes that 'instance.test' is not String")
                    assert(validation, 'Validation is broken. Received input '+vows.inspect(t.input_invalid_string)+' should NOT be validated.');

                    var errors = t.schema.errors(instance);
                    validation = /(.*) is not a String/.test(errors.test);

                    assert(validation, 'Function schema.errors returns incorrect message : \n \t\t' + vows.inspect(errors));
                })

            }
        }
    }

}).export(module)
<|endoftext|>


<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/**
  @namespace

  Any views you implement that are scrollable should include this mixin to
  provide basic support for scrolling actions.  You can also override any
  of these methods as needed for your own specific behaviors.

  Often times instead of adding SC.Scrollable to your view, you should
  place your view inside of an SC.ScrollView.  See that class for more
  info.

  Note that isScrollable must always be true.

*/
SC.Scrollable = {

  /** Informs the view system that the receiver is scrollable.

    Must always be true.
  */
  isScrollable: true,

  /**
    Amount to scroll one vertical line.

    Used by the default implementation of scrollDownLine() and scrollUpLine().  Defaults
    to 20px.
  */
  verticalLineScroll: 20,

  /**
    Amount to scroll one horizontal line.

    Used by the default implementation of scrollLeftLine() and scrollRightLine(). Defaults
    to 20px.
  */
  horizontalLineScroll: 20,

  /**
    Amount to scroll one vertical page.

    Used by the default implementation of scrollUpPage() and scrollDownPage(). Defaults to
    current innerFrame height.
  */
  verticalPageScroll: function() {
    return this.get('innerFrame').height ;
  }.property('innerFrame'),

  /**
    Amount to scroll one horizontal page.

    Used by the default implementation of scrollLeftPage() and scrollRightPage().  Defaults
    to current innerFrame width.
  */
  horizontalPageScroll: function() {
    return this.get('innerFrame').width ;
  }.property('innerFrame'),

  /**
    Returns true if the receiver has enough vertical content to require
    scrolling.

    If you do not want to allow vertical scrolling, override this to be false
    and set the appropriate CSS.

  */
  hasVerticalScroller: function() {
    return this.get('scrollFrame').height > this.get('innerFrame').height ;
  }.property('scrollFrame'),

  /**
    Returns true if the receiver has enough horizontal content to require
    scrolling.

    If you do not want to allow horizontal scrolling, override this to be
    false and set the appropriate CSS.

  */
  hasHorizontalScroller: function() {
    return this.get('scrollFrame').width > this.get('innerFrame').width ;
  }.property('scrollFrame'),

  /**
    Scrolls the receiver in the horizontal and vertical directions by the
    amount specified, if allowed.

    @param {Point} amount the amount to scroll.  Must include x, y or both
    @returns {Point} the actual amount scrolled.
  */
  scrollBy: function(amount) {
    var sf = this.get('scrollFrame') ;
    var f = this.get('innerFrame') ;

    if (!this.get('hasVerticalScroller')) amount.y = 0 ;
    if (sf.height <= f.height) amount.y = 0 ;

    if (!this.get('hasHorizontalScroller')) amount.x = 0 ;
    if (sf.width <= f.width) amount.x = 0 ;

    // compute new sf
    var newSf = { x: sf.x - (amount.x || 0), y: sf.y - (amount.y || 0) } ;
    newSf = this.set('scrollFrame', newSf) ;
    return { x: newSf.x - sf.x, y: newSf.y - sf.y };
  },

  /**
    Scrolls the receiver to the specified x,y coordinate
  */
  scrollTo: function(x,y) {
    this.set('scrollFrame', { x: 0-x, y: 0-y }) ;
  },

  /**
    Scroll the view to make the passed frame visible.

    Frame must be relative to the receiver's offsetParent.

    @param {SC.View} view the view you want to make visible
  */
  scrollToVisible: function(view) {

    // get frames and convert them to proper offsets
    var f = this.get('innerFrame') ;
    var sf = this.get('scrollFrame') ;

    // frame of the view, relative to the top of the scroll frame
    var vf = this.convertFrameFromView(view.get('frame'), view) ;
    vf.x -= (f.x + sf.x); vf.y -= (f.y + sf.y);

    // first visible origin
    var vo = {
      x: 0-sf.x,
      y: 0-sf.y,
      width: f.width,
      height: f.height
    };

    // if top edge is not visible, shift origin
    vo.y -= Math.max(0, SC.minY(vo) - SC.minY(vf)) ;
    vo.x -= Math.max(0, SC.minX(vo) - SC.minX(vf)) ;

    // if bottom edge is not visible, shift origin
    vo.y += Math.max(0, SC.maxY(vf) - SC.maxY(vo)) ;
    vo.x += Math.max(0, SC.maxX(vf) - SC.maxX(vo)) ;

    console.log('scrollToVisible: f=%@ - vf=%@ - vo=%@'.fmt($I(f), $I(vf), $I(vo))) ;

    // scroll to that origin.
    this.scrollTo(vo.x, vo.y) ;
  },

  /**
    Scrolls the receiver down one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollDownLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return this.scrollBy({ y: this.get('verticalLineScroll')*lines }).y ;
  },

  /**
    Scrolls the receiver down up line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollUpLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return 0-this.scrollBy({ y: 0-this.get('verticalLineScroll')*lines }).y ;
  },

  /**
    Scrolls the receiver right one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollRightLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return this.scrollTo({ y: this.get('horizontalLineScroll')*lines }).x ;
  },

  /**
    Scrolls the receiver left one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollLeftLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return 0-this.scrollTo({ y: 0-this.get('horizontalLineScroll')*lines }).x ;
  },

  /**
    Scrolls the receiver down one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollDownPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return this.scrollBy({ y: this.get('verticalPageScroll')*pages }).y ;
  },

  /**
    Scrolls the receiver down up page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollUpPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return 0-this.scrollBy({ y: 0-this.get('verticalPageScroll')*pages }).y ;
  },

  /**
    Scrolls the receiver right one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollRightPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return this.scrollTo({ y: this.get('horizontalPageScroll')*pages }).x ;
  },

  /**
    Scrolls the receiver left one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollLeftPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return 0-this.scrollTo({ y: 0-this.get('horizontalPageScroll')*pages }).x ;
  }

} ;

<commit_msg>Removing stay console.log<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

/**
  @namespace

  Any views you implement that are scrollable should include this mixin to
  provide basic support for scrolling actions.  You can also override any
  of these methods as needed for your own specific behaviors.

  Often times instead of adding SC.Scrollable to your view, you should
  place your view inside of an SC.ScrollView.  See that class for more
  info.

  Note that isScrollable must always be true.

*/
SC.Scrollable = {

  /** Informs the view system that the receiver is scrollable.

    Must always be true.
  */
  isScrollable: true,

  /**
    Amount to scroll one vertical line.

    Used by the default implementation of scrollDownLine() and scrollUpLine().  Defaults
    to 20px.
  */
  verticalLineScroll: 20,

  /**
    Amount to scroll one horizontal line.

    Used by the default implementation of scrollLeftLine() and scrollRightLine(). Defaults
    to 20px.
  */
  horizontalLineScroll: 20,

  /**
    Amount to scroll one vertical page.

    Used by the default implementation of scrollUpPage() and scrollDownPage(). Defaults to
    current innerFrame height.
  */
  verticalPageScroll: function() {
    return this.get('innerFrame').height ;
  }.property('innerFrame'),

  /**
    Amount to scroll one horizontal page.

    Used by the default implementation of scrollLeftPage() and scrollRightPage().  Defaults
    to current innerFrame width.
  */
  horizontalPageScroll: function() {
    return this.get('innerFrame').width ;
  }.property('innerFrame'),

  /**
    Returns true if the receiver has enough vertical content to require
    scrolling.

    If you do not want to allow vertical scrolling, override this to be false
    and set the appropriate CSS.

  */
  hasVerticalScroller: function() {
    return this.get('scrollFrame').height > this.get('innerFrame').height ;
  }.property('scrollFrame'),

  /**
    Returns true if the receiver has enough horizontal content to require
    scrolling.

    If you do not want to allow horizontal scrolling, override this to be
    false and set the appropriate CSS.

  */
  hasHorizontalScroller: function() {
    return this.get('scrollFrame').width > this.get('innerFrame').width ;
  }.property('scrollFrame'),

  /**
    Scrolls the receiver in the horizontal and vertical directions by the
    amount specified, if allowed.

    @param {Point} amount the amount to scroll.  Must include x, y or both
    @returns {Point} the actual amount scrolled.
  */
  scrollBy: function(amount) {
    var sf = this.get('scrollFrame') ;
    var f = this.get('innerFrame') ;

    if (!this.get('hasVerticalScroller')) amount.y = 0 ;
    if (sf.height <= f.height) amount.y = 0 ;

    if (!this.get('hasHorizontalScroller')) amount.x = 0 ;
    if (sf.width <= f.width) amount.x = 0 ;

    // compute new sf
    var newSf = { x: sf.x - (amount.x || 0), y: sf.y - (amount.y || 0) } ;
    newSf = this.set('scrollFrame', newSf) ;
    return { x: newSf.x - sf.x, y: newSf.y - sf.y };
  },

  /**
    Scrolls the receiver to the specified x,y coordinate
  */
  scrollTo: function(x,y) {
    this.set('scrollFrame', { x: 0-x, y: 0-y }) ;
  },

  /**
    Scroll the view to make the passed frame visible.

    Frame must be relative to the receiver's offsetParent.

    @param {SC.View} view the view you want to make visible
  */
  scrollToVisible: function(view) {

    // get frames and convert them to proper offsets
    var f = this.get('innerFrame') ;
    var sf = this.get('scrollFrame') ;

    // frame of the view, relative to the top of the scroll frame
    var vf = this.convertFrameFromView(view.get('frame'), view) ;
    vf.x -= (f.x + sf.x); vf.y -= (f.y + sf.y);

    // first visible origin
    var vo = {
      x: 0-sf.x,
      y: 0-sf.y,
      width: f.width,
      height: f.height
    };

    // if top edge is not visible, shift origin
    vo.y -= Math.max(0, SC.minY(vo) - SC.minY(vf)) ;
    vo.x -= Math.max(0, SC.minX(vo) - SC.minX(vf)) ;

    // if bottom edge is not visible, shift origin
    vo.y += Math.max(0, SC.maxY(vf) - SC.maxY(vo)) ;
    vo.x += Math.max(0, SC.maxX(vf) - SC.maxX(vo)) ;

    // scroll to that origin.
    this.scrollTo(vo.x, vo.y) ;
  },

  /**
    Scrolls the receiver down one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollDownLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return this.scrollBy({ y: this.get('verticalLineScroll')*lines }).y ;
  },

  /**
    Scrolls the receiver down up line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollUpLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return 0-this.scrollBy({ y: 0-this.get('verticalLineScroll')*lines }).y ;
  },

  /**
    Scrolls the receiver right one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollRightLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return this.scrollTo({ y: this.get('horizontalLineScroll')*lines }).x ;
  },

  /**
    Scrolls the receiver left one line if allowed.

    @param {Number} lines number of lines to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollLeftLine: function(lines) {
    if (lines === undefined) lines = 1 ;
    return 0-this.scrollTo({ y: 0-this.get('horizontalLineScroll')*lines }).x ;
  },

  /**
    Scrolls the receiver down one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollDownPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return this.scrollBy({ y: this.get('verticalPageScroll')*pages }).y ;
  },

  /**
    Scrolls the receiver down up page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollUpPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return 0-this.scrollBy({ y: 0-this.get('verticalPageScroll')*pages }).y ;
  },

  /**
    Scrolls the receiver right one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollRightPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return this.scrollTo({ y: this.get('horizontalPageScroll')*pages }).x ;
  },

  /**
    Scrolls the receiver left one page if allowed.

    @param {Number} pages number of pages to scroll
    @returns {Number} the amount actually scrolled.
  */
  scrollLeftPage: function(pages) {
    if (pages === undefined) pages = 1 ;
    return 0-this.scrollTo({ y: 0-this.get('horizontalPageScroll')*pages }).x ;
  }

} ;

<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
 * Set up git repositories for testing.
 *
 * @copyright Copyright 2016-2020 Kevin Locke <kevin@kevinlocke.name>
 * @license MIT
 */

'use strict';

// TODO [engine:node@>=14.14]: Use rm instead of rmdir.
const { mkdir, rmdir } = require('fs/promises');
const path = require('path');

const git = require('../test-lib/git.js');
const constants = require('../test-lib/constants.js');

// Local copy of shared constants
const {
  BRANCH_CURRENT,
  BRANCH_SAME_COMMIT,
  SUBDIR_NAME,
  TEST_REPO_BRANCH_PATH,
  TEST_REPO_DETACHED_PATH,
} = constants;

function initRepo(repoPath) {
  return rmdir(repoPath, { recursive: true })
    .then(() => git('init', '-q', repoPath))
    // The user name and email must be configured for the later git commands
    // to work.  On Travis CI (and probably others) there is no global config
    .then(() => git(
      '-C',
      repoPath,
      'config',
      'user.name',
      'Test User',
    ))
    .then(() => git(
      '-C',
      repoPath,
      'config',
      'user.email',
      'test@example.com',
    ))
    .then(() => git(
      '-C',
      repoPath,
      'commit',
      '-q',
      '-m',
      'Initial Commit',
      '--allow-empty',
    ));
}

function setUpBranchRepo(repoPath) {
  return initRepo(repoPath)
    .then(() => git('-C', repoPath, 'branch', '-m', BRANCH_CURRENT))
    .then(() => git('-C', repoPath, 'branch', BRANCH_SAME_COMMIT))
    .then(() => mkdir(path.join(repoPath, SUBDIR_NAME)));
}

function setUpDetachedRepo(repoPath) {
  return initRepo(repoPath)
    .then(() => git('-C', repoPath, 'checkout', '--detach'))
    .then(() => mkdir(path.join(repoPath, SUBDIR_NAME)));
}

module.exports =
function setUpTestRepos(args, options, callback) {
  if (args.length > 2) {
    options.stderr.write('Error: No arguments expected.\n');
    callback(1);
    return;
  }

  // eslint-disable-next-line promise/catch-or-return
  Promise.all([
    setUpBranchRepo(TEST_REPO_BRANCH_PATH),
    setUpDetachedRepo(TEST_REPO_DETACHED_PATH),
  ])
    /* eslint-disable promise/no-callback-in-promise */
    .then(
      () => callback(0),
      (err) => {
        options.stderr.write(`Unhandled Exception: ${err.stack}\n`);
        callback(1);
      },
    );
};

if (require.main === module) {
  // This file was invoked directly.
  // Note:  Could pass process.exit as callback to force immediate exit.
  module.exports(process.argv, process, (exitCode) => {
    process.exitCode = exitCode;
  });
}
<commit_msg>Partially revert "style: use fs/promises directly"<commit_after>#!/usr/bin/env node
/**
 * Set up git repositories for testing.
 *
 * @copyright Copyright 2016-2020 Kevin Locke <kevin@kevinlocke.name>
 * @license MIT
 */

'use strict';

const { mkdir } = require('fs/promises');
const path = require('path');
// TODO [engine:node@>=14.14]: Use fs.rm({force: true, recursive: true})
const rimraf = require('rimraf');
const { promisify } = require('util');

const git = require('../test-lib/git.js');
const constants = require('../test-lib/constants.js');

const rimrafP = promisify(rimraf);

// Local copy of shared constants
const {
  BRANCH_CURRENT,
  BRANCH_SAME_COMMIT,
  SUBDIR_NAME,
  TEST_REPO_BRANCH_PATH,
  TEST_REPO_DETACHED_PATH,
} = constants;

function initRepo(repoPath) {
  return rimrafP(repoPath)
    .then(() => git('init', '-q', repoPath))
    // The user name and email must be configured for the later git commands
    // to work.  On Travis CI (and probably others) there is no global config
    .then(() => git(
      '-C',
      repoPath,
      'config',
      'user.name',
      'Test User',
    ))
    .then(() => git(
      '-C',
      repoPath,
      'config',
      'user.email',
      'test@example.com',
    ))
    .then(() => git(
      '-C',
      repoPath,
      'commit',
      '-q',
      '-m',
      'Initial Commit',
      '--allow-empty',
    ));
}

function setUpBranchRepo(repoPath) {
  return initRepo(repoPath)
    .then(() => git('-C', repoPath, 'branch', '-m', BRANCH_CURRENT))
    .then(() => git('-C', repoPath, 'branch', BRANCH_SAME_COMMIT))
    .then(() => mkdir(path.join(repoPath, SUBDIR_NAME)));
}

function setUpDetachedRepo(repoPath) {
  return initRepo(repoPath)
    .then(() => git('-C', repoPath, 'checkout', '--detach'))
    .then(() => mkdir(path.join(repoPath, SUBDIR_NAME)));
}

module.exports =
function setUpTestRepos(args, options, callback) {
  if (args.length > 2) {
    options.stderr.write('Error: No arguments expected.\n');
    callback(1);
    return;
  }

  // eslint-disable-next-line promise/catch-or-return
  Promise.all([
    setUpBranchRepo(TEST_REPO_BRANCH_PATH),
    setUpDetachedRepo(TEST_REPO_DETACHED_PATH),
  ])
    /* eslint-disable promise/no-callback-in-promise */
    .then(
      () => callback(0),
      (err) => {
        options.stderr.write(`Unhandled Exception: ${err.stack}\n`);
        callback(1);
      },
    );
};

if (require.main === module) {
  // This file was invoked directly.
  // Note:  Could pass process.exit as callback to force immediate exit.
  module.exports(process.argv, process, (exitCode) => {
    process.exitCode = exitCode;
  });
}
<|endoftext|>


<commit_before>const chai = require('chai');
const expect = chai.expect;
const getDependencyConfig = require('../src/getDependencyConfig');

describe('getDependencyConfig', () => {
  it.skip('should return an array of dependencies\' rnpm config', () => {

  });

  it.skip('should filter out invalid react-native projects', () => {

  });
});
<commit_msg>Unit tests<commit_after>const chai = require('chai');
const expect = chai.expect;
const getDependencyConfig = require('../src/getDependencyConfig');
const sinon = require('sinon');

describe('getDependencyConfig', () => {
  it('should return an array of dependencies\' rnpm config', () => {
    const config = {
      getDependencyConfig: sinon.stub(),
    };

    expect(getDependencyConfig(config, ['abcd'])).to.be.an.array;
    expect(config.getDependencyConfig.callCount).to.equals(1);
  });

  it('should filter out invalid react-native projects', () => {
    const config = {
      getDependencyConfig: sinon.stub().throws(new Error('Cannot require')),
    };

    expect(getDependencyConfig(config, ['abcd'])).to.deep.equal([]);
  });
});
<|endoftext|>


<commit_before>/*global canny */
/*jslint browser: true*/

/**
 * Required: 'canny' in global scope
 *
 * E.g.:
 * canny.async.load(URL, function (src) {
 *     node.innerHTML = src;
 *     // trigger canny parse to register canny on our new modules
 *     canny.cannyParse(node, function () {
 *         console.log('CANNY PARSE DONE');
 *     });
 * });
 *
 * Alternative you can just use loadHTML (scripts will automatically added and parsed by canny):
 * canny.async.loadHTML(node, {url : URL}, function () {
 *     console.log('kodos_load READY');
 * });
 *
 * Or directly as canny module:
 *

 *
 * TODO solve dependency problem to canny.
 *
 */
(function () {
    'use strict';
    var async = (function () {
        var filesToLoad = [],
            pushLoadCBs = [],
            ready = false;

        /**
         *
         * @param script
         * @param mediaURL
         * @param cb
         */
        function appendScript(script, mediaURL, cb) {
            var node = document.createElement('script'),
                src = script.getAttribute('src');
            // handle mediaURL and all relative script are loaded from the media URL string
            if (mediaURL && src[0] !== '/') {
                if (mediaURL[mediaURL.length - 1] !== '/') {
                    mediaURL += '/';
                }
                src = mediaURL + src;
            }
            node.type = "text/javascript";
            node.async = true;
            node.setAttribute('src', src);
            node.addEventListener('load', cb, false);
            node.addEventListener('error', cb, true);
            document.head.appendChild(node);
        }

        /**
         *
         * @param scripts
         * @param mediaURL
         * @param cb
         */
        function appendScriptsToHead(scripts, mediaURL, cb) {
            var script, i, includesScripts = false,
                scriptCounter = (function () {
                    var count = 0;
                    return {
                        up : function () {count++; },
                        ready : function () {
                            count--;
                            if (count <= 0) {
                                cb();
                            }
                        }
                    };
                }());

            for (i = 0; i < scripts.length; i++) {
                script = scripts[i];
                if (script.getAttribute('src')) {
                    includesScripts = true;
                    scriptCounter.up();
                    appendScript(script, mediaURL, scriptCounter.ready);
                } else {
                    console.warn('async: found inline script tag!!!');
                }
            }

            if (scripts.length === 0 || includesScripts === false) {
                cb();
            }

        }

        /**
         * Parse the complete given DOM and prefix all relative href URL's with the given URL
         * All URL's are handled as relative if there starts not with a / or http:// or https://
         * TODO add support for URL's with a ./ or ../ and so on
         *
         * @param node
         */
        function handleLinks(node, mediaURL) {
            Array.prototype.slice.call(node.getElementsByTagName('link')).forEach(function (link) {
                var href = link.getAttribute('href');
                if (link.getAttribute('type') === 'text/css' &&
                        href !== undefined &&
                        href[0] !== '/' &&
                        !/^http:\/\/.*/.test(href) &&
                        !/^https:\/\/.*/.test(href)) {
                    if (mediaURL[mediaURL.length - 1] !== '/') {
                        mediaURL += '/';
                    }
                    href = mediaURL + href;
                    link.setAttribute('href', href);
                }
            })
        }

        /**
         *
         * @param node
         * @param attr {{url:string}}
         * @param cb
         */
        function loadHTML(node, attr, cb) {
            var div = document.createElement('div'),
                scripts,
                // only parse if html and scripts are loaded (scripts has callbacks because there are needs to loaded asynchronous)
                handleCannyParse = (function (cb) {
                    var waitForScripts = true,
                        waitForHTML = true,
                        triggger = function () {
                            if (!waitForScripts && !waitForHTML) {
                                canny.cannyParse(node, cb); // init only canny own modules
                            }
                        };
                    return {
                        scriptReady : function () {
                            waitForScripts = false;
                            triggger();
                        },
                        htmlReady : function () {
                            waitForHTML = false;
                            triggger();
                        }
                    };
                }(function () {
                    cb(attr);
                }));

            load(attr.url, function (src) {
                var childs;
                if (src) {
                    div.innerHTML = src;
                    scripts = div.getElementsByTagName('script');
                    childs = [].slice.call(div.childNodes);
                    appendScriptsToHead(scripts, attr.mediaURL, handleCannyParse.scriptReady);
                    childs.forEach(function (child) {
                        if (!(child.tagName === 'SCRIPT' && child.getAttribute('src'))) {
                            node.appendChild(child);
                        }
                    });
                    if (attr.mediaURL) {
                        handleLinks(node, attr.mediaURL);
                    }
                    handleCannyParse.htmlReady();
                } else {
                    console.warn('async: Loading async HTML failed');
                }
            });
        }
        /**
         * simple wrapper to load HTML files with GET
         * @param path
         * @param cb
         */
        function load(path, cb) {
            doAjax({
                method: 'GET',
                path: path,
                onSuccess: function (response) {
                    cb(response.responseText);
                }
            });
        }
        /**
         *
         * @param params {{
         *   noCache:boolean,
         *   method:string|POST(default),
         *   data:object|string,
         *   path:string,
         *   async:boolean|true(default),
         *   onRequest:function (will be called with the xmlHTTPRequest object quite close before the send method is called),
         *   onFailure:function,
         *   onSuccess:function,
         *   contentType:string|Content-Type(default),
         *   mimeType:string|text plain(default)
         * }}
         */
         function doAjax(params) {
            var call = new XMLHttpRequest();
            var url = params.path;
            if (params.method === 'GET' && typeof params.data === 'object') {
                for (var attr in params.data) {
                    url = url + ((/\?/).test(url) ? "&" : "?") + attr + "=" + params.data[attr];
                }
            }
            if (params.noCache) {
                url = url + ((/\?/).test(url) ? "&" : "?") + "ts=" + (new Date()).getTime();
            }
            params.method = params.method || 'POST';
            call.open(params.method, url, params.async !== false);
            call.onreadystatechange = function () {
                // TODO use === is this string or number ?
                if (call.readyState == 4) {
                    if (call.status >= 400) {
                        if (params.onFailure) {
                            params.onFailure(call);
                        }
                    } else {
                        if (params.onSuccess) {
                            params.onSuccess(call);
                        }
                    }
                }
            };

            call.setRequestHeader(params.contentType || "Content-Type", params.mimeType || "text/plain");

            // allow the caller to do some extra stuff on the request object
            if (params.onRequest && typeof params.onRequest === 'function') {
                params.onRequest(call);
            }

            if (params.method === 'POST') {
                call.send(params.data);
            } else {
                call.send();
            }
        }

        return {
            /**
             * add a callback. So you will be notified when files are loaded asynchronous.
             * You will be called only once except your return true then async will keep
             * your callback in the notifier list and you will be informed for each async request.
             *
             * The async module will call each callback with the actual attr. So you have the control
             * how often you will be notified.
             *
             * Might be changed in the future version of async:
             * Currently this is only executed for canny modules which are loaded from the DOM directly.
             *
             * @param fc
             */
            pushLoadCB : function (fc) {
                pushLoadCBs.push(fc);
            },
            /**
             * Do a simple ajax call.
             *
             * @param params {{
             *   noCache:boolean,
             *   method:string|POST(default),
             *   data:object,string,
             *   async:boolean|true(default),
             *   path:string,
             *   onRequest:function (will be called with the xmlHTTPRequest object quite close before the send method is called),
             *   onFailure:function,
             *   onSuccess:function,
             *   contentType:string|Content-Type(default),
             *   mimeType:string|text plain(default)
             * }}
             */
            doAjax: doAjax,
            /**
             *
             * @param node
             * @param attr {{
             *  url:string,
             *  mediaURL:string
             * }}
             * @param cb
             */
            loadHTML : loadHTML,
            /**
             * Deprecated: use loadHTML instead
             * @param path
             * @param cb
             */
            load: function () {
                console.warn('async:load function load is deprecated. Use loadHTML instead');
                load.apply(null, arguments);
            },
            /**
             * canny's add method
             *
             * @param node
             * @param attr
             */
            add: function (node, attr) {    // part of api
                // TODO implement logic for loading it directly from html
                if (attr.hasOwnProperty('url')) {
                    if (!ready) {
                        filesToLoad.push({
                            node: node,
                            attr: attr
                        });
                    } else {
                        loadHTML(node, attr);
                    }
                }
            },
            ready: function () {
                var obj, cbCount = filesToLoad.length;
                while (filesToLoad.length > 0) {
                    obj = filesToLoad.splice(0, 1)[0];
                    loadHTML(obj.node, obj.attr, function (attr) {
                        var keepPushCB = [], tmpCb;
                        cbCount--;
                        while (pushLoadCBs.length > 0) {
                            tmpCb = pushLoadCBs.splice(0, 1)[0];
                            if (tmpCb(attr) === true) {
                                keepPushCB.push(tmpCb);
                            }
                        }
                        pushLoadCBs = keepPushCB;
                    });
                }
            }
        };
    }());
    // export as module or bind to global
    if (typeof module !== 'undefined' && module.hasOwnProperty('exports')) {
        module.exports = async;
    } else {
        canny.add('async', async);
    }

}());<commit_msg>adapt href for async loaded content before adding the node<commit_after>/*global canny */
/*jslint browser: true*/

/**
 * Required: 'canny' in global scope
 *
 * E.g.:
 * canny.async.load(URL, function (src) {
 *     node.innerHTML = src;
 *     // trigger canny parse to register canny on our new modules
 *     canny.cannyParse(node, function () {
 *         console.log('CANNY PARSE DONE');
 *     });
 * });
 *
 * Alternative you can just use loadHTML (scripts will automatically added and parsed by canny):
 * canny.async.loadHTML(node, {url : URL}, function () {
 *     console.log('kodos_load READY');
 * });
 *
 * Or directly as canny module:
 *

 *
 * TODO solve dependency problem to canny.
 *
 */
(function () {
    'use strict';
    var async = (function () {
        var filesToLoad = [],
            pushLoadCBs = [],
            ready = false;

        /**
         *
         * @param script
         * @param mediaURL
         * @param cb
         */
        function appendScript(script, mediaURL, cb) {
            var node = document.createElement('script'),
                src = script.getAttribute('src');
            // handle mediaURL and all relative script are loaded from the media URL string
            if (mediaURL && src[0] !== '/') {
                if (mediaURL[mediaURL.length - 1] !== '/') {
                    mediaURL += '/';
                }
                src = mediaURL + src;
            }
            node.type = "text/javascript";
            node.async = true;
            node.setAttribute('src', src);
            node.addEventListener('load', cb, false);
            node.addEventListener('error', cb, true);
            document.head.appendChild(node);
        }

        /**
         *
         * @param scripts
         * @param mediaURL
         * @param cb
         */
        function appendScriptsToHead(scripts, mediaURL, cb) {
            var script, i, includesScripts = false,
                scriptCounter = (function () {
                    var count = 0;
                    return {
                        up : function () {count++; },
                        ready : function () {
                            count--;
                            if (count <= 0) {
                                cb();
                            }
                        }
                    };
                }());

            for (i = 0; i < scripts.length; i++) {
                script = scripts[i];
                if (script.getAttribute('src')) {
                    includesScripts = true;
                    scriptCounter.up();
                    appendScript(script, mediaURL, scriptCounter.ready);
                } else {
                    console.warn('async: found inline script tag!!!');
                }
            }

            if (scripts.length === 0 || includesScripts === false) {
                cb();
            }

        }

        /**
         * Parse the complete given DOM and prefix all relative href URL's with the given URL
         * All URL's are handled as relative if there starts not with a / or http:// or https://
         * TODO add support for URL's with a ./ or ../ and so on
         *
         * @param node parent element
         * @param mediaURL mediaPath to another server
         */
        function handleLinks(node, mediaURL) {
            Array.prototype.slice.call(node.getElementsByTagName('link')).forEach(function (link) {
                var href = link.getAttribute('href');
                if (link.getAttribute('type') === 'text/css' &&
                        href !== undefined &&
                        href[0] !== '/' &&
                        !/^http:\/\/.*/.test(href) &&
                        !/^https:\/\/.*/.test(href)) {
                    if (mediaURL[mediaURL.length - 1] !== '/') {
                        mediaURL += '/';
                    }
                    href = mediaURL + href;
                    link.setAttribute('href', href);
                }
            })
        }

        /**
         *
         * @param node
         * @param attr {{url:string}}
         * @param cb
         */
        function loadHTML(node, attr, cb) {
            var div = document.createElement('div'),
                scripts,
                // only parse if html and scripts are loaded (scripts has callbacks because there are needs to loaded asynchronous)
                handleCannyParse = (function (cb) {
                    var waitForScripts = true,
                        waitForHTML = true,
                        triggger = function () {
                            if (!waitForScripts && !waitForHTML) {
                                canny.cannyParse(node, cb); // init only canny own modules
                            }
                        };
                    return {
                        scriptReady : function () {
                            waitForScripts = false;
                            triggger();
                        },
                        htmlReady : function () {
                            waitForHTML = false;
                            triggger();
                        }
                    };
                }(function () {
                    cb(attr);
                }));

            load(attr.url, function (src) {
                var childs;
                if (src) {
                    div.innerHTML = src;
                    scripts = div.getElementsByTagName('script');
                    childs = [].slice.call(div.childNodes);
                    appendScriptsToHead(scripts, attr.mediaURL, handleCannyParse.scriptReady);

                    if (attr.mediaURL) {
                        handleLinks(div, attr.mediaURL);
                    }
                    childs.forEach(function (child) {
                        if (!(child.tagName === 'SCRIPT' && child.getAttribute('src'))) {
                            node.appendChild(child);
                        }
                    });
                    handleCannyParse.htmlReady();
                } else {
                    console.warn('async: Loading async HTML failed');
                }
            });
        }
        /**
         * simple wrapper to load HTML files with GET
         * @param path
         * @param cb
         */
        function load(path, cb) {
            doAjax({
                method: 'GET',
                path: path,
                onSuccess: function (response) {
                    cb(response.responseText);
                }
            });
        }
        /**
         *
         * @param params {{
         *   noCache:boolean,
         *   method:string|POST(default),
         *   data:object|string,
         *   path:string,
         *   async:boolean|true(default),
         *   onRequest:function (will be called with the xmlHTTPRequest object quite close before the send method is called),
         *   onFailure:function,
         *   onSuccess:function,
         *   contentType:string|Content-Type(default),
         *   mimeType:string|text plain(default)
         * }}
         */
         function doAjax(params) {
            var call = new XMLHttpRequest();
            var url = params.path;
            if (params.method === 'GET' && typeof params.data === 'object') {
                for (var attr in params.data) {
                    url = url + ((/\?/).test(url) ? "&" : "?") + attr + "=" + params.data[attr];
                }
            }
            if (params.noCache) {
                url = url + ((/\?/).test(url) ? "&" : "?") + "ts=" + (new Date()).getTime();
            }
            params.method = params.method || 'POST';
            call.open(params.method, url, params.async !== false);
            call.onreadystatechange = function () {
                // TODO use === is this string or number ?
                if (call.readyState == 4) {
                    if (call.status >= 400) {
                        if (params.onFailure) {
                            params.onFailure(call);
                        }
                    } else {
                        if (params.onSuccess) {
                            params.onSuccess(call);
                        }
                    }
                }
            };

            call.setRequestHeader(params.contentType || "Content-Type", params.mimeType || "text/plain");

            // allow the caller to do some extra stuff on the request object
            if (params.onRequest && typeof params.onRequest === 'function') {
                params.onRequest(call);
            }

            if (params.method === 'POST') {
                call.send(params.data);
            } else {
                call.send();
            }
        }

        return {
            /**
             * add a callback. So you will be notified when files are loaded asynchronous.
             * You will be called only once except your return true then async will keep
             * your callback in the notifier list and you will be informed for each async request.
             *
             * The async module will call each callback with the actual attr. So you have the control
             * how often you will be notified.
             *
             * Might be changed in the future version of async:
             * Currently this is only executed for canny modules which are loaded from the DOM directly.
             *
             * @param fc
             */
            pushLoadCB : function (fc) {
                pushLoadCBs.push(fc);
            },
            /**
             * Do a simple ajax call.
             *
             * @param params {{
             *   noCache:boolean,
             *   method:string|POST(default),
             *   data:object,string,
             *   async:boolean|true(default),
             *   path:string,
             *   onRequest:function (will be called with the xmlHTTPRequest object quite close before the send method is called),
             *   onFailure:function,
             *   onSuccess:function,
             *   contentType:string|Content-Type(default),
             *   mimeType:string|text plain(default)
             * }}
             */
            doAjax: doAjax,
            /**
             *
             * @param node
             * @param attr {{
             *  url:string,
             *  mediaURL:string
             * }}
             * @param cb
             */
            loadHTML : loadHTML,
            /**
             * Deprecated: use loadHTML instead
             * @param path
             * @param cb
             */
            load: function () {
                console.warn('async:load function load is deprecated. Use loadHTML instead');
                load.apply(null, arguments);
            },
            /**
             * canny's add method
             *
             * @param node
             * @param attr
             */
            add: function (node, attr) {    // part of api
                // TODO implement logic for loading it directly from html
                if (attr.hasOwnProperty('url')) {
                    if (!ready) {
                        filesToLoad.push({
                            node: node,
                            attr: attr
                        });
                    } else {
                        loadHTML(node, attr);
                    }
                }
            },
            ready: function () {
                var obj, cbCount = filesToLoad.length;
                while (filesToLoad.length > 0) {
                    obj = filesToLoad.splice(0, 1)[0];
                    loadHTML(obj.node, obj.attr, function (attr) {
                        var keepPushCB = [], tmpCb;
                        cbCount--;
                        while (pushLoadCBs.length > 0) {
                            tmpCb = pushLoadCBs.splice(0, 1)[0];
                            if (tmpCb(attr) === true) {
                                keepPushCB.push(tmpCb);
                            }
                        }
                        pushLoadCBs = keepPushCB;
                    });
                }
            }
        };
    }());
    // export as module or bind to global
    if (typeof module !== 'undefined' && module.hasOwnProperty('exports')) {
        module.exports = async;
    } else {
        canny.add('async', async);
    }

}());<|endoftext|>