<commit_before>const path = require('path');
const nodeExternals = require('webpack-node-externals');
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');

const getScripts = function () {
    let script = [{src: `${process.env.CLIENT_STATIC_URL}/polyfill.min.js`}];
    if (process.env.NODE_ENV === 'production') {
        script.push({
            src: `https://www.googletagmanager.com/gtag/js?id=AW-773868544`, async: true
        });
        script.push({
            innerHTML: "window.dataLayer = window.dataLayer || []; " +
                "function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-773868544');"
        });
    }
    return script;
};

module.exports = {
    head: {
        title: 'DumondaMe',
        meta: [
            {charset: 'utf-8'},
            {name: 'viewport', content: 'width=device-width, initial-scale=1'},
            {hid: 'mobile-web-app-capable', name: 'mobile-web-app-capable', content: 'yes'},
            {hid: 'apple-mobile-web-app-capable', name: 'apple-mobile-web-app-capable', content: 'yes'},
            {hid: 'apple-mobile-web-app-status-bar-style', name: 'apple-mobile-web-app-status-bar-style',
                content: 'default'},
            {hid: 'theme-color', name: 'theme-color', content: '#009e97'},
            {hid: 'og:type', property: 'og:type', content: 'website'},
            {hid: 'og:url', property: 'og:url', content: 'https://www.dumonda.me/'},
            {hid: 'og:site_name', property: 'og:site_name', content: 'Dumonda Me'},
            {
                hid: 'og:image', property: 'og:image',
                content: `${process.env.CLIENT_STATIC_URL}/img/socialMedia/preview.jpg`
            },
            {hid: 'twitter:card', property: 'twitter:card', content: 'summary'},
            {hid: 'twitter:url', property: 'twitter:url', content: 'https://www.dumonda.me/'},
            {
                hid: 'twitter:image', property: 'twitter:image',
                content: `${process.env.CLIENT_STATIC_URL}/img/socialMedia/preview.jpg`
            }
        ],
        noscript: [{innerHTML: "This website requires JavaScript", body: true}],
        link: [
            {rel: 'icon', type: 'image/x-icon', href: `${process.env.CLIENT_STATIC_URL}/favicon.ico`},
            {rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500'}
        ],
        script: getScripts(),
        __dangerouslyDisableSanitizers: ['script']
    },
    dev: (process.env.NODE_ENV !== 'production'),
    env: {
        staticUrl: process.env.CLIENT_STATIC_URL,
        isProduction: process.env.NODE_ENV === 'production',
        oAuthGoogleClientUrl: process.env.OAUTH_GOOGLE_CLIENT_URL,
        oAuthOutlookClientUrl: process.env.OAUTH_OUTLOOK_CLIENT_URL
    },
    router: {
        scrollBehavior: function () {
            return {x: 0, y: 0}
        }
    },
    modules: [
        ['@nuxtjs/pwa', {meta: false, oneSignal: false}],
        '@nuxtjs/proxy',
        '@nuxtjs/axios',
        ['nuxt-matomo', {matomoUrl: process.env.MATOMO_URL, siteId: process.env.MATOMO_SIDE_ID}],
        'nuxt-sass-resources-loader'
    ],
    sassResources: [
        path.resolve(__dirname, 'assets/style/variables.scss'),
        path.resolve(__dirname, 'assets/style/card.scss'),
        path.resolve(__dirname, 'assets/style/layout.scss')
    ],
    loading: {
        color: '#009e97'
    },
    transition: {
        name: 'page',
        mode: 'out-in',
        css: false,
        duration: 0
    },
    axios: {
        prefix: '/api/',
        proxy: true
    },
    render: {
        resourceHints: false
    },
    manifest: {
        name: 'DumondaMe',
        "short_name": 'DumondaMe',
        lang: 'de',
        "start_url": '/'
    },
    build: {
        transpile: [/^vuetify/],
        plugins: [
            new VuetifyLoaderPlugin()
        ],
        optimizeCSS: true,
        extend(config, {isServer}) {
            if (isServer) {
                config.externals = [
                    nodeExternals({
                        whitelist: [/^vuetify/]
                    })
                ]
            }
        }
    },
    plugins: ['~/plugins/i18n.js',
        '~/plugins/moment.js',
        '~/plugins/vuetify.js',
        '~/plugins/axios.js'],
    css: [
        '~/assets/style/app.styl',
        'cropperjs/dist/cropper.min.css',
        '@mdi/font/css/materialdesignicons.min.css'
    ]
};
<commit_msg>Set display to standalone in manifest for pwa.<commit_after>const path = require('path');
const nodeExternals = require('webpack-node-externals');
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');

const getScripts = function () {
    let script = [{src: `${process.env.CLIENT_STATIC_URL}/polyfill.min.js`}];
    if (process.env.NODE_ENV === 'production') {
        script.push({
            src: `https://www.googletagmanager.com/gtag/js?id=AW-773868544`, async: true
        });
        script.push({
            innerHTML: "window.dataLayer = window.dataLayer || []; " +
                "function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-773868544');"
        });
    }
    return script;
};

module.exports = {
    head: {
        title: 'DumondaMe',
        meta: [
            {charset: 'utf-8'},
            {name: 'viewport', content: 'width=device-width, initial-scale=1'},
            {hid: 'mobile-web-app-capable', name: 'mobile-web-app-capable', content: 'yes'},
            {hid: 'apple-mobile-web-app-capable', name: 'apple-mobile-web-app-capable', content: 'yes'},
            {hid: 'apple-mobile-web-app-status-bar-style', name: 'apple-mobile-web-app-status-bar-style',
                content: 'default'},
            {hid: 'theme-color', name: 'theme-color', content: '#009e97'},
            {hid: 'og:type', property: 'og:type', content: 'website'},
            {hid: 'og:url', property: 'og:url', content: 'https://www.dumonda.me/'},
            {hid: 'og:site_name', property: 'og:site_name', content: 'Dumonda Me'},
            {
                hid: 'og:image', property: 'og:image',
                content: `${process.env.CLIENT_STATIC_URL}/img/socialMedia/preview.jpg`
            },
            {hid: 'twitter:card', property: 'twitter:card', content: 'summary'},
            {hid: 'twitter:url', property: 'twitter:url', content: 'https://www.dumonda.me/'},
            {
                hid: 'twitter:image', property: 'twitter:image',
                content: `${process.env.CLIENT_STATIC_URL}/img/socialMedia/preview.jpg`
            }
        ],
        noscript: [{innerHTML: "This website requires JavaScript", body: true}],
        link: [
            {rel: 'icon', type: 'image/x-icon', href: `${process.env.CLIENT_STATIC_URL}/favicon.ico`},
            {rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500'}
        ],
        script: getScripts(),
        __dangerouslyDisableSanitizers: ['script']
    },
    dev: (process.env.NODE_ENV !== 'production'),
    env: {
        staticUrl: process.env.CLIENT_STATIC_URL,
        isProduction: process.env.NODE_ENV === 'production',
        oAuthGoogleClientUrl: process.env.OAUTH_GOOGLE_CLIENT_URL,
        oAuthOutlookClientUrl: process.env.OAUTH_OUTLOOK_CLIENT_URL
    },
    router: {
        scrollBehavior: function () {
            return {x: 0, y: 0}
        }
    },
    modules: [
        ['@nuxtjs/pwa', {meta: false, oneSignal: false}],
        '@nuxtjs/proxy',
        '@nuxtjs/axios',
        ['nuxt-matomo', {matomoUrl: process.env.MATOMO_URL, siteId: process.env.MATOMO_SIDE_ID}],
        'nuxt-sass-resources-loader'
    ],
    sassResources: [
        path.resolve(__dirname, 'assets/style/variables.scss'),
        path.resolve(__dirname, 'assets/style/card.scss'),
        path.resolve(__dirname, 'assets/style/layout.scss')
    ],
    loading: {
        color: '#009e97'
    },
    transition: {
        name: 'page',
        mode: 'out-in',
        css: false,
        duration: 0
    },
    axios: {
        prefix: '/api/',
        proxy: true
    },
    render: {
        resourceHints: false
    },
    manifest: {
        name: 'DumondaMe',
        "short_name": 'DumondaMe',
        lang: 'de',
        "start_url": '/',
        display: 'standalone'
    },
    build: {
        transpile: [/^vuetify/],
        plugins: [
            new VuetifyLoaderPlugin()
        ],
        optimizeCSS: true,
        extend(config, {isServer}) {
            if (isServer) {
                config.externals = [
                    nodeExternals({
                        whitelist: [/^vuetify/]
                    })
                ]
            }
        }
    },
    plugins: ['~/plugins/i18n.js',
        '~/plugins/moment.js',
        '~/plugins/vuetify.js',
        '~/plugins/axios.js'],
    css: [
        '~/assets/style/app.styl',
        'cropperjs/dist/cropper.min.css',
        '@mdi/font/css/materialdesignicons.min.css'
    ]
};
<|endoftext|>


<commit_before>const path = require('path');

module.exports = {
  '@tags': ['search-page'],

  before: function (browser) {
    browser
      .url('https://soundcloud.com/search?q=test')
      .dismissCookiePolicyNotification();
  },

  after: function (browser) {
    browser.end();
  },

  'Adds a Download button for every track or playlist item in the Everything list': function (browser) {
    browser
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item .sound__body', function (results) {
        browser.globals.numSoundItemsInitial = results.value.length;
      })
      .elements('css selector', '.searchList__item .sound__body .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtnsInitial = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numSoundItemsInitial > 1,
          `Number of track or playlist items loaded (${browser.globals.numSoundItemsInitial}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtnsInitial, browser.globals.numSoundItemsInitial,
          'Should add a Download button for every track or playlist item in the list.');
      });
  },

  'Continues adding Download buttons as more items are loaded into the list': function (browser) {
    browser
      .loadMoreItemsIntoList('.searchList .searchList__item:last-of-type')
      .elements('css selector', '.searchList__item .sound__body', function (results) {
        browser.globals.numSoundItemsFinal = results.value.length;
      })
      .elements('css selector', '.searchList__item .sound__body .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtnsFinal = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numSoundItemsFinal > browser.globals.numSoundItemsInitial,
          `Number of track or playlist items loaded (${browser.globals.numSoundItemsFinal}) `
          + `should be more than initial (${browser.globals.numSoundItemsInitial}).`);
        browser.assert.strictEqual(browser.globals.numZcBtnsFinal, browser.globals.numSoundItemsFinal,
          'Should add a Download button for every track or playlist item in the list.');
      });
  },

  'Adds a Download button for every track in the SoundCloud Go+ tracks list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'SoundCloud Go+ tracks\')]')
      .useCss()
      .waitForElementVisible('.searchPremiumContentHeader')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numTracks = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numTracks > 1,
          `Number of tracks loaded (${browser.globals.numTracks}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numTracks,
          'Should add a Download button for every track in the list.');
      });
  },

  'Adds a Download button for every track in the Tracks list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Tracks\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numTracks = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numTracks > 1,
          `Number of tracks loaded (${browser.globals.numTracks}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numTracks,
          'Should add a Download button for every track in the list.');
      });
  },

  'Downloads the track when the Download button in the list is clicked': function (browser) {
    const trackPath = path.join(browser.globals.downloadDir, '07 Lil Baby & Marlo- Time After Time Ft. Tk Kravitz [Prod. By Quay Global].mp3');
    browser
      .setValue('input[type="search"]', ['07 Lil Baby & Marlo- Time After Time Ft. Tk Kravitz [Prod. By Quay Global]', browser.Keys.ENTER])
      .pause(1000) // wait for search to initiate
      .waitForElementVisible('.searchResultGroupHeading')
      .click('.searchList__item button.zc-button-download')
      .assert.fileDownloaded(trackPath)
      .verify.fileHasSize(trackPath, 3360581)
      .clearValue('input[type="search"]')
      .setValue('input[type="search"]', ['test', browser.Keys.ENTER]) // reset search back to 'test'
  },

  'Adds a Download button for every album in the Albums list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Albums\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numAlbums = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numAlbums > 1,
          `Number of albums loaded (${browser.globals.numAlbums}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numAlbums,
          'Should add a Download button for every album in the list.');
      });
  },

  'Adds a Download button for every playlist in the Playlists list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Playlists\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numPlaylists = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numPlaylists > 1,
          `Number of playlists loaded (${browser.globals.numPlaylists}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numPlaylists,
          'Should add a Download button for every playlist in the list.');
      });
  },

  'Downloads the playlist when the Download button in the list is clicked': function (browser) {
    const playlistDir = path.join(browser.globals.downloadDir, 'xtangle - Zqc9JceJT11d27T3ukAM');
    const track1Path = path.join(playlistDir, 'STOOPID (Feat. Bobby Shmurda).mp3');
    const track2Path = path.join(playlistDir, 'Lil Peep & XXXTENTACION - Falling Down.mp3');
    browser
      .setValue('input[type="search"]', ['xtangle Zqc9JceJT11d27T3ukAM', browser.Keys.ENTER])
      .pause(1000) // wait for search to initiate
      .waitForElementVisible('.searchResultGroupHeading')
      .click('.searchList__item button.zc-button-download')
      .assert.fileDownloaded(track1Path)
      .verify.fileHasSize(track1Path, 2489082)
      .assert.fileDownloaded(track2Path)
      .verify.fileHasSize(track2Path, 3215457);
  }
};
<commit_msg>fix(e2e): fix wrong selector<commit_after>const path = require('path');

module.exports = {
  '@tags': ['search-page'],

  before: function (browser) {
    browser
      .url('https://soundcloud.com/search?q=test')
      .dismissCookiePolicyNotification();
  },

  after: function (browser) {
    browser.end();
  },

  'Adds a Download button for every track or playlist item in the Everything list': function (browser) {
    browser
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item .sound__body', function (results) {
        browser.globals.numSoundItemsInitial = results.value.length;
      })
      .elements('css selector', '.searchList__item .sound__body .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtnsInitial = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numSoundItemsInitial > 1,
          `Number of track or playlist items loaded (${browser.globals.numSoundItemsInitial}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtnsInitial, browser.globals.numSoundItemsInitial,
          'Should add a Download button for every track or playlist item in the list.');
      });
  },

  'Continues adding Download buttons as more items are loaded into the list': function (browser) {
    browser
      .loadMoreItemsIntoList('.searchList .searchList__item:last-of-type')
      .elements('css selector', '.searchList__item .sound__body', function (results) {
        browser.globals.numSoundItemsFinal = results.value.length;
      })
      .elements('css selector', '.searchList__item .sound__body .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtnsFinal = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numSoundItemsFinal > browser.globals.numSoundItemsInitial,
          `Number of track or playlist items loaded (${browser.globals.numSoundItemsFinal}) `
          + `should be more than initial (${browser.globals.numSoundItemsInitial}).`);
        browser.assert.strictEqual(browser.globals.numZcBtnsFinal, browser.globals.numSoundItemsFinal,
          'Should add a Download button for every track or playlist item in the list.');
      });
  },

  'Adds a Download button for every track in the SoundCloud Go+ tracks list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'SoundCloud Go+ tracks\')]')
      .useCss()
      .waitForElementVisible('.searchPremiumContentHeader')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numTracks = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numTracks > 1,
          `Number of tracks loaded (${browser.globals.numTracks}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numTracks,
          'Should add a Download button for every track in the list.');
      });
  },

  'Adds a Download button for every track in the Tracks list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Tracks\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numTracks = results.value.length;
      })
      .elements('css selector', '.searchList__item .sc-button-share + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numTracks > 1,
          `Number of tracks loaded (${browser.globals.numTracks}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numTracks,
          'Should add a Download button for every track in the list.');
      });
  },

  'Downloads the track when the Download button in the list is clicked': function (browser) {
    const trackPath = path.join(browser.globals.downloadDir, '07 Lil Baby & Marlo- Time After Time Ft. Tk Kravitz [Prod. By Quay Global].mp3');
    browser
      .setValue('input[type="search"]', ['07 Lil Baby & Marlo- Time After Time Ft. Tk Kravitz [Prod. By Quay Global]', browser.Keys.ENTER])
      .pause(1000) // wait for search to initiate
      .waitForElementVisible('.searchResultGroupHeading')
      .click('.searchList__item button.zc-button-download')
      .assert.fileDownloaded(trackPath)
      .verify.fileHasSize(trackPath, 3360581)
      .clearValue('input[type="search"]')
      .setValue('input[type="search"]', ['test', browser.Keys.ENTER]) // reset search back to 'test'
  },

  'Adds a Download button for every album in the Albums list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Albums\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numAlbums = results.value.length;
      })
      .elements('css selector', '.searchList__item :not(.zc-button-download) + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numAlbums > 1,
          `Number of albums loaded (${browser.globals.numAlbums}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numAlbums,
          'Should add a Download button for every album in the list.');
      });
  },

  'Adds a Download button for every playlist in the Playlists list': function (browser) {
    browser
      .useXpath()
      .click('//*[contains(@class,\'g-nav-item\')]//a[contains(text(),\'Playlists\')]')
      .useCss()
      .waitForElementVisible('.searchResultGroupHeading')
      .elements('css selector', '.searchList__item', function (results) {
        browser.globals.numPlaylists = results.value.length;
      })
      .elements('css selector', '.searchList__item :not(.zc-button-download) + button.zc-button-download', function (results) {
        browser.globals.numZcBtns = results.value.length;
      })
      .perform(function () {
        browser.assert.ok(browser.globals.numPlaylists > 1,
          `Number of playlists loaded (${browser.globals.numPlaylists}) should be more than 1.`);
        browser.assert.strictEqual(browser.globals.numZcBtns, browser.globals.numPlaylists,
          'Should add a Download button for every playlist in the list.');
      });
  },

  'Downloads the playlist when the Download button in the list is clicked': function (browser) {
    const playlistDir = path.join(browser.globals.downloadDir, 'xtangle - Zqc9JceJT11d27T3ukAM');
    const track1Path = path.join(playlistDir, 'STOOPID (Feat. Bobby Shmurda).mp3');
    const track2Path = path.join(playlistDir, 'Lil Peep & XXXTENTACION - Falling Down.mp3');
    browser
      .setValue('input[type="search"]', ['xtangle Zqc9JceJT11d27T3ukAM', browser.Keys.ENTER])
      .pause(1000) // wait for search to initiate
      .waitForElementVisible('.searchResultGroupHeading')
      .click('.searchList__item button.zc-button-download')
      .assert.fileDownloaded(track1Path)
      .verify.fileHasSize(track1Path, 2489082)
      .assert.fileDownloaded(track2Path)
      .verify.fileHasSize(track2Path, 3215457);
  }
};
<|endoftext|>


<commit_before>"use strict";

var LEVEL = { CRITICAL   : [0, 'CRITICAL'],
              ERROR      : [1, 'ERROR'],
              WARNING    : [2, 'WARNING'],
              INFO       : [3, 'INFO'],
              DEBUG      : [4, 'DEBUG'] };

var DT_FORMAT = { DATE_TIME : 0,
                  DATE      : 1,
                  TIME      : 2 };

var CURRENT_LEVEL = LEVEL.INFO;

var LEVEL_SET = false;

var LOCAL_TAG = "simplog";

var DATE_TIME_FORMAT = DT_FORMAT.DATE_TIME;

function setLevel(customLevel) {
    if(!LEVEL_SET) {
        CURRENT_LEVEL = customLevel;
        LEVEL_SET = true;
    } else {
        log(LEVEL.WARNING, LOCAL_TAG, 'Logging level already set, cannot change!');
    }
    return CURRENT_LEVEL;
}

function getLevel() {
    return CURRENT_LEVEL;
}

function write(where, what) {
    if(where === 'console') {
        console.log(what);
    }
}

function log(messageLevel, tag, message) {
    if(messageLevel[0] <= CURRENT_LEVEL[0]) {
        var what = "[" + getFormattedTimestamp() + '] ';
        what += messageLevel[1] + ' - ';
        what += tag + ' - ';
        what += message;
        write('console', what);
        return what;
    } else {
        return null;
    }
}

function setTimestampFormat(format) {
    DATE_TIME_FORMAT = format;
}

function getTimestampFormat() {
    return DATE_TIME_FORMAT;
}

function getFormattedTimestamp() {
    var date = new Date();
    switch(DATE_TIME_FORMAT) {
        case DT_FORMAT.DATE_TIME:
            return date.getFullYear() + "/" +
                   date.getMonth() + "/" +
                   date.getDate() + " " +
                   date.getHours() + ":" +
                   date.getMinutes();
        case DT_FORMAT.DATE:
            return date.getFullYear() + "/" +
                   date.getMonth() + "/" +
                   date.getDate();
        case DT_FORMAT.TIME:
            return date.getHours() + ":" +
                   date.getMinutes();
        default:
            DATE_TIME_FORMAT = DT_FORMAT.DATE_TIME;
            log(LEVEL.WARNING, LOCAL_TAG, 'Invalid date/ time format, fail-safe to default!');
            return date.getFullYear() + "/" +
                   date.getMonth() + "/" +
                   date.getDate() + " " +
                   date.getHours() + ":" +
                   date.getMinutes();
    }
}

exports.level = LEVEL;
exports.setLevel = setLevel;
exports.getLevel = getLevel;
exports.dtFormat = DT_FORMAT;
exports.setTimestampFormat = setTimestampFormat;
exports.getTimestampFormat = getTimestampFormat;
exports.log = log;<commit_msg>Fix for issue #8: added checks for message levels<commit_after>"use strict";

var LEVEL = { CRITICAL   : [0, "CRITICAL"],
              ERROR      : [1, "ERROR"],
              WARNING    : [2, "WARNING"],
              INFO       : [3, "INFO"],
              DEBUG      : [4, "DEBUG"] };

var DT_FORMAT = { DATE_TIME : 0,
                  DATE      : 1,
                  TIME      : 2 };

var CURRENT_LEVEL = LEVEL.INFO;

var LEVEL_SET = false;

var LOCAL_TAG = "silog";

var DATE_TIME_FORMAT = DT_FORMAT.DATE_TIME;

function setLevel(customLevel) {
    if(!LEVEL_SET) {
        if(!(customLevel instanceof Array && typeof customLevel[0] === "number")) {
            // TODO: this check is kinda shabby
            log(LEVEL.WARNING, LOCAL_TAG, "Invalid message level: " + customLevel);
            return false;
        } else {
            CURRENT_LEVEL = customLevel;
            LEVEL_SET = true;
        }
    } else {
        log(LEVEL.WARNING, LOCAL_TAG, "Logging level already set, cannot change!");
    }
    return CURRENT_LEVEL;
}

function getLevel() {
    return CURRENT_LEVEL;
}

function write(where, what) {
    if(where === "console") {
        console.log(what);
    }
}

function log(messageLevel, tag, message) {
    if(!(messageLevel instanceof Array && typeof messageLevel[0] === "number")) {
        // TODO: this check is kinda shabby
        return log(LEVEL.WARNING, LOCAL_TAG, "Invalid message level for: " +
                                             tag +
                                             " - " +
                                             message +
                                             " = " +
                                             messageLevel);
    }
    if(messageLevel[0] <= CURRENT_LEVEL[0] || tag === LOCAL_TAG) {
        var what = "[" + getFormattedTimestamp() + "] ";
        what += messageLevel[1] + ' - ';
        what += tag + ' - ';
        what += message;
        write("console", what);
        return what;
    } else {
        return null;
    }
}

function setTimestampFormat(format) {
    DATE_TIME_FORMAT = format;
}

function getTimestampFormat() {
    return DATE_TIME_FORMAT;
}

function getFormattedTimestamp() {
    var date = new Date();
    switch(DATE_TIME_FORMAT) {
        case DT_FORMAT.DATE_TIME:
            return date.getFullYear() + '/' +
                   date.getMonth() + '/' +
                   date.getDate() + ' ' +
                   date.getHours() + ':' +
                   date.getMinutes();
        case DT_FORMAT.DATE:
            return date.getFullYear() + '/' +
                   date.getMonth() + '/' +
                   date.getDate();
        case DT_FORMAT.TIME:
            return date.getHours() + ':' +
                   date.getMinutes();
        default:
            DATE_TIME_FORMAT = DT_FORMAT.DATE_TIME;
            log(LEVEL.WARNING, LOCAL_TAG, "Invalid date/ time format, fail-safe to default!");
            return date.getFullYear() + '/' +
                   date.getMonth() + '/' +
                   date.getDate() + ' ' +
                   date.getHours() + ':' +
                   date.getMinutes();
    }
}

exports.level = LEVEL;
exports.setLevel = setLevel;
exports.getLevel = getLevel;
exports.dtFormat = DT_FORMAT;
exports.setTimestampFormat = setTimestampFormat;
exports.getTimestampFormat = getTimestampFormat;
exports.log = log;<|endoftext|>


<commit_before>/**
 * Infinite Ajax Scroll v2.0.0
 * A jQuery plugin for infinite scrolling
 * http://infiniteajaxscroll.com
 *
 * Commercial use requires one-time purchase of a commercial license
 * http://infiniteajaxscroll.com/docs/license.html
 *
 * Non-commercial use is licensed under the MIT License
 *
 * Copyright 2014 Webcreate (Jeroen Fiege)
 */

(function($) {

  'use strict';

  var UNDETERMINED_SCROLLOFFSET = -1;

  var IAS = function($element, options) {
    this.itemsContainerSelector = options.container;
    this.itemSelector = options.item;
    this.nextSelector = options.next;
    this.paginationSelector = options.pagination;
    this.$scrollContainer = $element;
    this.$itemsContainer = $(this.itemsContainerSelector);
    this.$container = (window === $element.get(0) ? $(document) : $element);
    this.defaultDelay = options.delay;
    this.negativeMargin = options.negativeMargin;
    this.nextUrl = null;
    this.isBound = false;
    this.listeners = {
      next: new IASCallbacks(),
      load: new IASCallbacks(),
      didLoad: new IASCallbacks(),
      render: new IASCallbacks(),
      scroll: new IASCallbacks(),
      noneLeft: new IASCallbacks()
    };
    this.extensions = [];

    /**
     * Scroll event handler
     *
     * Note: calls to this functions should be throttled
     *
     * @private
     */
    this.scrollHandler = function() {
      var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
          scrollThreshold = this.getScrollThreshold()
      ;

      // the throttle method can call the scrollHandler even thought we have called unbind()
      if (!this.isBound) {
        return;
      }

      // invalid scrollThreshold. The DOM might not have loaded yet...
      if (UNDETERMINED_SCROLLOFFSET == scrollThreshold) {
        return;
      }

      this.fire('scroll', [currentScrollOffset, scrollThreshold]);

      if (currentScrollOffset >= scrollThreshold) {
        this.next();
      }
    };

    /**
     * Returns the last item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getLastItem = function() {
      return $(this.itemSelector, this.$itemsContainer.get(0)).last();
    };

    /**
     * Returns scroll threshold. This threshold marks the line from where
     * IAS should start loading the next page.
     *
     * @private
     * @param negativeMargin defaults to {this.negativeMargin}
     * @return {number}
     */
    this.getScrollThreshold = function(negativeMargin) {
      var lastElement;

      negativeMargin = negativeMargin || this.negativeMargin;
      negativeMargin = (negativeMargin >= 0 ? negativeMargin * -1 : negativeMargin);

      lastElement = this.getLastItem();

      // if the don't have a last element, the DOM might not have been loaded,
      // or the selector is invalid
      if (0 === lastElement.size()) {
        return UNDETERMINED_SCROLLOFFSET;
      }

      return (lastElement.offset().top + lastElement.height() + negativeMargin);
    };

    /**
     * Returns current scroll offset for the given scroll container
     *
     * @private
     * @param $container
     * @returns {number}
     */
    this.getCurrentScrollOffset = function($container) {
      var scrollTop = 0,
          containerHeight = $container.height();

      if (window === $container.get(0))  {
        scrollTop = $container.scrollTop();
      } else {
        scrollTop = $container.offset().top;
      }

      // compensate for iPhone
      if (navigator.platform.indexOf("iPhone") != -1 || navigator.platform.indexOf("iPod") != -1) {
        containerHeight += 80;
      }

      return (scrollTop + containerHeight);
    };

    /**
     * Returns the url for the next page
     *
     * @private
     */
    this.getNextUrl = function(container) {
      if (!container) {
        container = this.$container;
      }

      return $(this.nextSelector, container).attr('href');
    };

    /**
     * Loads a page url
     *
     * @param url
     * @param callback
     * @param delay
     * @returns {object}        jsXhr object
     */
    this.load = function(url, callback, delay) {
      var self = this,
          $itemContainer,
          items = [],
          timeStart = +new Date(),
          timeDiff;

      delay = delay || this.defaultDelay;

      return $.get(url, null, $.proxy(function(data) {
        $itemContainer = $(this.itemsContainerSelector, data).eq(0);
        if (0 === $itemContainer.length) {
          $itemContainer = $(data).filter(this.itemsContainerSelector).eq(0);
        }

        if ($itemContainer) {
          $itemContainer.find(this.itemSelector).each(function() {
            items.push(this);
          });
        }

        // @todo it's best practise to fire events at the beginning of the method
        self.fire('load', [data, items]);

        if (callback) {
          timeDiff = +new Date() - timeStart;
          if (timeDiff < delay) {
            setTimeout(function() {
              callback.call(self, data, items);
            }, delay - timeDiff);
          } else {
            callback.call(self, data, items);
          }
        }
      }, self), 'html');
    };

    /**
     * Renders items
     *
     * @param items
     */
    this.render = function(items) {
      var lastItem = this.getLastItem();

      this.fire('render', [items]);

      $(items).hide(); // at first, hide it so we can fade it in later

      lastItem.after(items);

      $(items).fadeIn();
    };

    /**
     * Hides the pagination
     */
    this.hidePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).hide();
      }
    };

    /**
     * Restores the pagination
     */
    this.restorePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).show();
      }
    };

    /**
     * Throttles a method
     *
     * Adopted from Ben Alman's jQuery throttle / debounce plugin
     *
     * @param callback
     * @param delay
     * @return {object}
     */
    this.throttle = function(callback, delay) {
      var lastExecutionTime = 0,
          wrapper,
          timerId
      ;

      wrapper = function() {
        var that = this,
            args = arguments,
            diff = +new Date() - lastExecutionTime;

        function execute() {
          lastExecutionTime = +new Date();
          callback.apply(that, args);
        }

        if (!timerId) {
          execute();
        } else {
          clearTimeout(timerId);
        }

        if (diff > delay) {
          execute();
        } else {
          timerId = setTimeout(execute, delay);
        }
      };

      if ($.guid) {
        wrapper.guid = callback.guid = callback.guid || $.guid++;
      }

      return wrapper;
    };

    /**
     * Fires an event with the ability to cancel further processing. This
     * can be achieved by returning false in a listener.
     *
     * @param event
     * @param args
     * @returns {*}
     */
    this.fire = function(event, args) {
      return this.listeners[event].fireWith(this, args);
    };

    return this;
  };

  /**
   * Initialize IAS
   *
   * Note: Should be called when the document is ready
   *
   * @public
   */
  IAS.prototype.initialize = function() {
    var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
        scrollThreshold = this.getScrollThreshold();

    this.hidePagination();
    this.bind();

    this.nextUrl = this.getNextUrl();

    // start loading next page if content is shorter than page fold
    if (currentScrollOffset >= scrollThreshold) {
      this.next();
    }

    return this;
  };

  /**
   * Binds IAS to DOM events
   *
   * @public
   */
  IAS.prototype.bind = function() {
    if (this.isBound) {
      return;
    }

    this.$scrollContainer.on('scroll', $.proxy(this.throttle(this.scrollHandler, 150), this));

    this.isBound = true;
  };

  /**
   * Unbinds IAS to events
   *
   * @public
   */
  IAS.prototype.unbind = function() {
    if (!this.isBound) {
      return;
    }

    this.$scrollContainer.off('scroll', this.scrollHandler);

    this.isBound = false;
  };

  /**
   * Destroys IAS instance
   *
   * @public
   */
  IAS.prototype.destroy = function() {
    this.unbind();
  };

  /**
   * Registers an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.on = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].add($.proxy(callback, this));

    return this;
  };

  /**
   * Removes an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.off = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].remove(callback);

    return this;
  };

  /**
   * Load the next page
   *
   * @public
   */
  IAS.prototype.next = function() {
    var url = this.nextUrl,
        self = this;

    this.unbind();

    if (!url) {
      this.fire('noneLeft', [this.getLastItem()]);

      return false;
    }

    var promise = this.fire('next', [url]);

    promise.done(function() {
      self.load(url, function(data, items) {
        self.render(items);

        self.nextUrl = self.getNextUrl(data);

        self.bind();
      });
    });

    promise.fail(function() {
      self.bind();
    });

    return true;
  };

  /**
   * Adds an extension
   *
   * @public
   */
  IAS.prototype.extension = function(extension) {
    if (typeof extension['bind'] == 'undefined') {
      throw new Error('Extension doesn\'t have required method "bind"');
    }

    extension.bind(this);

    this.extensions.push(extension);

    return this;
  };

  /**
   * Shortcut. Sets the window as scroll container.
   *
   * @public
   * @param option
   * @returns {*}
   */
  $.ias = function(option) {
    var $window = $(window);

    return $window.ias.apply($window, arguments);
  };

  /**
   * jQuery plugin initialization
   *
   * @public
   * @param option
   * @returns {*} the last IAS instance will be returned
   */
  $.fn.ias = function(option) {
    var args = Array.prototype.slice.call(arguments);
    var retval = this;

    this.each(function() {
      var $this = $(this),
          data = $this.data('ias'),
          options = $.extend({}, $.fn.ias.defaults, $this.data(), typeof option == 'object' && option)
      ;

      // set a new instance as data
      if (!data) {
        $this.data('ias', (data = new IAS($this, options)));

        $(document).ready($.proxy(data.initialize, data));
      }

      // when the plugin is called with a method
      if (typeof option === 'string') {
        if (typeof data[option] !== 'function') {
          throw new Error('There is no method called "' + option + '"');
        }

        args.shift(); // remove first argument ('option')
        data[option].apply(data, args);

        if (option === 'destroy') {
          $this.data('ias', null);
        }
      }

      retval = $this.data('ias');
    });

    return retval;
  };

  /**
   * Plugin defaults
   *
   * @public
   * @type {object}
   */
  $.fn.ias.defaults = {
    item: '.item',
    container: '.listing',
    next: '.next',
    pagination: false,
    delay: 600,
    negativeMargin: 10
  };
})(jQuery);
<commit_msg>always take last matching next element (issue #74)<commit_after>/**
 * Infinite Ajax Scroll v2.0.0
 * A jQuery plugin for infinite scrolling
 * http://infiniteajaxscroll.com
 *
 * Commercial use requires one-time purchase of a commercial license
 * http://infiniteajaxscroll.com/docs/license.html
 *
 * Non-commercial use is licensed under the MIT License
 *
 * Copyright 2014 Webcreate (Jeroen Fiege)
 */

(function($) {

  'use strict';

  var UNDETERMINED_SCROLLOFFSET = -1;

  var IAS = function($element, options) {
    this.itemsContainerSelector = options.container;
    this.itemSelector = options.item;
    this.nextSelector = options.next;
    this.paginationSelector = options.pagination;
    this.$scrollContainer = $element;
    this.$itemsContainer = $(this.itemsContainerSelector);
    this.$container = (window === $element.get(0) ? $(document) : $element);
    this.defaultDelay = options.delay;
    this.negativeMargin = options.negativeMargin;
    this.nextUrl = null;
    this.isBound = false;
    this.listeners = {
      next: new IASCallbacks(),
      load: new IASCallbacks(),
      didLoad: new IASCallbacks(),
      render: new IASCallbacks(),
      scroll: new IASCallbacks(),
      noneLeft: new IASCallbacks()
    };
    this.extensions = [];

    /**
     * Scroll event handler
     *
     * Note: calls to this functions should be throttled
     *
     * @private
     */
    this.scrollHandler = function() {
      var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
          scrollThreshold = this.getScrollThreshold()
      ;

      // the throttle method can call the scrollHandler even thought we have called unbind()
      if (!this.isBound) {
        return;
      }

      // invalid scrollThreshold. The DOM might not have loaded yet...
      if (UNDETERMINED_SCROLLOFFSET == scrollThreshold) {
        return;
      }

      this.fire('scroll', [currentScrollOffset, scrollThreshold]);

      if (currentScrollOffset >= scrollThreshold) {
        this.next();
      }
    };

    /**
     * Returns the last item currently in the DOM
     *
     * @private
     * @returns {object}
     */
    this.getLastItem = function() {
      return $(this.itemSelector, this.$itemsContainer.get(0)).last();
    };

    /**
     * Returns scroll threshold. This threshold marks the line from where
     * IAS should start loading the next page.
     *
     * @private
     * @param negativeMargin defaults to {this.negativeMargin}
     * @return {number}
     */
    this.getScrollThreshold = function(negativeMargin) {
      var lastElement;

      negativeMargin = negativeMargin || this.negativeMargin;
      negativeMargin = (negativeMargin >= 0 ? negativeMargin * -1 : negativeMargin);

      lastElement = this.getLastItem();

      // if the don't have a last element, the DOM might not have been loaded,
      // or the selector is invalid
      if (0 === lastElement.size()) {
        return UNDETERMINED_SCROLLOFFSET;
      }

      return (lastElement.offset().top + lastElement.height() + negativeMargin);
    };

    /**
     * Returns current scroll offset for the given scroll container
     *
     * @private
     * @param $container
     * @returns {number}
     */
    this.getCurrentScrollOffset = function($container) {
      var scrollTop = 0,
          containerHeight = $container.height();

      if (window === $container.get(0))  {
        scrollTop = $container.scrollTop();
      } else {
        scrollTop = $container.offset().top;
      }

      // compensate for iPhone
      if (navigator.platform.indexOf("iPhone") != -1 || navigator.platform.indexOf("iPod") != -1) {
        containerHeight += 80;
      }

      return (scrollTop + containerHeight);
    };

    /**
     * Returns the url for the next page
     *
     * @private
     */
    this.getNextUrl = function(container) {
      if (!container) {
        container = this.$container;
      }

      // always take the last matching item
      return $(this.nextSelector, container).last().attr('href');
    };

    /**
     * Loads a page url
     *
     * @param url
     * @param callback
     * @param delay
     * @returns {object}        jsXhr object
     */
    this.load = function(url, callback, delay) {
      var self = this,
          $itemContainer,
          items = [],
          timeStart = +new Date(),
          timeDiff;

      delay = delay || this.defaultDelay;

      return $.get(url, null, $.proxy(function(data) {
        $itemContainer = $(this.itemsContainerSelector, data).eq(0);
        if (0 === $itemContainer.length) {
          $itemContainer = $(data).filter(this.itemsContainerSelector).eq(0);
        }

        if ($itemContainer) {
          $itemContainer.find(this.itemSelector).each(function() {
            items.push(this);
          });
        }

        // @todo it's best practise to fire events at the beginning of the method
        self.fire('load', [data, items]);

        if (callback) {
          timeDiff = +new Date() - timeStart;
          if (timeDiff < delay) {
            setTimeout(function() {
              callback.call(self, data, items);
            }, delay - timeDiff);
          } else {
            callback.call(self, data, items);
          }
        }
      }, self), 'html');
    };

    /**
     * Renders items
     *
     * @param items
     */
    this.render = function(items) {
      var lastItem = this.getLastItem();

      this.fire('render', [items]);

      $(items).hide(); // at first, hide it so we can fade it in later

      lastItem.after(items);

      $(items).fadeIn();
    };

    /**
     * Hides the pagination
     */
    this.hidePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).hide();
      }
    };

    /**
     * Restores the pagination
     */
    this.restorePagination = function() {
      if (this.paginationSelector) {
        $(this.paginationSelector, this.$container).show();
      }
    };

    /**
     * Throttles a method
     *
     * Adopted from Ben Alman's jQuery throttle / debounce plugin
     *
     * @param callback
     * @param delay
     * @return {object}
     */
    this.throttle = function(callback, delay) {
      var lastExecutionTime = 0,
          wrapper,
          timerId
      ;

      wrapper = function() {
        var that = this,
            args = arguments,
            diff = +new Date() - lastExecutionTime;

        function execute() {
          lastExecutionTime = +new Date();
          callback.apply(that, args);
        }

        if (!timerId) {
          execute();
        } else {
          clearTimeout(timerId);
        }

        if (diff > delay) {
          execute();
        } else {
          timerId = setTimeout(execute, delay);
        }
      };

      if ($.guid) {
        wrapper.guid = callback.guid = callback.guid || $.guid++;
      }

      return wrapper;
    };

    /**
     * Fires an event with the ability to cancel further processing. This
     * can be achieved by returning false in a listener.
     *
     * @param event
     * @param args
     * @returns {*}
     */
    this.fire = function(event, args) {
      return this.listeners[event].fireWith(this, args);
    };

    return this;
  };

  /**
   * Initialize IAS
   *
   * Note: Should be called when the document is ready
   *
   * @public
   */
  IAS.prototype.initialize = function() {
    var currentScrollOffset = this.getCurrentScrollOffset(this.$scrollContainer),
        scrollThreshold = this.getScrollThreshold();

    this.hidePagination();
    this.bind();

    this.nextUrl = this.getNextUrl();

    // start loading next page if content is shorter than page fold
    if (currentScrollOffset >= scrollThreshold) {
      this.next();
    }

    return this;
  };

  /**
   * Binds IAS to DOM events
   *
   * @public
   */
  IAS.prototype.bind = function() {
    if (this.isBound) {
      return;
    }

    this.$scrollContainer.on('scroll', $.proxy(this.throttle(this.scrollHandler, 150), this));

    this.isBound = true;
  };

  /**
   * Unbinds IAS to events
   *
   * @public
   */
  IAS.prototype.unbind = function() {
    if (!this.isBound) {
      return;
    }

    this.$scrollContainer.off('scroll', this.scrollHandler);

    this.isBound = false;
  };

  /**
   * Destroys IAS instance
   *
   * @public
   */
  IAS.prototype.destroy = function() {
    this.unbind();
  };

  /**
   * Registers an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.on = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].add($.proxy(callback, this));

    return this;
  };

  /**
   * Removes an eventListener
   *
   * Note: chainable
   *
   * @public
   * @returns IAS
   */
  IAS.prototype.off = function(event, callback) {
    if (typeof this.listeners[event] == 'undefined') {
      throw new Error('There is no event called "' + event + '"');
    }

    this.listeners[event].remove(callback);

    return this;
  };

  /**
   * Load the next page
   *
   * @public
   */
  IAS.prototype.next = function() {
    var url = this.nextUrl,
        self = this;

    this.unbind();

    if (!url) {
      this.fire('noneLeft', [this.getLastItem()]);

      return false;
    }

    var promise = this.fire('next', [url]);

    promise.done(function() {
      self.load(url, function(data, items) {
        self.render(items);

        self.nextUrl = self.getNextUrl(data);

        self.bind();
      });
    });

    promise.fail(function() {
      self.bind();
    });

    return true;
  };

  /**
   * Adds an extension
   *
   * @public
   */
  IAS.prototype.extension = function(extension) {
    if (typeof extension['bind'] == 'undefined') {
      throw new Error('Extension doesn\'t have required method "bind"');
    }

    extension.bind(this);

    this.extensions.push(extension);

    return this;
  };

  /**
   * Shortcut. Sets the window as scroll container.
   *
   * @public
   * @param option
   * @returns {*}
   */
  $.ias = function(option) {
    var $window = $(window);

    return $window.ias.apply($window, arguments);
  };

  /**
   * jQuery plugin initialization
   *
   * @public
   * @param option
   * @returns {*} the last IAS instance will be returned
   */
  $.fn.ias = function(option) {
    var args = Array.prototype.slice.call(arguments);
    var retval = this;

    this.each(function() {
      var $this = $(this),
          data = $this.data('ias'),
          options = $.extend({}, $.fn.ias.defaults, $this.data(), typeof option == 'object' && option)
      ;

      // set a new instance as data
      if (!data) {
        $this.data('ias', (data = new IAS($this, options)));

        $(document).ready($.proxy(data.initialize, data));
      }

      // when the plugin is called with a method
      if (typeof option === 'string') {
        if (typeof data[option] !== 'function') {
          throw new Error('There is no method called "' + option + '"');
        }

        args.shift(); // remove first argument ('option')
        data[option].apply(data, args);

        if (option === 'destroy') {
          $this.data('ias', null);
        }
      }

      retval = $this.data('ias');
    });

    return retval;
  };

  /**
   * Plugin defaults
   *
   * @public
   * @type {object}
   */
  $.fn.ias.defaults = {
    item: '.item',
    container: '.listing',
    next: '.next',
    pagination: false,
    delay: 600,
    negativeMargin: 10
  };
})(jQuery);
<|endoftext|>


<commit_before>import React from "react";
import ReactDOM from "react-dom";

import Search from "./components/Search";

var searchDiv = document.getElementById("charles-search");
if(searchDiv == null) {
    searchDiv = document.createElement("div");
    searchDiv.setAttribute("id", "charles-search");
    document.body.appendChild(searchDiv);
}

function getQueryStringParams(url) {
    var queryStringParams = url.substring(url.indexOf("?") + 1, url.length).split('&');
    var result = {}, s2, i;
    for (i = 0; i < queryStringParams.length; i += 1) {
        s2 = queryStringParams[i].split('=');
        result[decodeURIComponent(s2[0]).toLowerCase()] = decodeURIComponent(s2[1]);
    }
    return result;
};

var autocomplete = '';
var search = '';
var scripts = document.getElementsByTagName('script');
var paramsFound=false;
for(var i=0;i<scripts.length;i++) {
  if(scripts[i].src.indexOf("/charles.min.js") != -1) {
    var params = getQueryStringParams(scripts[i].src);
    autocomplete = params['autocomplete'];
    search = params['search'];
    if(autocomplete && search) {
      paramsFound=true;
      break;
    }
  }
}
if(paramsFound) {
  ReactDOM.render(
    <Search
      autocompleteUrl={autocomplete}
      searchUrl={search}
    />,
    searchDiv
  );
} else {
  console.error("charles.min.js: Please specify urls for both autocomplete and search!");
}
<commit_msg>method doc<commit_after>import React from "react";
import ReactDOM from "react-dom";

import Search from "./components/Search";

var searchDiv = document.getElementById("charles-search");
if(searchDiv == null) {
    searchDiv = document.createElement("div");
    searchDiv.setAttribute("id", "charles-search");
    document.body.appendChild(searchDiv);
}

/**
 * Get the query params from the src url.
 */
function getQueryStringParams(url) {
    var queryStringParams = url.substring(url.indexOf("?") + 1, url.length).split('&');
    var result = {}, s2, i;
    for (i = 0; i < queryStringParams.length; i += 1) {
        s2 = queryStringParams[i].split('=');
        result[decodeURIComponent(s2[0]).toLowerCase()] = decodeURIComponent(s2[1]);
    }
    return result;
};

var autocomplete = '';
var search = '';
var scripts = document.getElementsByTagName('script');
var paramsFound=false;
for(var i=0;i<scripts.length;i++) {
  if(scripts[i].src.indexOf("/charles.min.js") != -1) {
    var params = getQueryStringParams(scripts[i].src);
    autocomplete = params['autocomplete'];
    search = params['search'];
    if(autocomplete && search) {
      paramsFound=true;
      break;
    }
  }
}
if(paramsFound) {
  ReactDOM.render(
    <Search
      autocompleteUrl={autocomplete}
      searchUrl={search}
    />,
    searchDiv
  );
} else {
  console.error("charles.min.js: Please specify urls for both autocomplete and search!");
}
<|endoftext|>


<commit_before>'use strict';

//
// Create the URL for pulling data from Yahoo.
//
var formatYahooUrl = function (code, options) {

    options = options || {};

    var url = 'http://ichart.yahoo.com/table.csv?s=' + code.toUpperCase() + '.AX';

    if (options.fromDate) {
        url += '&a=' + options.fromDate.getMonth(); // Month. Yahoo expects 0-based month.
        url += '&b=' + options.fromDate.getDate(); // Date.
        url += '&c=' + options.fromDate.getFullYear(); // Year.
    }

    if (options.toDate) {

        url += '&d=' + options.toDate.getMonth(); // Month. Yahoo expects 0-based month.
        url += '&e=' + options.toDate.getDate(); // Date.
        url += '&f=' + options.toDate.getFullYear(); // Year.
    }

    if (options.interval) {
        // Time interval.
        // https://code.google.com/p/yahoo-finance-managed/wiki/enumHistQuotesInterval
        var intervalCode;
        if (options.interval === 'daily') {
            intervalCode = 'd';
        }
        else if (options.interval === 'weekly') {
            intervalCode = 'w';
        }
        else if (options.interval === 'monthly') {
            intervalCode = 'm';
        }
        else {
            observer.onError(new Error("Invalid interval: " + options.interval + ", should be one of daily, weekly or monthly."));
        }

        url += '&g=' + intervalCode;
    }

    url += '&ignore=.csv'; // Docs say to add this.

    return url;
};

//
// Load CSV data from Yahoo.
//
var loadYahooData = function (code, options) {

    return new Promise(function (resolve, reject) {

        var yahooUrl = formatYahooUrl(code, options);
        var proxyUrl = 'http://cors.io/?u=' + encodeURIComponent(yahooUrl);
        $.ajax({
            type: 'GET',
            url: proxyUrl,
            dataType: 'text',
            crossDomain: true,
            success: function (data) {
                resolve(data);
            },
            error: function (err) {
                reject(err);
            },
        });
    });
};
<commit_msg>Added primitive parsing of Yahoo CSV data to arrays.<commit_after>'use strict';

//
// Create the URL for pulling data from Yahoo.
//
var formatYahooUrl = function (code, options) {

    options = options || {};

    var url = 'http://ichart.yahoo.com/table.csv?s=' + code.toUpperCase() + '.AX';

    if (options.fromDate) {
        url += '&a=' + options.fromDate.getMonth(); // Month. Yahoo expects 0-based month.
        url += '&b=' + options.fromDate.getDate(); // Date.
        url += '&c=' + options.fromDate.getFullYear(); // Year.
    }

    if (options.toDate) {

        url += '&d=' + options.toDate.getMonth(); // Month. Yahoo expects 0-based month.
        url += '&e=' + options.toDate.getDate(); // Date.
        url += '&f=' + options.toDate.getFullYear(); // Year.
    }

    if (options.interval) {
        // Time interval.
        // https://code.google.com/p/yahoo-finance-managed/wiki/enumHistQuotesInterval
        var intervalCode;
        if (options.interval === 'daily') {
            intervalCode = 'd';
        }
        else if (options.interval === 'weekly') {
            intervalCode = 'w';
        }
        else if (options.interval === 'monthly') {
            intervalCode = 'm';
        }
        else {
            observer.onError(new Error("Invalid interval: " + options.interval + ", should be one of daily, weekly or monthly."));
        }

        url += '&g=' + intervalCode;
    }

    url += '&ignore=.csv'; // Docs say to add this.

    return url;
};

//
// Parse CSV data from Yahoo API.
//
var parseYahooCsv = function (csv) {
    var lines = Enumerable.from(
            csv.split('\n')
        )
        .select(function (row) {
            return Enumerable.from(
                    row.split(',')
                )
                .select(function (col) {
                    return col.trim();
                })
                .toArray();
        })
        .where(function (cols) {
            if (cols.length > 1) {
                return true;
            }
            else if (cols.length == 1) {
                return cols[0].length > 0;
            }
            else {
                return false;
            }
        });

    var header = lines.take(1);

    var parsed = lines
        .skip(1) // Cut out header.
        .select(function (cols) {
            chai.assert(cols.length === 7);
            return [
                moment(cols[0]).toDate(),
                parseFloat(cols[1]),
                parseFloat(cols[2]),
                parseFloat(cols[3]),
                parseFloat(cols[4]),
                parseInt(cols[5]),
                parseFloat(cols[6]),
            ];
        })
        .toArray();

    var rows = header
        .concat(parsed)
        .toArray();

    return rows;
};

//
// Load CSV data from Yahoo.
//
var loadYahooData = function (code, options) {

    return new Promise(function (resolve, reject) {

        var yahooUrl = formatYahooUrl(code, options);
        var proxyUrl = 'http://cors.io/?u=' + encodeURIComponent(yahooUrl);
        $.ajax({
            type: 'GET',
            url: proxyUrl,
            dataType: 'text',
            crossDomain: true,
            success: function (data) {
                resolve(parseYahooCsv(data));
            },
            error: function (err) {
                reject(err);
            },
        });
    });
};
<|endoftext|>


<commit_before>/*
Copyright 2014 Sebastian Zimmer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

function ajax_get(url, success_callback){

	var http = new XMLHttpRequest();

	http.open("GET", url, true);

	http.onreadystatechange = function() { //Call a function when the state changes.

		if(http.readyState == 4 && http.status == 200) {

			console.log("AJAX successful!");

			var response = http.responseText;

			success_callback(response);

		}
	}

	console.log("Sending ajax request to: " + url);

	return http.send();

}

function readFileAsText(file, onsuccess){

	var reader = new FileReader();

	reader.onload = function(e){

		var result = e.target.result;

		onsuccess(result);

	};

	reader.readAsText(file);

}

function getFileTypeFromFilename(filename){

	var pos_of_dot = filename.lastIndexOf(".");

	return filename.slice(pos_of_dot+1,filename.length).toLowerCase();

}

function getFilenameFromUNIXFilePath(path){

	var pos_of_slash = path.lastIndexOf("/");

	if (pos_of_slash == -1){
		return path;
	}

	return path.substring(pos_of_slash+1);

}

function addScript(url, onloaded){

	console.log("adding script: " + url);

	var script = document.createElement("script");
	script.src = url;
	script.addEventListener("load", onloaded, false);

	document.head.appendChild(script);

}

function getDirectoryFromUNIXFilePath(path){

	var pos_of_slash = path.lastIndexOf("/");

	if (pos_of_slash == -1){
		return undefined;
	}

	return path.substring(0, pos_of_slash+1);

}

var linesToArray = function(string){

	return string.split("\n");

};

var getIndex = function(array, key, value){

	for (var i=0; i < array.length; i++){

		if (array[i][key] == value){
			return i;
		}
	}

	return undefined;

};

var getIndexByID = function(array, id){

	return getIndex(array, "id", id);

};

var getObject = function(array, key, value){

	for (var i=0; i < array.length; i++){

		if (array[i][key] == value){
			return array[i];
		}
	}

	return undefined;

};

var getObjectByID = function(array, id){

	return getObject(array, "id", id);

};

var map = function(array, transform) {
	var mapped = [];

	for (var i = 0; i < array.length; i++){
		mapped.push(transform(array[i]));
	}

	return mapped;
};

var getArrayWithValuesByKey = function(array, key){

	var new_array = map(array, function(item){
		return item[key];
	});

	return new_array;

}

var getArrayWithIDs = function(array){

	return getArrayWithValuesByKey(array, "id");

}

var filter = function(array, test) {
	var passed = [];
	for (var i = 0; i < array.length; i++) {
		if (test(array[i])){
			passed.push(array[i]);
		}
	}

	return passed;

};

var forEach = function(array, action) {
	for (var i = 0; i < array.length; i++){
		action(array[i], i);
	}
};

var getSelectedRadioIndex = function (radios){

	for (var r = 0; r < radios.length; r++){

		if (radios[r].checked === true){

			return r;

		}

	}

	return 0;

};

var getSelectedRadioValue = function (radios){

	return radios[getSelectedRadioIndex(radios)].value;

};

function get(name){

	var elem = document.getElementsByName(name);

	if (elem[0] && elem[0].nodeName == "INPUT" && elem[0].type == "radio"){
		return getSelectedRadioValue(elem);
	}

	elem = elem[0];

	if (typeof elem == "undefined"){

		elem = g(name);

	}

	if (typeof elem == "undefined"){

		console.error("get: Element " + name + " is undefined!");
		return;

	}

	switch (elem.nodeName){

		case "INPUT": return elem.value;

		case "TEXTAREA": return elem.value;

		case "SELECT": {

			if (elem.selectedIndex != -1){
				return elem.options[elem.selectedIndex].value;
			}

			else {
				return "";
			}

			break;
		}

		default: console.log("Function \"get\" has been misused with a "+elem.nodeName+" element. This should not have happened!");

	}
}

function g_value(id){

	return document.getElementById(id).options[document.getElementById(id).options.selectedIndex].value;

}

var g = function (id){

	if (document.getElementById(id)){
		return document.getElementById(id);
	}

	if (document.getElementsByName(id).length > 0){
		return document.getElementsByName(id);
	}

	return undefined;

};

function randomString(length, chars) {
	var result = '';
	for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
	return result;
}

function replaceAccentBearingLettersWithASCISubstitute(string){

	var text = string;

	if (typeof text != "string"){

		console.error("replaceAccentBearingLetters: Argument is not of type 'string'");

	}

	//A
	text = text.replace(/á/g,"a").replace(/à/g,"a").replace(/Á/g,"A").replace(/À/g,"A").replace(/ä/g,"ae").replace(/Ä/g,"Ae");

	//C
	text = text.replace(/Ç/g,"C").replace(/Ĉ/g,"C").replace(/ĉ/g,"c").replace(/ć/g,"c").replace(/č/g,"c").replace(/Ć/g,"C").replace(/Č/g,"C")
	.replace(/ï/g,"c").replace(/Ï/g,"C").replace(/ċ/g,"c").replace(/Ċ/g,"C");

	//E
	text = text.replace(/é/g,"e").replace(/è/g,"e").replace(/É/g,"E").replace(/È/g,"E").replace(/Ê/g,"E").replace(/ê/g,"e");

	//I
	text = text.replace(/î/g,"i").replace(/Î/g,"I").replace(/í/g,"i").replace(/ì/g,"i").replace(/Í/g,"I").replace(/Ì/g,"I")
	.replace(/ï/g,"i").replace(/Ï/g,"I");

	//O
	text = text.replace(/ö/g,"oe").replace(/Ö/g,"Oe").replace(/ó/g,"o").replace(/ò/g,"o").replace(/Ó/g,"O").replace(/Ò/g,"O")
	.replace(/Ô/g,"O").replace(/ô/g,"o").replace(/õ/g,"o");

	//U
	text.replace(/Ü/g,"Ue").replace(/ü/g,"ue").replace(/Û/g,"U").replace(/û/g,"u");

	//Y
	text.replace(/ÿ/g,"y").replace(/Ý/g,"Y");

	//ß
	text = text.replace(/ß/g,"ss").replace(/\s+/g, '_');

	return text;
}

var removeCharactersFromString = function (string, char_string){
	var character;
	var pos;

	for (var c=0; c< char_string.length; c++){

		character = char_string[c];

		while (string.indexOf(character) != -1){

			pos = string.indexOf(character);

			string = string.slice(0, pos) + string.slice(pos+1, string.length);

		}

	}

	return string;
};

var removeAllCharactersFromStringExcept = function(string, valid_chars){

	var character;
	var pos;

	for (var c = 0; c < string.length;){

		character = string[c];

		if (valid_chars.indexOf(character) == -1){

			pos = string.indexOf(character);

			string = string.slice(0, pos) + string.slice(pos+1, string.length);

		}

		else {

			c++;

		}

	}

	return string;

}

var replaceCharactersInStringWithSubstitute = function (string, char_string, substitute){

	var character;

	//for each char in char_string, i. e. for each char, that is to be replaced
	for (var c=0; c< char_string.length; c++){

		character = char_string[c];

		for (var i=0; i<string.length;){  //here it is important, that string.length gets evaluated anew each loop

			if (string[i] == character) {

				string = string.slice(0, i) + substitute + string.slice(i+1);

				//move the pointer to the point after the substitute and continue
				i += substitute.length;
				continue;
			}

			i += 1;

		}

	}

	return string;
};

function calcAgeAtDate(dateString,birthDate) {

	var date = +new Date(dateString);
	var birthday = +new Date(birthDate);
	return ~~((date - birthday) / (31557600000));
}

function isSubstringAStartOfAWordInString(string, substring){

	string = string.toLowerCase();
	substring = substring.toLowerCase();

	//check if there is no letter in front of the substring
	switch (string.indexOf(substring)){

		case -1: {   //if substring is not part of string
			return false;
		}

		case 0: {  //if substring is at the beginning of the string
			return true;
		}

		default: { //if substring is somewhere in string, check if the character before substring is a letter

			var char_before_substring = string[string.indexOf(substring)-1];

			if (",. ".indexOf(char_before_substring) != -1){

				return true;

			}

			else {

				return false;

			}

		}

	}

}

function removeEndingFromFilename(filename){

	var pos_of_dot = filename.lastIndexOf(".");

	return filename.slice(0,pos_of_dot);

}

function o(object, property_array){
//returns value of object properties if they exist, if not returns ""

	var value = object;

	for (var p=0; p<property_array.length; p++){

		if (property_array[p] in value){
			value = value[property_array[p]];

		}

		else {
			return "";
		}

	}

	return value;

}

function a(array,index){

	var list = [];

	for (var i=0;i<array.length;i++){

		list.push(array[i][index]);

	}

	return list;

}

function today(){

	var now = new Date();

	var day = now.getDate();
	if (day < 10) {day = "0" + day;}

	var month = now.getMonth();
	month += 1;  //as months begin with 0 here
	if (month < 10) {month = "0" + month;}

	var today_date = now.getFullYear() + "-" + month + "-" + day;

	return today_date;
}

function bytesToSize(bytes, precision){

    var kilobyte = 1024;
    var megabyte = kilobyte * 1024;
    var gigabyte = megabyte * 1024;
    var terabyte = gigabyte * 1024;

    if ((bytes >= 0) && (bytes < kilobyte)) {
        return bytes + ' B';

    }

	else if ((bytes >= kilobyte) && (bytes < megabyte)) {
        return (bytes / kilobyte).toFixed(precision) + ' KB';

    }

	else if ((bytes >= megabyte) && (bytes < gigabyte)) {
        return (bytes / megabyte).toFixed(precision) + ' MB';

    }

	else if ((bytes >= gigabyte) && (bytes < terabyte)) {
        return (bytes / gigabyte).toFixed(precision) + ' GB';

    }

	else if (bytes >= terabyte) {
        return (bytes / terabyte).toFixed(precision) + ' TB';

    }

	else {
        return bytes + ' B';
    }
}

function sortBySubKey(array,keys){

    return array.sort(function(a, b) {
        var x = a[keys[0]][keys[1]];
        var y = b[keys[0]][keys[1]];

        if (typeof x == "string"){
            x = x.toLowerCase();
            y = y.toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });

}

function sortByKey(array, key) {

    return array.sort(function(a, b) {
        var x = a[key];
        var y = b[key];

        if (typeof x == "string"){
            x = x.toLowerCase();
            y = y.toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

function parseDate(str){

	var t = str.match(/([1-2][0-9][0-9][0-9])\-([0-1][0-9])\-([0-3][0-9])/);

	if(t!==null){

		var y=+t[1], m=+t[2], d=+t[3];
		var date = new Date(y,m-1,d);

		if(date.getFullYear()===y && date.getMonth()===m-1){
			return {

				year: t[1],
				month: t[2],
				day: t[3]

			};
		}

	}

	return null;
}

var parse_birth_date = function(string){
	var year;
	var month;
	var day;

	if (string.length != 10 || string == "Unspecified" || string == "Unknown"){
		year = "YYYY";
		month = "MM";
		day = "DD";
	}

	else {

		year = string.slice(0,4);
		month = string.slice(5,7);
		day = string.slice(8,10);

	}

	var object = {

		year: year,
		month: month,
		day: day

	};

	return object;

};

var areOnlyTheseCharsInString = function(string, chars){

	for (var i=0; i<string.length; i++){

		if (chars.indexOf(string[i]) == -1){
			return false;
		}

	}

	return true;

}<commit_msg>helpers: new method remove to insert later<commit_after>/*
Copyright 2014 Sebastian Zimmer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

function ajax_get(url, success_callback){

	var http = new XMLHttpRequest();

	http.open("GET", url, true);

	http.onreadystatechange = function() { //Call a function when the state changes.

		if(http.readyState == 4 && http.status == 200) {

			console.log("AJAX successful!");

			var response = http.responseText;

			success_callback(response);

		}
	}

	console.log("Sending ajax request to: " + url);

	return http.send();

}

function readFileAsText(file, onsuccess){

	var reader = new FileReader();

	reader.onload = function(e){

		var result = e.target.result;

		onsuccess(result);

	};

	reader.readAsText(file);

}

function getFileTypeFromFilename(filename){

	var pos_of_dot = filename.lastIndexOf(".");

	return filename.slice(pos_of_dot+1,filename.length).toLowerCase();

}

function getFilenameFromUNIXFilePath(path){

	var pos_of_slash = path.lastIndexOf("/");

	if (pos_of_slash == -1){
		return path;
	}

	return path.substring(pos_of_slash+1);

}

function addScript(url, onloaded){

	console.log("adding script: " + url);

	var script = document.createElement("script");
	script.src = url;
	script.addEventListener("load", onloaded, false);

	document.head.appendChild(script);

}

function getDirectoryFromUNIXFilePath(path){

	var pos_of_slash = path.lastIndexOf("/");

	if (pos_of_slash == -1){
		return undefined;
	}

	return path.substring(0, pos_of_slash+1);

}

var linesToArray = function(string){

	return string.split("\n");

};

var getIndex = function(array, key, value){

	for (var i=0; i < array.length; i++){

		if (array[i][key] == value){
			return i;
		}
	}

	return undefined;

};

var getIndexByID = function(array, id){

	return getIndex(array, "id", id);

};

var getObject = function(array, key, value){

	for (var i=0; i < array.length; i++){

		if (array[i][key] == value){
			return array[i];
		}
	}

	return undefined;

};

var getObjectByID = function(array, id){

	return getObject(array, "id", id);

};

var map = function(array, transform) {
	var mapped = [];

	for (var i = 0; i < array.length; i++){
		mapped.push(transform(array[i]));
	}

	return mapped;
};

var getArrayWithValuesByKey = function(array, key){

	var new_array = map(array, function(item){
		return item[key];
	});

	return new_array;

}

var getArrayWithIDs = function(array){

	return getArrayWithValuesByKey(array, "id");

}

var filter = function(array, test) {
	var passed = [];
	for (var i = 0; i < array.length; i++) {
		if (test(array[i])){
			passed.push(array[i]);
		}
	}

	return passed;

};

var forEach = function(array, action) {
	for (var i = 0; i < array.length; i++){
		action(array[i], i);
	}
};

var getSelectedRadioIndex = function (radios){

	for (var r = 0; r < radios.length; r++){

		if (radios[r].checked === true){

			return r;

		}

	}

	return 0;

};

var getSelectedRadioValue = function (radios){

	return radios[getSelectedRadioIndex(radios)].value;

};

function get(name){

	var elem = document.getElementsByName(name);

	if (elem[0] && elem[0].nodeName == "INPUT" && elem[0].type == "radio"){
		return getSelectedRadioValue(elem);
	}

	elem = elem[0];

	if (typeof elem == "undefined"){

		elem = g(name);

	}

	if (typeof elem == "undefined"){

		console.error("get: Element " + name + " is undefined!");
		return;

	}

	switch (elem.nodeName){

		case "INPUT": return elem.value;

		case "TEXTAREA": return elem.value;

		case "SELECT": {

			if (elem.selectedIndex != -1){
				return elem.options[elem.selectedIndex].value;
			}

			else {
				return "";
			}

			break;
		}

		default: console.log("Function \"get\" has been misused with a "+elem.nodeName+" element. This should not have happened!");

	}
}

function g_value(id){

	return document.getElementById(id).options[document.getElementById(id).options.selectedIndex].value;

}

var g = function (id){

	if (document.getElementById(id)){
		return document.getElementById(id);
	}

	if (document.getElementsByName(id).length > 0){
		return document.getElementsByName(id);
	}

	return undefined;

};

/**
 * Remove an element and provide a function that inserts it into its original position
 * @param element {Element} The element to be temporarily removed
 * @return {Function} A function that inserts the element into its original position
 **/
var removeToInsertLater = function(element) {
	var parentNode = element.parentNode;
	var nextSibling = element.nextSibling;
	parentNode.removeChild(element);
	return function() {
		if (nextSibling) {
			parentNode.insertBefore(element, nextSibling);
		} else {
			parentNode.appendChild(element);
		}
	};
}

function randomString(length, chars) {
	var result = '';
	for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
	return result;
}

function replaceAccentBearingLettersWithASCISubstitute(string){

	var text = string;

	if (typeof text != "string"){

		console.error("replaceAccentBearingLetters: Argument is not of type 'string'");

	}

	//A
	text = text.replace(/á/g,"a").replace(/à/g,"a").replace(/Á/g,"A").replace(/À/g,"A").replace(/ä/g,"ae").replace(/Ä/g,"Ae");

	//C
	text = text.replace(/Ç/g,"C").replace(/Ĉ/g,"C").replace(/ĉ/g,"c").replace(/ć/g,"c").replace(/č/g,"c").replace(/Ć/g,"C").replace(/Č/g,"C")
	.replace(/ï/g,"c").replace(/Ï/g,"C").replace(/ċ/g,"c").replace(/Ċ/g,"C");

	//E
	text = text.replace(/é/g,"e").replace(/è/g,"e").replace(/É/g,"E").replace(/È/g,"E").replace(/Ê/g,"E").replace(/ê/g,"e");

	//I
	text = text.replace(/î/g,"i").replace(/Î/g,"I").replace(/í/g,"i").replace(/ì/g,"i").replace(/Í/g,"I").replace(/Ì/g,"I")
	.replace(/ï/g,"i").replace(/Ï/g,"I");

	//O
	text = text.replace(/ö/g,"oe").replace(/Ö/g,"Oe").replace(/ó/g,"o").replace(/ò/g,"o").replace(/Ó/g,"O").replace(/Ò/g,"O")
	.replace(/Ô/g,"O").replace(/ô/g,"o").replace(/õ/g,"o");

	//U
	text.replace(/Ü/g,"Ue").replace(/ü/g,"ue").replace(/Û/g,"U").replace(/û/g,"u");

	//Y
	text.replace(/ÿ/g,"y").replace(/Ý/g,"Y");

	//ß
	text = text.replace(/ß/g,"ss").replace(/\s+/g, '_');

	return text;
}

var removeCharactersFromString = function (string, char_string){
	var character;
	var pos;

	for (var c=0; c< char_string.length; c++){

		character = char_string[c];

		while (string.indexOf(character) != -1){

			pos = string.indexOf(character);

			string = string.slice(0, pos) + string.slice(pos+1, string.length);

		}

	}

	return string;
};

var removeAllCharactersFromStringExcept = function(string, valid_chars){

	var character;
	var pos;

	for (var c = 0; c < string.length;){

		character = string[c];

		if (valid_chars.indexOf(character) == -1){

			pos = string.indexOf(character);

			string = string.slice(0, pos) + string.slice(pos+1, string.length);

		}

		else {

			c++;

		}

	}

	return string;

}

var replaceCharactersInStringWithSubstitute = function (string, char_string, substitute){

	var character;

	//for each char in char_string, i. e. for each char, that is to be replaced
	for (var c=0; c< char_string.length; c++){

		character = char_string[c];

		for (var i=0; i<string.length;){  //here it is important, that string.length gets evaluated anew each loop

			if (string[i] == character) {

				string = string.slice(0, i) + substitute + string.slice(i+1);

				//move the pointer to the point after the substitute and continue
				i += substitute.length;
				continue;
			}

			i += 1;

		}

	}

	return string;
};

function calcAgeAtDate(dateString,birthDate) {

	var date = +new Date(dateString);
	var birthday = +new Date(birthDate);
	return ~~((date - birthday) / (31557600000));
}

function isSubstringAStartOfAWordInString(string, substring){

	string = string.toLowerCase();
	substring = substring.toLowerCase();

	//check if there is no letter in front of the substring
	switch (string.indexOf(substring)){

		case -1: {   //if substring is not part of string
			return false;
		}

		case 0: {  //if substring is at the beginning of the string
			return true;
		}

		default: { //if substring is somewhere in string, check if the character before substring is a letter

			var char_before_substring = string[string.indexOf(substring)-1];

			if (",. ".indexOf(char_before_substring) != -1){

				return true;

			}

			else {

				return false;

			}

		}

	}

}

function removeEndingFromFilename(filename){

	var pos_of_dot = filename.lastIndexOf(".");

	return filename.slice(0,pos_of_dot);

}

function o(object, property_array){
//returns value of object properties if they exist, if not returns ""

	var value = object;

	for (var p=0; p<property_array.length; p++){

		if (property_array[p] in value){
			value = value[property_array[p]];

		}

		else {
			return "";
		}

	}

	return value;

}

function a(array,index){

	var list = [];

	for (var i=0;i<array.length;i++){

		list.push(array[i][index]);

	}

	return list;

}

function today(){

	var now = new Date();

	var day = now.getDate();
	if (day < 10) {day = "0" + day;}

	var month = now.getMonth();
	month += 1;  //as months begin with 0 here
	if (month < 10) {month = "0" + month;}

	var today_date = now.getFullYear() + "-" + month + "-" + day;

	return today_date;
}

function bytesToSize(bytes, precision){

    var kilobyte = 1024;
    var megabyte = kilobyte * 1024;
    var gigabyte = megabyte * 1024;
    var terabyte = gigabyte * 1024;

    if ((bytes >= 0) && (bytes < kilobyte)) {
        return bytes + ' B';

    }

	else if ((bytes >= kilobyte) && (bytes < megabyte)) {
        return (bytes / kilobyte).toFixed(precision) + ' KB';

    }

	else if ((bytes >= megabyte) && (bytes < gigabyte)) {
        return (bytes / megabyte).toFixed(precision) + ' MB';

    }

	else if ((bytes >= gigabyte) && (bytes < terabyte)) {
        return (bytes / gigabyte).toFixed(precision) + ' GB';

    }

	else if (bytes >= terabyte) {
        return (bytes / terabyte).toFixed(precision) + ' TB';

    }

	else {
        return bytes + ' B';
    }
}

function sortBySubKey(array,keys){

    return array.sort(function(a, b) {
        var x = a[keys[0]][keys[1]];
        var y = b[keys[0]][keys[1]];

        if (typeof x == "string"){
            x = x.toLowerCase();
            y = y.toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });

}

function sortByKey(array, key) {

    return array.sort(function(a, b) {
        var x = a[key];
        var y = b[key];

        if (typeof x == "string"){
            x = x.toLowerCase();
            y = y.toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

function parseDate(str){

	var t = str.match(/([1-2][0-9][0-9][0-9])\-([0-1][0-9])\-([0-3][0-9])/);

	if(t!==null){

		var y=+t[1], m=+t[2], d=+t[3];
		var date = new Date(y,m-1,d);

		if(date.getFullYear()===y && date.getMonth()===m-1){
			return {

				year: t[1],
				month: t[2],
				day: t[3]

			};
		}

	}

	return null;
}

var parse_birth_date = function(string){
	var year;
	var month;
	var day;

	if (string.length != 10 || string == "Unspecified" || string == "Unknown"){
		year = "YYYY";
		month = "MM";
		day = "DD";
	}

	else {

		year = string.slice(0,4);
		month = string.slice(5,7);
		day = string.slice(8,10);

	}

	var object = {

		year: year,
		month: month,
		day: day

	};

	return object;

};

var areOnlyTheseCharsInString = function(string, chars){

	for (var i=0; i<string.length; i++){

		if (chars.indexOf(string[i]) == -1){
			return false;
		}

	}

	return true;

}<|endoftext|>


<commit_before>/*
 * @title yakcd
 * @description Yet Another Kindle Cloud Downloader
 * @include https://read.amazon.com
 * @include https://read.amazon.co.jp
 * @license MIT License
 */

$.when(
  $.getScript("//asannou.github.io/yakcd/jszip/dist/jszip.min.js"),
  $.getScript("//asannou.github.io/yakcd/jszip/vendor/FileSaver.js")
).
done(function() {

var Indicator = function() {
  var originalTitle = document.title;
  var value = 0;
  var maximum = 1;
  return {
    setMaximum: function(m) {
      maximum = m;
    },
    increment: function() {
      value++;
    },
    display: function() {
      var percent = Math.round(value * 100 / maximum);
      document.title = "[" + percent + "%] " + originalTitle;
    },
    incrementAndDisplay: function() {
      this.increment();
      this.display();
    },
    clear: function() {
      value = 0;
      document.title = originalTitle;
    }
  };
}();

var Blocker = function() {
  var div = $('<div/>').
  css({
    top: 0,
    right: 0,
    width: "100%",
    height: "100%",
    position: "fixed",
    zIndex: 1000,
    background: "#000",
    opacity: 0.5
  }).
  hide().
  appendTo("body");
  return {
    block: function() {
      div.animate({ opacity: "show" });
    },
    unblock: function() {
      div.animate({ opacity: "hide" });
    }
  };
}();

var Ajax = function() {
  var deviceSessionToken;
  var getDeviceToken = function() {
    if (deviceSessionToken) {
      return $.Deferred().resolve(deviceSessionToken);
    }
    return KindleModuleManager.
    getModule(KindleModuleManager.DB_CLIENT).
    pipe(function(module){
      return module.getAppDb().getDeviceToken();
    }).
    done(function(token){
      deviceSessionToken = token["deviceSessionToken"];
      return $.Deferred().resolve(deviceSessionToken);
    });
  };
  return {
    get: function() {
      var url = arguments[0];
      var param = "";
      if (arguments[1]) {
        param = "?" + $.param(arguments[1]);
      }
      return getDeviceToken().
      pipe(function(sessionToken) {
        return $.ajax({
          url: url + param,
          headers: {
            "X-ADP-Session-Token": sessionToken
          }
        });
      });
    }
  };
}();

var Yakcd = function(asin) {

return $.Deferred().
resolve().
pipe(function(content) {
  Indicator.display();
  return Ajax.get(
    "/service/web/reader/startReading", {
      asin: asin,
      clientVersion: KindleVersion.getVersionNumber()
    }
  );
}).
pipe(function(book) {
  return $.Deferred().
  resolve().
  pipe(function() {
    return $.ajax({
      url: book["manifestUrl"],
      dataType: "jsonp",
      jsonpCallback: "loadManifest"
    });
  }).
  pipe(function(manifest) {
    var id = $.map(manifest["resourceManifest"], function(m, i) {
      var type = m["type"].split("/")[0];
      if (type == "image") {
        return i;
      } else {
        return null;
      }
    });
    Indicator.setMaximum(id.length);
    return Ajax.get(
      "/service/web/reader/getFileUrl", {
        asin: asin,
        contentVersion: book["contentVersion"],
        formatVersion: book["formatVersion"],
        kindleSessionId: book["kindleSessionId"],
        resourceIds: id.join(",")
      }
    );
  });
}).
pipe(function(url) {
  var zip = new JSZip();
  return $.when.apply($, $.map(url["resourceUrls"], function(u, i) {
    return $.ajax({
      url: u["signedUrl"],
      dataType: "jsonp",
      jsonpCallback: "loadResource" + u["id"],
      timeout: 0
    }).
    success(function(resource) {
      var id = ("000" + resource["metadata"]["id"]).substr(-4);
      var type = resource["metadata"]["type"].split("/")[1];
      var data = resource["data"].split(",")[1];
      zip.file("resource" + id + "." + type, data, { base64: true });
      Indicator.incrementAndDisplay();
    });
  })).
  done(function() {
    var content = zip.generate({ type: "blob" });
    saveAs(content, asin + ".zip");
    Indicator.clear();
  });
});

};

var iframe = $("#KindleLibraryIFrame").contents();

$("<link/>").
attr({
  rel: "stylesheet",
  type: "text/css",
  href: "//asannou.github.io/yakcd/yakcd.css"
}).
appendTo(iframe.find("head"));

iframe.
find(".book_container").
each(function(){
  var bookImage = $(this).find(".book_image");
  offset = bookImage.offset();
  offset.left += bookImage.width() - 16;
  offset.top -= 16;
  $("<div/>").
  appendTo($(this)).
  attr("class", "yakcdButton").
  css("position", "absolute").
  offset(offset).
  click(function() {
    Blocker.block();
    var asin = $(this).parent().attr("id");
    Yakcd(asin).done(function() {
      Blocker.unblock();
    });
  }).
  append($("<div/>").attr("class", "cloudDown")).
  append($("<div/>").attr("class", "cloudDownArrow"));
});

});

<commit_msg>既存のプログレスダイアログを使う<commit_after>/*
 * @title yakcd
 * @description Yet Another Kindle Cloud Downloader
 * @include https://read.amazon.com
 * @include https://read.amazon.co.jp
 * @license MIT License
 */

$.when(
  $.getScript("//asannou.github.io/yakcd/jszip/dist/jszip.min.js"),
  $.getScript("//asannou.github.io/yakcd/jszip/vendor/FileSaver.js")
).
done(function() {

var iframeWindow = $("#KindleLibraryIFrame").get(0).contentWindow;
var iframeDocument = $(iframeWindow.document);

var Indicator = function() {
  var originalTitle = document.title;
  var dialog = iframeWindow.KindleLibraryProgressDialog;
  var value = 0;
  var maximum = 1;
  return {
    setMaximum: function(m) {
      maximum = m;
    },
    increment: function() {
      value++;
    },
    display: function() {
      var percent = Math.round(value * 100 / maximum);
      document.title = "[" + percent + "%] " + originalTitle;
      if (!value) {
        this.openDialog();
      }
      dialog.updateValue(percent);
    },
    incrementAndDisplay: function() {
      this.increment();
      this.display();
    },
    clear: function() {
      value = 0;
      document.title = originalTitle;
      this.closeDialog();
    },
    openDialog: function() {
      dialog.open(function() {
        location.reload();
      });
      iframeDocument.find("#kindleLibrary_dialog_progressMessage").hide();
    },
    closeDialog: function() {
      iframeDocument.find("#kindleLibrary_dialog_progressMessage").show();
      dialog.close();
    }
  };
}();

var Ajax = function() {
  var deviceSessionToken;
  var getDeviceToken = function() {
    if (deviceSessionToken) {
      return $.Deferred().resolve(deviceSessionToken);
    }
    return KindleModuleManager.
    getModule(KindleModuleManager.DB_CLIENT).
    pipe(function(module){
      return module.getAppDb().getDeviceToken();
    }).
    done(function(token){
      deviceSessionToken = token["deviceSessionToken"];
      return $.Deferred().resolve(deviceSessionToken);
    });
  };
  return {
    get: function() {
      var url = arguments[0];
      var param = "";
      if (arguments[1]) {
        param = "?" + $.param(arguments[1]);
      }
      return getDeviceToken().
      pipe(function(sessionToken) {
        return $.ajax({
          url: url + param,
          headers: {
            "X-ADP-Session-Token": sessionToken
          }
        });
      });
    }
  };
}();

var Yakcd = function(asin) {

return $.Deferred().
resolve().
pipe(function(content) {
  Indicator.display();
  return Ajax.get(
    "/service/web/reader/startReading", {
      asin: asin,
      clientVersion: KindleVersion.getVersionNumber()
    }
  );
}).
pipe(function(book) {
  return $.Deferred().
  resolve().
  pipe(function() {
    return $.ajax({
      url: book["manifestUrl"],
      dataType: "jsonp",
      jsonpCallback: "loadManifest"
    });
  }).
  pipe(function(manifest) {
    var id = $.map(manifest["resourceManifest"], function(m, i) {
      var type = m["type"].split("/")[0];
      if (type == "image") {
        return i;
      } else {
        return null;
      }
    });
    Indicator.setMaximum(id.length);
    return Ajax.get(
      "/service/web/reader/getFileUrl", {
        asin: asin,
        contentVersion: book["contentVersion"],
        formatVersion: book["formatVersion"],
        kindleSessionId: book["kindleSessionId"],
        resourceIds: id.join(",")
      }
    );
  });
}).
pipe(function(url) {
  var zip = new JSZip();
  return $.when.apply($, $.map(url["resourceUrls"], function(u, i) {
    return $.ajax({
      url: u["signedUrl"],
      dataType: "jsonp",
      jsonpCallback: "loadResource" + u["id"],
      timeout: 0
    }).
    success(function(resource) {
      var id = ("000" + resource["metadata"]["id"]).substr(-4);
      var type = resource["metadata"]["type"].split("/")[1];
      var data = resource["data"].split(",")[1];
      zip.file("resource" + id + "." + type, data, { base64: true });
      Indicator.incrementAndDisplay();
    });
  })).
  done(function() {
    var content = zip.generate({ type: "blob" });
    saveAs(content, asin + ".zip");
    Indicator.clear();
  });
});

};

$("<link/>").
attr({
  rel: "stylesheet",
  type: "text/css",
  href: "//asannou.github.io/yakcd/yakcd.css"
}).
appendTo(iframeDocument.find("head"));

iframeDocument.
find(".book_container").
each(function(){
  var bookImage = $(this).find(".book_image");
  offset = bookImage.offset();
  offset.left += bookImage.width() - 16;
  offset.top -= 16;
  $("<div/>").
  appendTo($(this)).
  attr("class", "yakcdButton").
  css("position", "absolute").
  offset(offset).
  click(function() {
    var asin = $(this).parent().attr("id");
    Yakcd(asin);
  }).
  append($("<div/>").attr("class", "cloudDown")).
  append($("<div/>").attr("class", "cloudDownArrow"));
});

});

<|endoftext|>