<commit_before>import * as actions from './';
import * as types from '../constants/action-types';
import thunkMiddleware from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import panes from '../constants/pane-types';
import { initializeCalculator } from '../lib/calculator';
import * as calcHelpers from '../lib/calc-helpers';
import * as inputHelpers from '../lib/input-helpers';

const middlewares = [thunkMiddleware];
const mockStore = configureMockStore(middlewares);

const initialState = {
  images: {
    frames: {},
    frameIDs: [],
    gifProgress: 0,
    gifData: '',
    caption: '',
    fontColor: '#000000',
    gifFileName: ''
  },
  settings: {
    image: {
      width: 300,
      height: 300,
      interval: 100,
      oversample: false
    },
    bounds: {
      left: -10,
      right: 10,
      bottom: -10,
      top: 10
    },
    strategy: 'contain'
  },
  ui: {
    expandedPane: panes.NONE,
    previewIdx: 0,
    playing: false,
    error: ''
  }
};

// Tests

describe('Action creators', () => {
  describe('synchronous action creators', () => {
    it('return an action to add a frame', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_FRAME,
        payload: {
          id: 1,
          imageData
        }
      };
      expect(actions.addFrame(imageData)).toEqual(expected);
    });

    it('return an action to add frame from storage', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_FRAME,
        payload: {
          id: 0,
          imageData
        }
      };
      expect(actions.addSavedFrame(imageData, 0)).toEqual(expected);
    });

    it('return an action to update GIF creation progress', () => {
      const progress = 0.5;
      const expected = {
        type: types.UPDATE_GIF_PROGRESS,
        payload: { progress }
      };
      expect(actions.updateGIFProgress(progress)).toEqual(expected);
    });

    it('return an action to update GIF file name', () => {
      const gifFileName = 'name';
      const expected = {
        type: types.UPDATE_GIF_FILENAME,
        payload: { gifFileName }
      };
      expect(actions.updateGIFFileName('name')).toEqual(expected);
    });

    it('return an action to update GIF text', () => {
      const text = 'text';
      const expected = {
        type: types.UPDATE_TEXT,
        payload: { text }
      };
      expect(actions.updateText('text')).toEqual(expected);
    });

    it('return an action to update GIF text color', () => {
      const fontColor = '#FFFFFF';
      const expected = {
        type: types.UPDATE_TEXT_COLOR,
        payload: { fontColor }
      };
      expect(actions.updateTextColor('#FFFFFF')).toEqual(expected);
    });

    it('return an action to update GIF text position', () => {
      const textOpts = {
        textAlign: 'left',
        textBaseline: 'top'
      };
      const expected = {
        type: types.UPDATE_TEXT_POSITION,
        payload: { textAlign: 'left', textBaseline: 'top' }
      };
      expect(actions.updateTextPosition(textOpts)).toEqual(expected);
    });

    it('return an action to add processed GIF data', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_GIF,
        payload: { imageData }
      };
      expect(actions.addGIF(imageData)).toEqual(expected);
    });

    it('return an action to undo a captured burst', () => {
      const expected = {
        type: types.UNDO_BURST,
        payload: { frames: { 0: 'zero' }, frameIDs: [0] }
      };
      expect(actions.undoBurst({ 0: 'zero' }, [0])).toEqual(expected);
    });

    it('return an action to toggle the visible pane', () => {
      const pane = panes.PREVIEW;
      const expected = {
        type: types.TOGGLE_PANE,
        payload: { pane }
      };
      expect(actions.togglePane(panes.PREVIEW)).toEqual(expected);
    });

    it('return an action to update the preview index', () => {
      const idx = 2;
      const expected = {
        type: types.UPDATE_PREVIEW_IDX,
        payload: { idx }
      };
      expect(actions.updatePreviewIdx(idx)).toEqual(expected);
    });

    it('return an action to update a setting', () => {
      const imageSettings = {
        width: 400,
        height: 200,
        interval: 250,
        oversample: true
      };
      const boundsSettings = {
        left: -20,
        right: 20,
        top: 20,
        bottom: -20
      };

      let key;
      for (key in imageSettings) {
        expect(actions.updateSetting(key, imageSettings[key])).toEqual({
          type: types.UPDATE_IMAGE_SETTING,
          payload: {
            [key]: imageSettings[key]
          }
        });
      }

      for (key in boundsSettings) {
        expect(actions.updateSetting(key, boundsSettings[key])).toEqual({
          type: types.UPDATE_BOUNDS_SETTING,
          payload: {
            [key]: boundsSettings[key]
          }
        });
      }

      expect(actions.updateSetting('strategy', 'stretch')).toEqual({
        type: types.UPDATE_STRATEGY,
        payload: { strategy: 'stretch' }
      });
    });

    it('return an action to play the preview animation', () => {
      expect(actions.playPreview()).toEqual({ type: types.PLAY_PREVIEW });
    });

    it('return an action to pause the preview animation', () => {
      expect(actions.stopAnimation()).toEqual({ type: types.PAUSE_PREVIEW });
    });

    it('return an action to set an error', () => {
      const message = 'error';
      const expected = {
        type: types.SET_ERROR,
        payload: { message }
      };
      expect(actions.setError(message)).toEqual(expected);
    });

    it('return an action to clear an error', () => {
      expect(actions.clearError()).toEqual({ type: types.CLEAR_ERROR });
    });

    it('return an action to reset the frames', () => {
      expect(actions.reset()).toEqual({ type: types.RESET });
    });
  });

  describe('async action creators', () => {
    let store;
    beforeEach(() => {
      store = mockStore(initialState);
    });

    it('return an action to temporarily display error message', () => {
      jest.useFakeTimers();
      const expectedActions = [
        { type: types.SET_ERROR, payload: { message: 'error' } },
        { type: types.CLEAR_ERROR }
      ];
      store.dispatch(actions.flashError('error'));
      jest.runAllTimers();
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return an action to request a frame from the calculator', () => {
      const opts = {
        height: 300,
        targetPixelRatio: 1,
        width: 300,
        mode: 'contain',
        mathBounds: {
          left: -10,
          right: 10,
          bottom: -10,
          top: 10
        }
      };
      const expectedActions = [
        {
          type: types.ADD_FRAME,
          payload: { id: 2, imageData: expect.any(String) }
        }
      ];
      initializeCalculator(global.Desmos, { current: undefined });
      return store.dispatch(actions.requestFrame(opts)).then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
    });

    it('return an action to request a burst of frames from the calculator', () => {
      const opts = {
        height: 300,
        idx: 1,
        max: 3,
        min: -3,
        oversample: false,
        step: 1,
        width: 300,
        frameIDs: [0, 1, 2],
        mode: 'contain',
        left: -10,
        right: 10,
        bottom: -10,
        top: 10
      };
      initializeCalculator(global.Desmos, { current: undefined });
      return store.dispatch(actions.requestBurst(opts)).then(() => {
        // slide with min/max of -3/3 should dispatch addFrame 7 times
        expect(store.getActions().length).toEqual(7);
      });
    });

    it('return actions to play animation via frames in state', () => {
      const expectedActions = [
        { type: types.PLAY_PREVIEW },
        { type: types.UPDATE_PREVIEW_IDX, payload: { idx: 1 } }
      ];
      store.dispatch(actions.startAnimation());
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return action to generate a GIF via frames in state', () => {
      const opts = {
        gifHeight: 300,
        gifWidth: 300,
        images: ['img1', 'img2', 'img3'],
        interval: 0.1,
        progressCallback: jest.fn()
      };

      const expectedActions = [
        { type: types.UPDATE_GIF_PROGRESS, payload: { progress: 100 } },
        { type: types.ADD_GIF, payload: { imageData: expect.any(String) } }
      ];

      store.dispatch(actions.generateGIF([], opts, global.gifshot));
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return actions to load saved frames from local storage', () => {
      calcHelpers.loadSavedGraph = jest.fn(() => ({
        frames: { 0: 'zero' },
        frameIDs: [0]
      }));
      const expectedActions = [
        { type: types.RESET },
        { type: types.ADD_FRAME, payload: { id: 0, imageData: 'zero' } }
      ];
      store.dispatch(actions.loadFramesFromLocal('date'));
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return an action to flash error on bad name input when saving graph', () => {
      jest.useFakeTimers();
      inputHelpers.getSaveGraphErrors = jest.fn(() => ({ name: 'name' }));
      const expectedActions = [
        {
          type: types.SET_ERROR,
          payload: { message: 'Invalid name input for saving graph: name' }
        },
        { type: types.CLEAR_ERROR }
      ];
      store.dispatch(actions.saveGraph());
      jest.runAllTimers();
      expect(store.getActions()).toEqual(expectedActions);
    });
  });
});
<commit_msg>fix typo<commit_after>import * as actions from './';
import * as types from '../constants/action-types';
import thunkMiddleware from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import panes from '../constants/pane-types';
import { initializeCalculator } from '../lib/calculator';
import * as calcHelpers from '../lib/calc-helpers';
import * as inputHelpers from '../lib/input-helpers';

const middlewares = [thunkMiddleware];
const mockStore = configureMockStore(middlewares);

const initialState = {
  images: {
    frames: {},
    frameIDs: [],
    gifProgress: 0,
    gifData: '',
    caption: '',
    fontColor: '#000000',
    gifFileName: ''
  },
  settings: {
    image: {
      width: 300,
      height: 300,
      interval: 100,
      oversample: false
    },
    bounds: {
      left: -10,
      right: 10,
      bottom: -10,
      top: 10
    },
    strategy: 'contain'
  },
  ui: {
    expandedPane: panes.NONE,
    previewIdx: 0,
    playing: false,
    error: ''
  }
};

// Tests

describe('Action creators', () => {
  describe('synchronous action creators', () => {
    it('return an action to add a frame', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_FRAME,
        payload: {
          id: 1,
          imageData
        }
      };
      expect(actions.addFrame(imageData)).toEqual(expected);
    });

    it('return an action to add frame from storage', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_FRAME,
        payload: {
          id: 0,
          imageData
        }
      };
      expect(actions.addSavedFrame(imageData, 0)).toEqual(expected);
    });

    it('return an action to update GIF creation progress', () => {
      const progress = 0.5;
      const expected = {
        type: types.UPDATE_GIF_PROGRESS,
        payload: { progress }
      };
      expect(actions.updateGIFProgress(progress)).toEqual(expected);
    });

    it('return an action to update GIF file name', () => {
      const gifFileName = 'name';
      const expected = {
        type: types.UPDATE_GIF_FILENAME,
        payload: { gifFileName }
      };
      expect(actions.updateGIFFileName('name')).toEqual(expected);
    });

    it('return an action to update GIF text', () => {
      const text = 'text';
      const expected = {
        type: types.UPDATE_TEXT,
        payload: { text }
      };
      expect(actions.updateText('text')).toEqual(expected);
    });

    it('return an action to update GIF text color', () => {
      const fontColor = '#FFFFFF';
      const expected = {
        type: types.UPDATE_TEXT_COLOR,
        payload: { fontColor }
      };
      expect(actions.updateTextColor('#FFFFFF')).toEqual(expected);
    });

    it('return an action to update GIF text position', () => {
      const textOpts = {
        textAlign: 'left',
        textBaseline: 'top'
      };
      const expected = {
        type: types.UPDATE_TEXT_POSITION,
        payload: { textAlign: 'left', textBaseline: 'top' }
      };
      expect(actions.updateTextPosition(textOpts)).toEqual(expected);
    });

    it('return an action to add processed GIF data', () => {
      const imageData = 'URI';
      const expected = {
        type: types.ADD_GIF,
        payload: { imageData }
      };
      expect(actions.addGIF(imageData)).toEqual(expected);
    });

    it('return an action to undo a captured burst', () => {
      const expected = {
        type: types.UNDO_BURST,
        payload: { frames: { 0: 'zero' }, frameIDs: [0] }
      };
      expect(actions.undoBurst({ 0: 'zero' }, [0])).toEqual(expected);
    });

    it('return an action to toggle the visible pane', () => {
      const pane = panes.PREVIEW;
      const expected = {
        type: types.TOGGLE_PANE,
        payload: { pane }
      };
      expect(actions.togglePane(panes.PREVIEW)).toEqual(expected);
    });

    it('return an action to update the preview index', () => {
      const idx = 2;
      const expected = {
        type: types.UPDATE_PREVIEW_IDX,
        payload: { idx }
      };
      expect(actions.updatePreviewIdx(idx)).toEqual(expected);
    });

    it('return an action to update a setting', () => {
      const imageSettings = {
        width: 400,
        height: 200,
        interval: 250,
        oversample: true
      };
      const boundsSettings = {
        left: -20,
        right: 20,
        top: 20,
        bottom: -20
      };

      let key;
      for (key in imageSettings) {
        expect(actions.updateSetting(key, imageSettings[key])).toEqual({
          type: types.UPDATE_IMAGE_SETTING,
          payload: {
            [key]: imageSettings[key]
          }
        });
      }

      for (key in boundsSettings) {
        expect(actions.updateSetting(key, boundsSettings[key])).toEqual({
          type: types.UPDATE_BOUNDS_SETTING,
          payload: {
            [key]: boundsSettings[key]
          }
        });
      }

      expect(actions.updateSetting('strategy', 'stretch')).toEqual({
        type: types.UPDATE_STRATEGY,
        payload: { strategy: 'stretch' }
      });
    });

    it('return an action to play the preview animation', () => {
      expect(actions.playPreview()).toEqual({ type: types.PLAY_PREVIEW });
    });

    it('return an action to pause the preview animation', () => {
      expect(actions.stopAnimation()).toEqual({ type: types.PAUSE_PREVIEW });
    });

    it('return an action to set an error', () => {
      const message = 'error';
      const expected = {
        type: types.SET_ERROR,
        payload: { message }
      };
      expect(actions.setError(message)).toEqual(expected);
    });

    it('return an action to clear an error', () => {
      expect(actions.clearError()).toEqual({ type: types.CLEAR_ERROR });
    });

    it('return an action to reset the frames', () => {
      expect(actions.reset()).toEqual({ type: types.RESET });
    });
  });

  describe('async action creators', () => {
    let store;
    beforeEach(() => {
      store = mockStore(initialState);
    });

    it('return an action to temporarily display error message', () => {
      jest.useFakeTimers();
      const expectedActions = [
        { type: types.SET_ERROR, payload: { message: 'error' } },
        { type: types.CLEAR_ERROR }
      ];
      store.dispatch(actions.flashError('error'));
      jest.runAllTimers();
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return an action to request a frame from the calculator', () => {
      const opts = {
        height: 300,
        targetPixelRatio: 1,
        width: 300,
        mode: 'contain',
        mathBounds: {
          left: -10,
          right: 10,
          bottom: -10,
          top: 10
        }
      };
      const expectedActions = [
        {
          type: types.ADD_FRAME,
          payload: { id: 2, imageData: expect.any(String) }
        }
      ];
      initializeCalculator(global.Desmos, { current: undefined });
      return store.dispatch(actions.requestFrame(opts)).then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
    });

    it('return an action to request a burst of frames from the calculator', () => {
      const opts = {
        height: 300,
        idx: 1,
        max: 3,
        min: -3,
        oversample: false,
        step: 1,
        width: 300,
        frameIDs: [0, 1, 2],
        mode: 'contain',
        left: -10,
        right: 10,
        bottom: -10,
        top: 10
      };
      initializeCalculator(global.Desmos, { current: undefined });
      return store.dispatch(actions.requestBurst(opts)).then(() => {
        // slide with min/max of -3/3 should dispatch addFrame 7 times
        expect(store.getActions().length).toEqual(7);
      });
    });

    it('return actions to play animation via frames in state', () => {
      const expectedActions = [
        { type: types.PLAY_PREVIEW },
        { type: types.UPDATE_PREVIEW_IDX, payload: { idx: 1 } }
      ];
      store.dispatch(actions.startAnimation());
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return actions to generate a GIF via frames in state', () => {
      const opts = {
        gifHeight: 300,
        gifWidth: 300,
        images: ['img1', 'img2', 'img3'],
        interval: 0.1,
        progressCallback: jest.fn()
      };

      const expectedActions = [
        { type: types.UPDATE_GIF_PROGRESS, payload: { progress: 100 } },
        { type: types.ADD_GIF, payload: { imageData: expect.any(String) } }
      ];

      store.dispatch(actions.generateGIF([], opts, global.gifshot));
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return actions to load saved frames from local storage', () => {
      calcHelpers.loadSavedGraph = jest.fn(() => ({
        frames: { 0: 'zero' },
        frameIDs: [0]
      }));
      const expectedActions = [
        { type: types.RESET },
        { type: types.ADD_FRAME, payload: { id: 0, imageData: 'zero' } }
      ];
      store.dispatch(actions.loadFramesFromLocal('date'));
      expect(store.getActions()).toEqual(expectedActions);
    });

    it('return an action to flash error on bad name input when saving graph', () => {
      jest.useFakeTimers();
      inputHelpers.getSaveGraphErrors = jest.fn(() => ({ name: 'name' }));
      const expectedActions = [
        {
          type: types.SET_ERROR,
          payload: { message: 'Invalid name input for saving graph: name' }
        },
        { type: types.CLEAR_ERROR }
      ];
      store.dispatch(actions.saveGraph());
      jest.runAllTimers();
      expect(store.getActions()).toEqual(expectedActions);
    });
  });
});
<|endoftext|>


<commit_before>/*!
 * Copyright 2020 Google LLC
 *
 * 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.
 */

const fs = require('fs');

const LICENSE_HEADER = `/*!
 * Copyright 2020 Google LLC
 *
 * 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.
 */
`;

for (const file of process.argv.slice(2)) {
  const content = fs.readFileSync(file, 'utf-8');
  fs.writeFileSync(file, `${LICENSE_HEADER}\n${content.trim()}\n`);
}
<commit_msg>chore: update license.js to 2021 (#1434)<commit_after>/*!
 * Copyright 2020 Google LLC
 *
 * 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.
 */

const fs = require('fs');

const LICENSE_HEADER = `/*!
 * Copyright 2021 Google LLC
 *
 * 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.
 */
`;

for (const file of process.argv.slice(2)) {
  const content = fs.readFileSync(file, 'utf-8');
  fs.writeFileSync(file, `${LICENSE_HEADER}\n${content.trim()}\n`);
}
<|endoftext|>


<commit_before><commit_msg>agregué comments en .js<commit_after><|endoftext|>


<commit_before>#!/usr/bin/node --harmony

// This script fetches different info from remote servers.
//
// It could take a lot of time later, so it's inadvisable
// to run it on every build.

var request = require('request')
var YAML = require('js-yaml')

var data = {
  projects: {},
  maintainers: {},
}
var config = require('./config')
var maintainersDB = config.maintainers
maintainersDB.forEach(function (maintainer) {
  data.maintainers[maintainer.npm] = {}
})
var projectsDB = config.projects
projectsDB.forEach(function (project) {
  data.projects[project.name] = {}
})

require('co')(function *() {
  yield [getInfoFromNpm, getInfoFromGithub, getMaintainersInfo]

  // it's pretty-printed only for git diffs, don't edit that manually
  var json = JSON.stringify(data, function(k, obj) {
    if (typeof obj !== 'object') return obj
    var ret = Object.create(null)
    if (k === "") {
      ret['//1'] = "Fair Warning: This file is autogenerated;"
      ret['//2'] = "you're free to change it as you like,"
      ret['//3'] = "but it will be overwritten on the next build."
    }
    Object.keys(obj).sort().forEach(function(i) { ret[i] = obj[i] })
    return ret
  }, 2)
  require('fs').writeFileSync(__dirname + '/db.json', json)
}).then()

//
// Generators
//

function *getNpmInfo(pkg) {
  return JSON.parse(yield get('https://registry.npmjs.org/' + pkg))
}

function *getTravis(repo) {
  var result = yield get('https://raw.githubusercontent.com/'
                         + repo + '/master/.travis.yml')
  try {
    return YAML.safeLoad(result)
  } catch(_) {
    return {}
  }
}

function *getUserInfo(user) {
  return JSON.parse(yield get('https://registry.npmjs.org/'
                              + '_users/org.couchdb.user:' + user))
}

function *getOwnedPackages(user) {
  var data = yield get('https://skimdb.npmjs.com/'
                     + 'registry/_design/app/_view/browseAuthors'
                     + '?startkey=' + escapeJSON([user])
                     + '&endkey=' + escapeJSON([user, {}])
                     + '&group_level=1') // set group_level=2 for list
  return JSON.parse(data).rows[0].value
}

function *getMaintainersInfo() {
  for (var name in data.maintainers) {
    var mData = data.maintainers[name]
    if (config['db.json'].maintainers.packages)
      mData.packages = yield getOwnedPackages(name)

    //if (config['db.json'].maintainers.avatar)
    //  mData.avatar = (yield getUserInfo(name)).avatar
  }
}

function *getInfoFromNpm() {
  if (!config['db.json'].projects.maintainer
   && !config['db.json'].projects.description) return

  for (var i=0; i<projectsDB.length; i++) {
    var project = projectsDB[i]
    var npm = yield getNpmInfo(project.npm)
    var projectData = data.projects[project.name]
    projectData.description = npm.description
    projectData.maintainer =
      npm.versions[npm['dist-tags'].latest]._npmUser.name
  }
}

function *getInfoFromGithub() {
  if (!config['db.json'].projects.node) return
  for (var i=0; i<projectsDB.length; i++) {
    var project = projectsDB[i]
    var travis = yield getTravis(project.repo)
    var projectData = data.projects[project.name]
    if (travis.node_js) {
      projectData.node = travis.node_js.sort(versionSort)[0]
    }
  }
}

//
// Helpers
//

function get(url) {
  return function(cb) {
    console.log(' -> ' + url.split('?')[0])
    request.get({
      url: url,
      encoding: 'utf8',
    }, function(err, res, body) {
      cb(err, body)
    })
  }
}

// for couchdb
function escapeJSON(data) {
  return encodeURIComponent(JSON.stringify(data))
}

function versionSort(a, b) {
  a = a.split('.')
  b = b.split('.')
  if (a[0] != b[0]) return a[0] - b[0]
  return a[1] - b[1]
}
<commit_msg>support numbers as node_js in travis<commit_after>#!/usr/bin/node --harmony

// This script fetches different info from remote servers.
//
// It could take a lot of time later, so it's inadvisable
// to run it on every build.

var request = require('request')
var YAML = require('js-yaml')

var data = {
  projects: {},
  maintainers: {},
}
var config = require('./config')
var maintainersDB = config.maintainers
maintainersDB.forEach(function (maintainer) {
  data.maintainers[maintainer.npm] = {}
})
var projectsDB = config.projects
projectsDB.forEach(function (project) {
  data.projects[project.name] = {}
})

require('co')(function *() {
  yield [getInfoFromNpm, getInfoFromGithub, getMaintainersInfo]

  // it's pretty-printed only for git diffs, don't edit that manually
  var json = JSON.stringify(data, function(k, obj) {
    if (typeof obj !== 'object') return obj
    var ret = Object.create(null)
    if (k === "") {
      ret['//1'] = "Fair Warning: This file is autogenerated;"
      ret['//2'] = "you're free to change it as you like,"
      ret['//3'] = "but it will be overwritten on the next build."
    }
    Object.keys(obj).sort().forEach(function(i) { ret[i] = obj[i] })
    return ret
  }, 2)
  require('fs').writeFileSync(__dirname + '/db.json', json)
}).then()

//
// Generators
//

function *getNpmInfo(pkg) {
  return JSON.parse(yield get('https://registry.npmjs.org/' + pkg))
}

function *getTravis(repo) {
  var result = yield get('https://raw.githubusercontent.com/'
                         + repo + '/master/.travis.yml')
  try {
    return YAML.safeLoad(result)
  } catch(_) {
    return {}
  }
}

function *getUserInfo(user) {
  return JSON.parse(yield get('https://registry.npmjs.org/'
                              + '_users/org.couchdb.user:' + user))
}

function *getOwnedPackages(user) {
  var data = yield get('https://skimdb.npmjs.com/'
                     + 'registry/_design/app/_view/browseAuthors'
                     + '?startkey=' + escapeJSON([user])
                     + '&endkey=' + escapeJSON([user, {}])
                     + '&group_level=1') // set group_level=2 for list
  return JSON.parse(data).rows[0].value
}

function *getMaintainersInfo() {
  for (var name in data.maintainers) {
    var mData = data.maintainers[name]
    if (config['db.json'].maintainers.packages)
      mData.packages = yield getOwnedPackages(name)

    //if (config['db.json'].maintainers.avatar)
    //  mData.avatar = (yield getUserInfo(name)).avatar
  }
}

function *getInfoFromNpm() {
  if (!config['db.json'].projects.maintainer
   && !config['db.json'].projects.description) return

  for (var i=0; i<projectsDB.length; i++) {
    var project = projectsDB[i]
    var npm = yield getNpmInfo(project.npm)
    var projectData = data.projects[project.name]
    projectData.description = npm.description
    projectData.maintainer =
      npm.versions[npm['dist-tags'].latest]._npmUser.name
  }
}

function *getInfoFromGithub() {
  if (!config['db.json'].projects.node) return
  for (var i=0; i<projectsDB.length; i++) {
    var project = projectsDB[i]
    var travis = yield getTravis(project.repo)
    var projectData = data.projects[project.name]
    if (travis.node_js) {
      projectData.node = travis.node_js.sort(versionSort)[0]
    }
  }
}

//
// Helpers
//

function get(url) {
  return function(cb) {
    console.log(' -> ' + url.split('?')[0])
    request.get({
      url: url,
      encoding: 'utf8',
    }, function(err, res, body) {
      cb(err, body)
    })
  }
}

// for couchdb
function escapeJSON(data) {
  return encodeURIComponent(JSON.stringify(data))
}

function versionSort(a, b) {
  a = String(a).split('.')
  b = String(b).split('.')
  if (a[0] != b[0]) return a[0] - b[0]
  return a[1] - b[1]
}
<|endoftext|>


<commit_before>import React from 'react';

import '../scss/_landing.scss';

const images = [
  'http://i.imgur.com/0uBzRJu.jpg', // chefs
  'http://i.imgur.com/KaHBbNU.jpg', // tomatoes
  'http://i.imgur.com/gBywUYs.jpg', // sandwich
  'http://i.imgur.com/3tPEYS9.jpg', // cookbook
  'http://i.imgur.com/U7yz4jB.jpg', // onion
  'http://i.imgur.com/P7HV4tq.jpg',
  'http://i.imgur.com/U7yPCPx.jpg',
  'http://i.imgur.com/MdsjkBd.jpg',
  'http://i.imgur.com/c8zNaLo.jpg',
  'http://i.imgur.com/YdLYJXS.jpg',
];

let randImage = Math.floor(Math.random() * images.length);
let heroImageStyle = {
  background: 'linear-gradient(rgba(50, 30, 45, 0.5), rgba(3, 79, 145, 0.5)), url(' + images[randImage] +')',
  backgroundSize: 'cover',
  backgroundRepeat: 'no-repeat',
  backgroundPosition: 'center center',
  paddingTop: '100px',
};

const Landing = () => (

Cook. Iterate. Repeat.

Forkful helps you perfect your next meal by tracking different iterations of recipes.

            {/**/}

or, continue without signing in

Fork Recipes

"Fork" a recipe to modify it and make it your own.

Recipe History

See how recipes have changed over time.

Explore

Browse our most popular recipes and get cooking!

Made by Andrew, Dylan, Nemo, and Thomas.

Recipes powered by Yummly. Icons from The Noun Project.

);

export default Landing;
<commit_msg>Switch to svg logo on homepage<commit_after>import React from 'react';

import '../scss/_landing.scss';

const images = [
  'http://i.imgur.com/0uBzRJu.jpg', // chefs
  'http://i.imgur.com/KaHBbNU.jpg', // tomatoes
  'http://i.imgur.com/gBywUYs.jpg', // sandwich
  'http://i.imgur.com/3tPEYS9.jpg', // cookbook
  'http://i.imgur.com/U7yz4jB.jpg', // onion
  'http://i.imgur.com/P7HV4tq.jpg',
  'http://i.imgur.com/U7yPCPx.jpg',
  'http://i.imgur.com/MdsjkBd.jpg',
  'http://i.imgur.com/c8zNaLo.jpg',
  'http://i.imgur.com/YdLYJXS.jpg',
];

let randImage = Math.floor(Math.random() * images.length);
let heroImageStyle = {
  background: 'linear-gradient(rgba(50, 30, 45, 0.5), rgba(3, 79, 145, 0.5)), url(' + images[randImage] +')',
  backgroundSize: 'cover',
  backgroundRepeat: 'no-repeat',
  backgroundPosition: 'center center',
  paddingTop: '100px',
};

const Landing = () => (

Cook. Iterate. Repeat.

Forkful helps you perfect your next meal by tracking different iterations of recipes.

            {/**/}

or, continue without signing in

Fork Recipes

"Fork" a recipe to modify it and make it your own.

Recipe History

See how recipes have changed over time.

Explore

Browse our most popular recipes and get cooking!

Made by Andrew, Dylan, Nemo, and Thomas.

Recipes powered by Yummly. Icons from The Noun Project.

);

export default Landing;
<|endoftext|>


<commit_before>var util = require('util');
var os = require("os");

var Q = require("q");
var dateformat = require("dateformat");
var md5 = require("crypto-md5");

var settings = require("../common/config");
var cacheProvider = require("../common/cacheProvider")(settings.caches[settings.cache]);
var helper = require('../common/helper');
var logger = require('../common/logger')("authorize");
var blockloginTimes = 10; //10次失败lock
var loginTimesExpire = 300; //5分钟
var MESSAGE_TEMP_LOCK_ACCOUNT = "为了您的账户安全，您的账户已被临时锁定，请在5分钟后再尝试";
var accountBL = require('./account.js');

function getSessionUserData(user) {
    return {
        userId: user.UserID,
        authToken: issueUserAuthToken(user)
    };
}

function issueUserAuthToken(user) {
    if (!user) {
        return null;
    }

    var data = {
        ID: user.ID
    };

    var token = util.format("%s-%s", JSON.stringify(data), new Date().valueOf());

    return helper.encrypt(token);
}

function parseUserInfoAuthToken(token) {
    var result = null;

    try {
        var decryptToken = helper.decrypt(token);

        if (decryptToken.indexOf("-") !== -1) {
            result = JSON.parse(decryptToken.split("-")[0]);
        }
    } catch (err) {
    }

    return result;
}

function login(req, username, password) {
    //var ip = helper.getClientIp(req);

    return accountBL.login(username, password).fail(function (err) {
        throw err;
    });
}

function generateRememberMeCookie(loginName, identifier, token, authToken) {
    var cookieKey = loginName + ":" + identifier + ":" + token + ":" + authToken;
    return helper.encrypt(cookieKey);
}

function generateRememberMeCacheKey(loginName, identifier) {
    return util.format(settings.cacheKeys.rememberme_token, loginName, identifier);
}

function parseRememberMeCookie(c) {
    var cookie = null;

    try {
        cookie = helper.decrypt(c);
    }
    catch (err) {
        //noop
    }

    if (!cookie) {
        return null;
    }

    var arr = cookie.split(":");
    if (arr.length !== 4) {
        return null;
    }
    return {
        loginName: arr[0],
        identifier: arr[1],
        token: arr[2],
        authToken: arr[3]
    };
}

exports.authenticate = function () {
    return function (req, username, password, done) {
        return login(req, username, password)
            .then(function (user) {
                done(null, user);
            })
            .fail(function (err) {
                done(err, null, {message: err.message});
            });
    };
};

exports.serializeUser = function () {
    return function (user, done) {
        //put to session
        done(null, user);
    }
};

exports.deserializeUser = function () {
    return function (user, done) {
        //read from sesson
        done(null, user);
    }
};

exports.verifyAuthToken = function () {
    return function (token, done) {
        var userInfo = parseUserInfoAuthToken(token);
        if (!userInfo) {
            return done(null, false);
        }

        return accountBL.getUserInfo(userInfo.ID)
            .then(function (user) {
                if (!user) {
                    return done(null, false);
                }

                return done(null, getSessionUserData(user));
            })
            .fail(function (err) {
                return done(err);
            });
    };
};

exports.updateAuthToken = function () {
    console.log("updateAuthToken")
    return function (user, done) {
        var authToken = issueUserAuthToken(user);

        return done(null, authToken);
    };
};

exports.verifyRememberMeToken = function () {
    return function (token, done, req) {
        console.log("======")
        console.log(token)
        var cookie = parseRememberMeCookie(token);
        if (!cookie) {
            return done(null, false);
        }
        var cacheKey = generateRememberMeCacheKey(cookie.loginName, cookie.identifier);
        cacheProvider.get(cacheKey)
            .then(function (data) {
                if (!data) {
                    return done(null, false);
                }

                if (data.token !== cookie.token) {
                    cacheProvider.del(cacheKey);
                    return done(null, false);
                }

                return accountBL.getUserInfo(data.userId)
                    .then(function (user) {
                        if (!user) {
                            return done(null, false);
                        }

                        return done(null, getSessionUserData(user));
                    });
            }).fail(function (err) {
            return done(err);
        });
    };
};

exports.updateRememberMeToken = function () {
    return function (user, done) {
        var identifier = helper.randomStr(6);
        var token = helper.randomStr(6);
        var data = {
            userId: user.userId,
            token: token,
            generateTime: new Date(),
            type: 'update'
        };

        var remembermeCookie = generateRememberMeCookie(user.loginName, identifier, token, user.authToken);
        var cacheKey = generateRememberMeCacheKey(user.loginName, identifier);
        cacheProvider.set(cacheKey, data)
            .then(function () {
                return done(null, remembermeCookie);
            }).fail(function (err) {
            return done(err);
        });
    };
};

exports.logout = function (req, res, disableAutoRedirect) {
    var cookieKeys = settings.cookieKeys;

    res.clearMkpCookie(cookieKeys.authToken);

    req.logout();
    req.session.destroy();
};

exports.login = login;

exports.issueUserAuthToken = issueUserAuthToken;
exports.parseUserInfoAuthToken = parseUserInfoAuthToken;
exports.blockloginTimes = blockloginTimes;
exports.blockloginMessage = MESSAGE_TEMP_LOCK_ACCOUNT;
exports.getSessionUserData = getSessionUserData;
<commit_msg>remove console<commit_after>var util = require('util');
var os = require("os");

var Q = require("q");
var dateformat = require("dateformat");
var md5 = require("crypto-md5");

var settings = require("../common/config");
var cacheProvider = require("../common/cacheProvider")(settings.caches[settings.cache]);
var helper = require('../common/helper');
var logger = require('../common/logger')("authorize");
var blockloginTimes = 10; //10次失败lock
var loginTimesExpire = 300; //5分钟
var MESSAGE_TEMP_LOCK_ACCOUNT = "为了您的账户安全，您的账户已被临时锁定，请在5分钟后再尝试";
var accountBL = require('./account.js');

function getSessionUserData(user) {
    return {
        userId: user.UserID,
        authToken: issueUserAuthToken(user)
    };
}

function issueUserAuthToken(user) {
    if (!user) {
        return null;
    }

    var data = {
        ID: user.ID
    };

    var token = util.format("%s-%s", JSON.stringify(data), new Date().valueOf());

    return helper.encrypt(token);
}

function parseUserInfoAuthToken(token) {
    var result = null;

    try {
        var decryptToken = helper.decrypt(token);

        if (decryptToken.indexOf("-") !== -1) {
            result = JSON.parse(decryptToken.split("-")[0]);
        }
    } catch (err) {
    }

    return result;
}

function login(req, username, password) {
    //var ip = helper.getClientIp(req);

    return accountBL.login(username, password).fail(function (err) {
        throw err;
    });
}

function generateRememberMeCookie(loginName, identifier, token, authToken) {
    var cookieKey = loginName + ":" + identifier + ":" + token + ":" + authToken;
    return helper.encrypt(cookieKey);
}

function generateRememberMeCacheKey(loginName, identifier) {
    return util.format(settings.cacheKeys.rememberme_token, loginName, identifier);
}

function parseRememberMeCookie(c) {
    var cookie = null;

    try {
        cookie = helper.decrypt(c);
    }
    catch (err) {
        //noop
    }

    if (!cookie) {
        return null;
    }

    var arr = cookie.split(":");
    if (arr.length !== 4) {
        return null;
    }
    return {
        loginName: arr[0],
        identifier: arr[1],
        token: arr[2],
        authToken: arr[3]
    };
}

exports.authenticate = function () {
    return function (req, username, password, done) {
        return login(req, username, password)
            .then(function (user) {
                done(null, user);
            })
            .fail(function (err) {
                done(err, null, {message: err.message});
            });
    };
};

exports.serializeUser = function () {
    return function (user, done) {
        //put to session
        done(null, user);
    }
};

exports.deserializeUser = function () {
    return function (user, done) {
        //read from sesson
        done(null, user);
    }
};

exports.verifyAuthToken = function () {
    return function (token, done) {
        var userInfo = parseUserInfoAuthToken(token);
        if (!userInfo) {
            return done(null, false);
        }

        return accountBL.getUserInfo(userInfo.ID)
            .then(function (user) {
                if (!user) {
                    return done(null, false);
                }

                return done(null, getSessionUserData(user));
            })
            .fail(function (err) {
                return done(err);
            });
    };
};

exports.updateAuthToken = function () {
    return function (user, done) {
        var authToken = issueUserAuthToken(user);

        return done(null, authToken);
    };
};

exports.verifyRememberMeToken = function () {
    return function (token, done, req) {
        console.log("======")
        console.log(token)
        var cookie = parseRememberMeCookie(token);
        if (!cookie) {
            return done(null, false);
        }
        var cacheKey = generateRememberMeCacheKey(cookie.loginName, cookie.identifier);
        cacheProvider.get(cacheKey)
            .then(function (data) {
                if (!data) {
                    return done(null, false);
                }

                if (data.token !== cookie.token) {
                    cacheProvider.del(cacheKey);
                    return done(null, false);
                }

                return accountBL.getUserInfo(data.userId)
                    .then(function (user) {
                        if (!user) {
                            return done(null, false);
                        }

                        return done(null, getSessionUserData(user));
                    });
            }).fail(function (err) {
            return done(err);
        });
    };
};

exports.updateRememberMeToken = function () {
    return function (user, done) {
        var identifier = helper.randomStr(6);
        var token = helper.randomStr(6);
        var data = {
            userId: user.userId,
            token: token,
            generateTime: new Date(),
            type: 'update'
        };

        var remembermeCookie = generateRememberMeCookie(user.loginName, identifier, token, user.authToken);
        var cacheKey = generateRememberMeCacheKey(user.loginName, identifier);
        cacheProvider.set(cacheKey, data)
            .then(function () {
                return done(null, remembermeCookie);
            }).fail(function (err) {
            return done(err);
        });
    };
};

exports.logout = function (req, res, disableAutoRedirect) {
    var cookieKeys = settings.cookieKeys;

    res.clearMkpCookie(cookieKeys.authToken);

    req.logout();
    req.session.destroy();
};

exports.login = login;

exports.issueUserAuthToken = issueUserAuthToken;
exports.parseUserInfoAuthToken = parseUserInfoAuthToken;
exports.blockloginTimes = blockloginTimes;
exports.blockloginMessage = MESSAGE_TEMP_LOCK_ACCOUNT;
exports.getSessionUserData = getSessionUserData;
<|endoftext|>


<commit_before>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function($) {
	/**
	 * yiiactiveform set function.
	 * @param map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function(settings) {
		var settings = $.extend({}, $.fn.yiiactiveform.defaults, settings || {});
		return this.each(function() {
			var $form = $(this);
			var id = $form.attr('id');
			if(settings.validationUrl == undefined)
				settings.validationUrl = $form.attr('action');
			$.each(settings.attributes, function(i,attribute){
				settings.attributes[i] = $.extend({
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					validatingCssClass : settings.validatingCssClass
				}, attribute);
				settings.attributes[i].value = $('#'+attribute.inputID).val();
			});
			$.fn.yiiactiveform.settings[id] = settings;

			var getInputContainer = function(attribute) {
				if(attribute.inputContainer == undefined)
					return $('#'+attribute.inputID).closest('div');
				else
					return $(attribute.inputContainer).filter(':has("#'+attribute.inputID+'")');
			};

			// updates the error message and the input container for a particular attribute
			var updateInput = function(attribute, messages) {
				attribute.status = 1;
				var hasError = messages && $.isArray(messages[attribute.inputID]) && messages[attribute.inputID].length>0;
				var $error = $('#'+attribute.errorID);
				var $container = getInputContainer(attribute);
				$container.removeClass(attribute.validatingCssClass)
					.removeClass(attribute.errorCssClass)
					.removeClass(attribute.successCssClass);

				if(hasError) {
					$error.html(messages[attribute.inputID][0]);
					$container.addClass(attribute.errorCssClass);
				}
				else {
					$container.addClass(attribute.successCssClass);
				}
				if(!attribute.hideErrorMessage)
					$error.toggle(hasError);

				attribute.value = $('#'+attribute.inputID).val();

				return hasError;
			};

			// updates the error summary
			var updateSummary = function(messages) {
				if (settings.summaryID == undefined)
					return;
				var content = '';
				$.each(settings.attributes, function(i, attribute){
					if(messages && $.isArray(messages[attribute.inputID])) {
						$.each(messages[attribute.inputID],function(j,message){
							content = content + '
' + message + '
';
						});
					}
				});
				$('#'+settings.summaryID+' ul').html(content);
				$('#'+settings.summaryID).toggle(content!='');
			}

			// performs AJAX validation
			var ajaxValidate = function(successCallback) {
				$.ajax({
					url : settings.validationUrl,
					type : $form.attr('method'),
					data : $form.serialize()+'&'+settings.ajaxVar+'='+id,
					dataType : 'json',
					success : successCallback,
					error : function() {
						$.each(settings.attributes, function(i, attribute){
							//updateInput(attribute, []);
						});
					}
				});
			};

			var validate = function(attribute, forceValidate) {
				if (forceValidate)
					attribute.status = 2;
				$.each(settings.attributes, function(){
					if (this.value != $('#'+this.inputID).val()) {
						this.status = 2;
						forceValidate = true;
					}
				});
				if (!forceValidate)
					return;

				if(settings.timer!=undefined)
					clearTimeout(settings.timer);
				settings.timer = setTimeout(function(){
					$.each(settings.attributes, function(){
						if (this.status == 2) {
							this.status = 3;
							getInputContainer(this).addClass(this.validatingCssClass);
						}
					});
					ajaxValidate(function(data) {
						$.each(settings.attributes, function(){
							if (this.status == 3) {
								updateInput(this, data);
							}
						});
					});
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function(i, attribute) {
				if (attribute.validateOnChange) {
					$('#'+attribute.inputID).change(function(){
						validate(attribute,false);
					}).blur(function(){
						if(attribute.status!=2 && attribute.status!=3)
							validate(attribute, !attribute.status);
					});
				}
				if (attribute.validateOnType) {
					$('#'+attribute.inputID).keyup(function(){
						if (attribute.value != $('#'+attribute.inputID).val())
							validate(attribute, false);
					});
				}
			});

			if (settings.validateOnSubmit) {
				var validated = false;
				$form.submit(function(){
					if (validated)
						return true;
					ajaxValidate(function(data){
						var hasError = false;
						$.each(settings.attributes, function(i, attribute){
							hasError = updateInput(attribute, data) || hasError;
						});
						updateSummary(data);
						if(!hasError) {
							validated = true;
							$form.submit();
							validated = false;
						}
					});
					return false;
				});
			}
		});
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 100,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: not validated,  1: validated, 2: pending validation, 3: validating
		 *     validationDelay: 100,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 * }
		 */
		attributes : []
	};

	$.fn.yiiactiveform.settings = {};

})(jQuery);<commit_msg>refactored activeform js.<commit_after>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function($) {
	/**
	 * yiiactiveform set function.
	 * @param map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function(settings) {
		var settings = $.extend({}, $.fn.yiiactiveform.defaults, settings || {});
		return this.each(function() {
			var $form = $(this);
			var id = $form.attr('id');
			if(settings.validationUrl == undefined)
				settings.validationUrl = $form.attr('action');
			$.each(settings.attributes, function(i,attribute){
				settings.attributes[i] = $.extend({
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					validatingCssClass : settings.validatingCssClass
				}, attribute);
				settings.attributes[i].value = $('#'+attribute.inputID).val();
			});
			$(this).data('settings', settings);

			var getInputContainer = function(attribute) {
				if(attribute.inputContainer == undefined)
					return $('#'+attribute.inputID).closest('div');
				else
					return $(attribute.inputContainer).filter(':has("#'+attribute.inputID+'")');
			};

			// updates the error message and the input container for a particular attribute
			var updateInput = function(attribute, messages) {
				attribute.status = 1;
				var hasError = messages && $.isArray(messages[attribute.inputID]) && messages[attribute.inputID].length>0;
				var $error = $('#'+attribute.errorID);
				var $container = getInputContainer(attribute);
				$container.removeClass(attribute.validatingCssClass)
					.removeClass(attribute.errorCssClass)
					.removeClass(attribute.successCssClass);

				if(hasError) {
					$error.html(messages[attribute.inputID][0]);
					$container.addClass(attribute.errorCssClass);
				}
				else {
					$container.addClass(attribute.successCssClass);
				}
				if(!attribute.hideErrorMessage)
					$error.toggle(hasError);

				attribute.value = $('#'+attribute.inputID).val();

				return hasError;
			};

			// updates the error summary
			var updateSummary = function(messages) {
				if (settings.summaryID == undefined)
					return;
				var content = '';
				$.each(settings.attributes, function(i, attribute){
					if(messages && $.isArray(messages[attribute.inputID])) {
						$.each(messages[attribute.inputID],function(j,message){
							content = content + '
' + message + '
';
						});
					}
				});
				$('#'+settings.summaryID+' ul').html(content);
				$('#'+settings.summaryID).toggle(content!='');
			}

			// performs AJAX validation
			var ajaxValidate = function(successCallback) {
				$.ajax({
					url : settings.validationUrl,
					type : $form.attr('method'),
					data : $form.serialize()+'&'+settings.ajaxVar+'='+id,
					dataType : 'json',
					success : successCallback,
					error : function() {
						$.each(settings.attributes, function(i, attribute){
							//updateInput(attribute, []);
						});
					}
				});
			};

			var validate = function(attribute, forceValidate) {
				if (forceValidate)
					attribute.status = 2;
				$.each(settings.attributes, function(){
					if (this.value != $('#'+this.inputID).val()) {
						this.status = 2;
						forceValidate = true;
					}
				});
				if (!forceValidate)
					return;

				if(settings.timer!=undefined)
					clearTimeout(settings.timer);
				settings.timer = setTimeout(function(){
					$.each(settings.attributes, function(){
						if (this.status == 2) {
							this.status = 3;
							getInputContainer(this).addClass(this.validatingCssClass);
						}
					});
					ajaxValidate(function(data) {
						$.each(settings.attributes, function(){
							if (this.status == 3) {
								updateInput(this, data);
							}
						});
					});
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function(i, attribute) {
				if (attribute.validateOnChange) {
					$('#'+attribute.inputID).change(function(){
						validate(attribute,false);
					}).blur(function(){
						if(attribute.status!=2 && attribute.status!=3)
							validate(attribute, !attribute.status);
					});
				}
				if (attribute.validateOnType) {
					$('#'+attribute.inputID).keyup(function(){
						if (attribute.value != $('#'+attribute.inputID).val())
							validate(attribute, false);
					});
				}
			});

			if (settings.validateOnSubmit) {
				var validated = false;
				$form.submit(function(){
					if (validated)
						return true;
					ajaxValidate(function(data){
						var hasError = false;
						$.each(settings.attributes, function(i, attribute){
							hasError = updateInput(attribute, data) || hasError;
						});
						updateSummary(data);
						if(!hasError) {
							validated = true;
							$form.submit();
							validated = false;
						}
					});
					return false;
				});
			}
		});
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 100,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: not validated,  1: validated, 2: pending validation, 3: validating
		 *     validationDelay: 100,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 * }
		 */
		attributes : []
	};

})(jQuery);<|endoftext|>


<commit_before>/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'hytteadmin-client',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    contentSecurityPolicy: {
      'default-src': "'none'",
      'script-src': [
        '\'self\'',
        '\'unsafe-inline\'',
        '\'unsafe-eval\'',
        'turistforeningen.atlassian.net',
        'dev.app.dnt.privat:49152',
        'hytte.app.dnt.privat:49152',
        'hytte.app.dnt.privat',
        'dev.app.dnt.no',
        'use.typekit.net',
        'connect.facebook.net',
        'maps.googleapis.com',
        'maps.gstatic.com',
        'cdn.ravenjs.com',
        'widget.uservoice.com',
        'by.uservoice.com'
      ].join(' '),
      'font-src': [
        '\'self\'',
        'data:',
        'use.typekit.net',
        'fonts.gstatic.com',
        's3-eu-west-1.amazonaws.com'
      ].join(' '),
      'connect-src': [
        '\'self\'',
        'ws://dev.app.dnt.privat:49152',
        'ws://hytte.app.dnt.privat:49152',
        'ws.geonorge.no',
        'jotunheimr.app.dnt.no'
      ].join(' '),
      'img-src': [
        '\'self\'',
        'data:',
        'www.google-analytics.com',
        'www.facebook.com',
        'p.typekit.net',
        'www.dnt.no',
        'www.turistforeningen.no',
        'www2.turistforeningen.no',
        's3-eu-west-1.amazonaws.com',
        'mt3.turistforeningen.no',
        'opencache.statkart.no',
        'app.getsentry.com',
        'sentry.app.dnt.no',
        'widget.uservoice.com'
      ].join(' '),
      'style-src': [
        '\'self\'',
        '\'unsafe-inline\'',
        'use.typekit.net',
        'fonts.googleapis.com',
        's3-eu-west-1.amazonaws.com'
      ].join(' '),
      'frame-src': [
        's-static.ak.facebook.com',
        'static.ak.facebook.com',
        'www.facebook.com',
        'dnt.uservoice.com'
      ].join(' ')
    },
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    },
    moment: {
      includeLocales: ['nb']
    },
    sentry: {
      skipCdn: false, // Skip loading from CDN
      cdn: '//cdn.ravenjs.com',
      dsn: 'https://1d5112a03f0941278212feeea3a98c75@sentry.app.dnt.no/16',
      version: '1.1.19',
      whitelistUrls: [/hytte\.app\.dnt\.no/], // Use regex if only setting domain
      serviceName: 'logger', // https://github.com/damiencaselli/ember-cli-sentry/wiki/Migration-from-1.x.x-to-2.x
      development: true // Set to true, to disable while developing
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.googleAnalytics = {
      webPropertyId: 'UA-45821478-3'
    };
  }

  return ENV;
};
<commit_msg>Allow script requests to `by2.uservoice.com`<commit_after>/* jshint node: true */

module.exports = function(environment) {
  var ENV = {
    modulePrefix: 'hytteadmin-client',
    environment: environment,
    baseURL: '/',
    locationType: 'auto',
    contentSecurityPolicy: {
      'default-src': "'none'",
      'script-src': [
        '\'self\'',
        '\'unsafe-inline\'',
        '\'unsafe-eval\'',
        'turistforeningen.atlassian.net',
        'dev.app.dnt.privat:49152',
        'hytte.app.dnt.privat:49152',
        'hytte.app.dnt.privat',
        'dev.app.dnt.no',
        'use.typekit.net',
        'connect.facebook.net',
        'maps.googleapis.com',
        'maps.gstatic.com',
        'cdn.ravenjs.com',
        'widget.uservoice.com',
        'by.uservoice.com',
        'by2.uservoice.com'
      ].join(' '),
      'font-src': [
        '\'self\'',
        'data:',
        'use.typekit.net',
        'fonts.gstatic.com',
        's3-eu-west-1.amazonaws.com'
      ].join(' '),
      'connect-src': [
        '\'self\'',
        'ws://dev.app.dnt.privat:49152',
        'ws://hytte.app.dnt.privat:49152',
        'ws.geonorge.no',
        'jotunheimr.app.dnt.no'
      ].join(' '),
      'img-src': [
        '\'self\'',
        'data:',
        'www.google-analytics.com',
        'www.facebook.com',
        'p.typekit.net',
        'www.dnt.no',
        'www.turistforeningen.no',
        'www2.turistforeningen.no',
        's3-eu-west-1.amazonaws.com',
        'mt3.turistforeningen.no',
        'opencache.statkart.no',
        'app.getsentry.com',
        'sentry.app.dnt.no',
        'widget.uservoice.com'
      ].join(' '),
      'style-src': [
        '\'self\'',
        '\'unsafe-inline\'',
        'use.typekit.net',
        'fonts.googleapis.com',
        's3-eu-west-1.amazonaws.com'
      ].join(' '),
      'frame-src': [
        's-static.ak.facebook.com',
        'static.ak.facebook.com',
        'www.facebook.com',
        'dnt.uservoice.com'
      ].join(' ')
    },
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    },
    moment: {
      includeLocales: ['nb']
    },
    sentry: {
      skipCdn: false, // Skip loading from CDN
      cdn: '//cdn.ravenjs.com',
      dsn: 'https://1d5112a03f0941278212feeea3a98c75@sentry.app.dnt.no/16',
      version: '1.1.19',
      whitelistUrls: [/hytte\.app\.dnt\.no/], // Use regex if only setting domain
      serviceName: 'logger', // https://github.com/damiencaselli/ember-cli-sentry/wiki/Migration-from-1.x.x-to-2.x
      development: true // Set to true, to disable while developing
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.baseURL = '/';
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.googleAnalytics = {
      webPropertyId: 'UA-45821478-3'
    };
  }

  return ENV;
};
<|endoftext|>


<commit_before><commit_msg>Update commento.js<commit_after><|endoftext|>


<commit_before>// Description:
// Get all phone numbers for active members

const _ = require('lodash');
const members = require('../lib/members');

module.exports = robot => {
  robot.hear(/nummer/i, msg => {
    members('?active=true')
      .then(members => {
        if (members.length === 0) {
          return;
        }
        msg.send(
          members
            .map(
              m =>
                `\`*${m.name}*:${'\t'} ${m.phone_number.substr(
                  0,
                  3
                )} ${m.phone_number
                  .substr(3)
                  .match(/.{1,2}/g)
                  .join(' ')}\``
            )
            .sort()
            .join('\n')
        );
      })
      .catch(error => msg.send(error.message));
  });
};
<commit_msg>FIX6.0<commit_after>// Description:
// Get all phone numbers for active members

const _ = require('lodash');
const members = require('../lib/members');

module.exports = robot => {
  robot.hear(/nummer/i, msg => {
    members('?active=true')
      .then(members => {
        if (members.length === 0) {
          return;
        }
        msg.send(
          members
            .map(
              m =>
                `*${m.name}*:${'\t'} ${m.phone_number.substr(
                  0,
                  3
                )} ${m.phone_number
                  .substr(3)
                  .match(/.{1,2}/g)
                  .join(' ')}`
            )
            .sort()
            .join('\n')
        );
      })
      .catch(error => msg.send(error.message));
  });
};
<|endoftext|>


<commit_before>angular.module("typeaheadDropdown.tpl", [])
        .run(["$templateCache",
            function (a) {
                a.put("templates/typeaheadDropdown.tpl.html",
                    "
" +
                    "
Loading options...
" +
                    "
" +
                    "
" +
                    "<input class=form-control placeholder=\"Select or type...\" ng-model=model[config.modelLabel] typeahead=\"op[config.optionLabel] for op in options | filter:$viewValue\" typeahead-editable=false typeahead-on-select=\"onSelect($item, $model, $label)\" ng-required=\"required\"> " +
                    "" +
                    "<button class=\"btn btn-default dropdown-toggle\" dropdown-toggle>" +
                    "" +
                    "" +
                    "</button>" +
                    "" +
                    "
" +
                    "
" +
                    "
" +
                    "{{op[config.optionLabel]}}" +
                    "
" +
                    "
" +
                    "
" +
                    "
");
            }
        ]),
    angular.module("apg.typeaheadDropdown", ["typeaheadDropdown.tpl", "ui.bootstrap"])
        .directive("typeaheadDropdown", function() {
            return {
                templateUrl: "templates/typeaheadDropdown.tpl.html",
                scope: { model: "=ngModel", getOptions: "&options", config: "=?", events: "=", required: "=?ngRequired" },
                replace: !0,
                link: function($scope, $element, $attrs) {
                    $scope.externalEvents = {
                        onItemSelect: angular.noop
                    };
                    angular.extend($scope.externalEvents, $scope.events || []);
                },

                controller: ["$scope", "$q",
                    function(a, b) {
                        a.config = angular.extend({ modelLabel: "name", optionLabel: "name" }, a.config),
                            b.when(a.getOptions())
                                .then(function(b) {
                                    a.options = b;
                                }),
                            a.onSelect = function(b) {
                                angular.extend(a.model, b);
                                a.model[a.config.modelLabel] = b[a.config.optionLabel];
                                a.events.onItemSelect(b);
                            }
                    }
                ]
            }
        });<commit_msg>improvements<commit_after>"use strict";
angular.module("typeaheadDropdown.tpl", [])
        .run(["$templateCache",
            function (a) {
                a.put("templates/typeaheadDropdown.tpl.html",
                    "
" +
                    //"
Loading options...
" +
                    "
" +
                    "
" +
                    "<input class=form-control placeholder=\"Select or type...\" ng-model=model[config.modelLabel] typeahead=\"op[config.optionLabel] for op in options | filter:$viewValue\" typeahead-editable=false typeahead-on-select=\"onSelect($item, $model, $label)\" ng-required=\"required\"> " +
                    "" +
                    "<button class=\"btn btn-default dropdown-toggle\" dropdown-toggle>" +
                    "" +
                    "" +
                    "</button>" +
                    "" +
                    "
" +
                    "
" +
                    "
" +
                    "{{op[config.optionLabel]}}" +
                    "
" +
                    "
" +
                    "
" +
                    "
");
            }
        ]),
    angular.module("apg.typeaheadDropdown", ["typeaheadDropdown.tpl", "ui.bootstrap"])
        .directive("typeaheadDropdown", function() {
            return {
                templateUrl: "templates/typeaheadDropdown.tpl.html",
                scope: { model: "=ngModel", options: "=",  config: "=?", events: "=", required: "=?ngRequired" },
                replace: !0,
                link: function($scope, $element, $attrs) {
                    $scope.externalEvents = {
                        onItemSelect: angular.noop
                    };

                    angular.extend($scope.externalEvents, $scope.events || []);
                },

                controller: ["$scope",
                    function (a) {
                        a.config = angular.extend({ modelLabel: "name", optionLabel: "name" }, a.config),

                        a.onSelect = function (i) {
                            if (a.model === undefined) {
                                a.model = [];
                            }
                            angular.extend(a.model, i);
                            a.model[a.config.modelLabel] = i[a.config.optionLabel];
                            if (a.events !== undefined) {
                                a.events.onItemSelect(i);
                            }
                        }
                    }
                ]
            }
        });<|endoftext|>


<commit_before>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function ($) {
	/*
	 * returns the value of the CActiveForm input field
	 * performs additional checks to get proper values for checkbox / radiobutton / checkBoxList / radioButtonList
	 * @param o object the jQuery object of the input element
	 */
	var getAFValue = function (o) {
		var type = o.attr('type');
		if (type === 'checkbox' || type === 'radio') {
			return o.filter(':checked').val();
		} else if (type !== undefined) {
			return o.val();
		} else {
			var c = [];
			o.find(':checked').each(function () {
				c.push(this.value);
			});
			return c.join(',');
		}
	};

	/**
	 * yiiactiveform set function.
	 * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function (options) {
		return this.each(function () {
			var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {}),
				$form = $(this);

			if (settings.validationUrl === undefined) {
				settings.validationUrl = $form.attr('action');
			}
			$.each(settings.attributes, function () {
				$.extend(this, {
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					beforeValidateAttribute : settings.beforeValidateAttribute,
					afterValidateAttribute : settings.afterValidateAttribute,
					validatingCssClass : settings.validatingCssClass
				});
				this.value = getAFValue($('#' + this.inputID, $form));
			});
			$(this).data('settings', settings);

			settings.submitting = false;  // whether it is waiting for ajax submission result
			var validate = function (attribute) {
				var changed = false;
				$.each(settings.attributes, function () {
					if (this.value !== getAFValue($('#' + this.inputID, $form))) {
						this.status = 2;
						changed = true;
					}
				});
				if (!changed) {
					return;
				}

				if (settings.timer !== undefined) {
					clearTimeout(settings.timer);
				}
				settings.timer = setTimeout(function () {
					if (settings.submitting || $form.is(':hidden')) {
						return;
					}
					if (attribute.beforeValidateAttribute === undefined || attribute.beforeValidateAttribute($form, attribute)) {
						$.each(settings.attributes, function () {
							if (this.status === 2) {
								this.status = 3;
								$.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass);
							}
						});
						$.fn.yiiactiveform.validate($form, function (data) {
							var hasError = false;
							$.each(settings.attributes, function () {
								if (this.status === 2 || this.status === 3) {
									hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
								}
							});
							if (attribute.afterValidateAttribute !== undefined) {
								attribute.afterValidateAttribute($form, attribute, data, hasError);
							}
						});
					}
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function () {
				if (this.validateOnChange) {
					$('#' + this.inputID, $form).change(function () {
						validate(this);
					}).blur(function () {
						if (this.status !== 2 && this.status !== 3) {
							validate(this);
						}
					});
				}
				if (this.validateOnType) {
					$('#' + this.inputID, $form).keyup(function () {
						if (this.value !== getAFValue($(this))) {
							validate(this);
						}
					});
				}
			});

			if (settings.validateOnSubmit) {
				$form.find(':submit').live('mouseup keyup', function () {
					$form.data('submitObject', $(this));
				});
				var validated = false;
				$form.submit(function () {
					if (validated) {
						return true;
					}
					if (settings.timer !== undefined) {
						clearTimeout(settings.timer);
					}
					settings.submitting = true;
					if (settings.beforeValidate === undefined || settings.beforeValidate($form)) {
						$.fn.yiiactiveform.validate($form, function (data) {
							var hasError = false;
							$.each(settings.attributes, function () {
								hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
							});
							$.fn.yiiactiveform.updateSummary($form, data);
							if (settings.afterValidate === undefined || settings.afterValidate($form, data, hasError)) {
								if (!hasError) {
									validated = true;
									var $button = $form.data('submitObject') || $form.find(':submit:first');
									// TODO: if the submission is caused by "change" event, it will not work
									if ($button.length) {
										$button.click();
									} else {  // no submit button in the form
										$form.submit();
									}
									return;
								}
							}
							settings.submitting = false;
						});
					}
					else {
						settings.submitting = false;
					}
					return false;
				});
			}

			/*
			 * In case of reseting the form we need to reset error messages
			 * NOTE1: $form.reset - does not exist
			 * NOTE2: $form.live('reset', ...) does not work
			 */
			$form.bind('reset', function () {
				/*
				 * because we bind directly to a form reset event, not to a reset button (that could or could not exist),
				 * when this function is executed form elements values have not been reset yet,
				 * because of that we use the setTimeout
				 */
				setTimeout(function () {
					$.each(settings.attributes, function () {
						this.status = 0;
						var $error = $('#' + this.errorID, $form),
							$container = $.fn.yiiactiveform.getInputContainer(this, $form);

						$container.removeClass(
							this.validatingCssClass + ' ' +
							this.errorCssClass + ' ' +
							this.successCssClass
						);

						$error.html('').hide();

						/*
						 * without the setTimeout() we would get here the current entered value before the reset instead of the reseted value
						 */
						this.value = getAFValue($('#' + this.inputID, $form));
					});
					/*
					 * If the form is submited (non ajax) with errors, labels and input gets the class 'error'
					 */
					$('label, input', $form).each(function () {
						$(this).removeClass('error');
					});
					$('#' + settings.summaryID + ' ul').html('');
					$('#' + settings.summaryID).hide();
					//.. set to initial focus on reset
					if (settings.focus !== undefined && !window.location.hash) {
						$(settings.focus).focus();
					}
				}, 1);
			});

			/*
			 * set to initial focus
			 */
			if (settings.focus !== undefined && !window.location.hash) {
				$(settings.focus).focus();
			}
		});
	};

	/**
	 * Returns the container element of the specified attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param form the form jQuery object
	 * @return jquery the jquery representation of the container
	 */
	$.fn.yiiactiveform.getInputContainer = function (attribute, form) {
		if (attribute.inputContainer === undefined) {
			return $('#' + attribute.inputID, form).closest('div');
		} else {
			return $(attribute.inputContainer).filter(':has("#' + attribute.inputID + '")');
		}
	};

	/**
	 * updates the error message and the input container for a particular attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param messages array the json data obtained from the ajax validation request
	 * @param form the form jQuery object
	 * @return boolean whether there is a validation error for the specified attribute
	 */
	$.fn.yiiactiveform.updateInput = function (attribute, messages, form) {
		attribute.status = 1;
		var hasError = messages !== null && $.isArray(messages[attribute.id]) && messages[attribute.id].length > 0,
			$error = $('#' + attribute.errorID, form),
			$container = $.fn.yiiactiveform.getInputContainer(attribute, form);

		$container.removeClass(
			attribute.validatingCssClass + ' ' +
			attribute.errorCssClass + ' ' +
			attribute.successCssClass
		);

		if (hasError) {
			$error.html(messages[attribute.id][0]);
			$container.addClass(attribute.errorCssClass);
		}
		else if (attribute.enableAjaxValidation || attribute.clientValidation) {
			$container.addClass(attribute.successCssClass);
		}
		if (!attribute.hideErrorMessage) {
			$error.toggle(hasError);
		}

		attribute.value = getAFValue($('#' + attribute.inputID, form));
		return hasError;
	};

	/**
	 * updates the error summary, if any.
	 * @param form jquery the jquery representation of the form
	 * @param messages array the json data obtained from the ajax validation request
	 */
	$.fn.yiiactiveform.updateSummary = function (form, messages) {
		var settings = $(form).data('settings'),
			content = '';
		if (settings.summaryID === undefined) {
			return;
		}
		$.each(settings.attributes, function () {
			if (messages && $.isArray(messages[this.id])) {
				$.each(messages[this.id], function (j, message) {
					content = content + '<li>' + message + '</li>';
				});
			}
		});
		$('#' + settings.summaryID + ' ul').html(content);
		$('#' + settings.summaryID).toggle(content !== '');
	};

	/**
	 * Performs the ajax validation request.
	 * This method is invoked internally to trigger the ajax validation.
	 * @param form jquery the jquery representation of the form
	 * @param successCallback function the function to be invoked if the ajax request succeeds
	 * @param errorCallback function the function to be invoked if the ajax request fails
	 */
	$.fn.yiiactiveform.validate = function (form, successCallback, errorCallback) {
		var $form = $(form),
			settings = $form.data('settings'),
			needAjaxValidation = false,
			messages = {};
		$.each(settings.attributes, function () {
			var value,
				msg = [];
			if (this.clientValidation !== undefined && (settings.submitting || this.status === 2 || this.status === 3)) {
				value = getAFValue($('#' + this.inputID, $form));
				this.clientValidation(value, msg, this);
				if (msg.length) {
					messages[this.id] = msg;
				}
			}
			if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status === 2 || this.status === 3)) {
				needAjaxValidation = true;
			}
		});

		if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) {
			if (settings.submitting) {
				// delay callback so that the form can be submitted without problem
				setTimeout(function () {
					successCallback(messages);
				}, 200);
			}
			else {
				successCallback(messages);
			}
			return;
		}

		var $button = $form.data('submitObject'),
			extData = '&' + settings.ajaxVar + '=' + $form.attr('id');
		if ($button && $button.length) {
			extData += '&' + $button.attr('name') + '=' + $button.attr('value');
		}

		$.ajax({
			url : settings.validationUrl,
			type : $form.attr('method'),
			data : $form.serialize() + extData,
			dataType : 'json',
			success : function (data) {
				if (data !== null && typeof data === 'object') {
					$.each(settings.attributes, function () {
						if (!this.enableAjaxValidation) {
							delete data[this.id];
						}
					});
					successCallback($.extend({}, messages, data));
				}
				else {
					successCallback(messages);
				}
			},
			error : function () {
				if (errorCallback !== undefined) {
					errorCallback();
				}
			}
		});
	};

	/**
	 * Returns the configuration for the specified form.
	 * The configuration contains all needed information to perform ajax-based validation.
	 * @param form jquery the jquery representation of the form
	 * @return object the configuration for the specified form.
	 */
	$.fn.yiiactiveform.getSettings = function (form) {
		return $(form).data('settings');
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 200,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		beforeValidateAttribute: undefined, // function (form, attribute) : boolean
		afterValidateAttribute: undefined,  // function (form, attribute, data, hasError)
		beforeValidate: undefined, // function (form) : boolean
		afterValidate: undefined,  // function (form, data, hasError) : boolean
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     id : 'ModelClass_attribute', // the unique attribute ID
		 *     model : 'ModelClass', // the model class name
		 *     name : 'name', // attribute name
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: empty, not entered before,  1: validated, 2: pending validation, 3: validating
		 *     focus : undefined,  // jquery selector that indicates which element to receive input focus initially
		 *     validationDelay: 200,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 *     enableAjaxValidation : true,
		 *     enableClientValidation : true,
		 *     clientValidation : undefined, // function (value, messages, attribute) : client-side validation
		 *     beforeValidateAttribute: undefined, // function (form, attribute) : boolean
		 *     afterValidateAttribute: undefined,  // function (form, attribute, data, hasError)
		 * }
		 */
		attributes : []
	};
})(jQuery);<commit_msg>(Fixes issue 3036) changed way of getting input element values - was not working for texarea<commit_after>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function ($) {
	/*
	 * returns the value of the CActiveForm input field
	 * performs additional checks to get proper values for checkbox / radiobutton / checkBoxList / radioButtonList
	 * @param o object the jQuery object of the input element
	 */
	var getAFValue = function (o) {
		var type = o.attr('type');
		if(o[0].tagName.toLowerCase()=='span') {
			var c = [];
			o.find(':checked').each(function () {
				c.push(this.value);
			});
			return c.join(',');
		}
		if (type === 'checkbox' || type === 'radio') {
			return o.filter(':checked').val();
		} else {
			return o.val();
		}
	};

	/**
	 * yiiactiveform set function.
	 * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function (options) {
		return this.each(function () {
			var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {}),
				$form = $(this);

			if (settings.validationUrl === undefined) {
				settings.validationUrl = $form.attr('action');
			}
			$.each(settings.attributes, function () {
				$.extend(this, {
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					beforeValidateAttribute : settings.beforeValidateAttribute,
					afterValidateAttribute : settings.afterValidateAttribute,
					validatingCssClass : settings.validatingCssClass
				});
				this.value = getAFValue($('#' + this.inputID, $form));
			});
			$(this).data('settings', settings);

			settings.submitting = false;  // whether it is waiting for ajax submission result
			var validate = function (attribute) {
				var changed = false;
				$.each(settings.attributes, function () {
					if (this.value !== getAFValue($('#' + this.inputID, $form))) {
						this.status = 2;
						changed = true;
					}
				});
				if (!changed) {
					return;
				}

				if (settings.timer !== undefined) {
					clearTimeout(settings.timer);
				}
				settings.timer = setTimeout(function () {
					if (settings.submitting || $form.is(':hidden')) {
						return;
					}
					if (attribute.beforeValidateAttribute === undefined || attribute.beforeValidateAttribute($form, attribute)) {
						$.each(settings.attributes, function () {
							if (this.status === 2) {
								this.status = 3;
								$.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass);
							}
						});
						$.fn.yiiactiveform.validate($form, function (data) {
							var hasError = false;
							$.each(settings.attributes, function () {
								if (this.status === 2 || this.status === 3) {
									hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
								}
							});
							if (attribute.afterValidateAttribute !== undefined) {
								attribute.afterValidateAttribute($form, attribute, data, hasError);
							}
						});
					}
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function () {
				if (this.validateOnChange) {
					$('#' + this.inputID, $form).change(function () {
						validate(this);
					}).blur(function () {
						if (this.status !== 2 && this.status !== 3) {
							validate(this);
						}
					});
				}
				if (this.validateOnType) {
					$('#' + this.inputID, $form).keyup(function () {
						if (this.value !== getAFValue($(this))) {
							validate(this);
						}
					});
				}
			});

			if (settings.validateOnSubmit) {
				$form.find(':submit').live('mouseup keyup', function () {
					$form.data('submitObject', $(this));
				});
				var validated = false;
				$form.submit(function () {
					if (validated) {
						return true;
					}
					if (settings.timer !== undefined) {
						clearTimeout(settings.timer);
					}
					settings.submitting = true;
					if (settings.beforeValidate === undefined || settings.beforeValidate($form)) {
						$.fn.yiiactiveform.validate($form, function (data) {
							var hasError = false;
							$.each(settings.attributes, function () {
								hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
							});
							$.fn.yiiactiveform.updateSummary($form, data);
							if (settings.afterValidate === undefined || settings.afterValidate($form, data, hasError)) {
								if (!hasError) {
									validated = true;
									var $button = $form.data('submitObject') || $form.find(':submit:first');
									// TODO: if the submission is caused by "change" event, it will not work
									if ($button.length) {
										$button.click();
									} else {  // no submit button in the form
										$form.submit();
									}
									return;
								}
							}
							settings.submitting = false;
						});
					}
					else {
						settings.submitting = false;
					}
					return false;
				});
			}

			/*
			 * In case of reseting the form we need to reset error messages
			 * NOTE1: $form.reset - does not exist
			 * NOTE2: $form.live('reset', ...) does not work
			 */
			$form.bind('reset', function () {
				/*
				 * because we bind directly to a form reset event, not to a reset button (that could or could not exist),
				 * when this function is executed form elements values have not been reset yet,
				 * because of that we use the setTimeout
				 */
				setTimeout(function () {
					$.each(settings.attributes, function () {
						this.status = 0;
						var $error = $('#' + this.errorID, $form),
							$container = $.fn.yiiactiveform.getInputContainer(this, $form);

						$container.removeClass(
							this.validatingCssClass + ' ' +
							this.errorCssClass + ' ' +
							this.successCssClass
						);

						$error.html('').hide();

						/*
						 * without the setTimeout() we would get here the current entered value before the reset instead of the reseted value
						 */
						this.value = getAFValue($('#' + this.inputID, $form));
					});
					/*
					 * If the form is submited (non ajax) with errors, labels and input gets the class 'error'
					 */
					$('label, input', $form).each(function () {
						$(this).removeClass('error');
					});
					$('#' + settings.summaryID + ' ul').html('');
					$('#' + settings.summaryID).hide();
					//.. set to initial focus on reset
					if (settings.focus !== undefined && !window.location.hash) {
						$(settings.focus).focus();
					}
				}, 1);
			});

			/*
			 * set to initial focus
			 */
			if (settings.focus !== undefined && !window.location.hash) {
				$(settings.focus).focus();
			}
		});
	};

	/**
	 * Returns the container element of the specified attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param form the form jQuery object
	 * @return jquery the jquery representation of the container
	 */
	$.fn.yiiactiveform.getInputContainer = function (attribute, form) {
		if (attribute.inputContainer === undefined) {
			return $('#' + attribute.inputID, form).closest('div');
		} else {
			return $(attribute.inputContainer).filter(':has("#' + attribute.inputID + '")');
		}
	};

	/**
	 * updates the error message and the input container for a particular attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param messages array the json data obtained from the ajax validation request
	 * @param form the form jQuery object
	 * @return boolean whether there is a validation error for the specified attribute
	 */
	$.fn.yiiactiveform.updateInput = function (attribute, messages, form) {
		attribute.status = 1;
		var hasError = messages !== null && $.isArray(messages[attribute.id]) && messages[attribute.id].length > 0,
			$error = $('#' + attribute.errorID, form),
			$container = $.fn.yiiactiveform.getInputContainer(attribute, form);

		$container.removeClass(
			attribute.validatingCssClass + ' ' +
			attribute.errorCssClass + ' ' +
			attribute.successCssClass
		);

		if (hasError) {
			$error.html(messages[attribute.id][0]);
			$container.addClass(attribute.errorCssClass);
		}
		else if (attribute.enableAjaxValidation || attribute.clientValidation) {
			$container.addClass(attribute.successCssClass);
		}
		if (!attribute.hideErrorMessage) {
			$error.toggle(hasError);
		}

		attribute.value = getAFValue($('#' + attribute.inputID, form));
		return hasError;
	};

	/**
	 * updates the error summary, if any.
	 * @param form jquery the jquery representation of the form
	 * @param messages array the json data obtained from the ajax validation request
	 */
	$.fn.yiiactiveform.updateSummary = function (form, messages) {
		var settings = $(form).data('settings'),
			content = '';
		if (settings.summaryID === undefined) {
			return;
		}
		$.each(settings.attributes, function () {
			if (messages && $.isArray(messages[this.id])) {
				$.each(messages[this.id], function (j, message) {
					content = content + '<li>' + message + '</li>';
				});
			}
		});
		$('#' + settings.summaryID + ' ul').html(content);
		$('#' + settings.summaryID).toggle(content !== '');
	};

	/**
	 * Performs the ajax validation request.
	 * This method is invoked internally to trigger the ajax validation.
	 * @param form jquery the jquery representation of the form
	 * @param successCallback function the function to be invoked if the ajax request succeeds
	 * @param errorCallback function the function to be invoked if the ajax request fails
	 */
	$.fn.yiiactiveform.validate = function (form, successCallback, errorCallback) {
		var $form = $(form),
			settings = $form.data('settings'),
			needAjaxValidation = false,
			messages = {};
		$.each(settings.attributes, function () {
			var value,
				msg = [];
			if (this.clientValidation !== undefined && (settings.submitting || this.status === 2 || this.status === 3)) {
				value = getAFValue($('#' + this.inputID, $form));
				this.clientValidation(value, msg, this);
				if (msg.length) {
					messages[this.id] = msg;
				}
			}
			if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status === 2 || this.status === 3)) {
				needAjaxValidation = true;
			}
		});

		if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) {
			if (settings.submitting) {
				// delay callback so that the form can be submitted without problem
				setTimeout(function () {
					successCallback(messages);
				}, 200);
			}
			else {
				successCallback(messages);
			}
			return;
		}

		var $button = $form.data('submitObject'),
			extData = '&' + settings.ajaxVar + '=' + $form.attr('id');
		if ($button && $button.length) {
			extData += '&' + $button.attr('name') + '=' + $button.attr('value');
		}

		$.ajax({
			url : settings.validationUrl,
			type : $form.attr('method'),
			data : $form.serialize() + extData,
			dataType : 'json',
			success : function (data) {
				if (data !== null && typeof data === 'object') {
					$.each(settings.attributes, function () {
						if (!this.enableAjaxValidation) {
							delete data[this.id];
						}
					});
					successCallback($.extend({}, messages, data));
				}
				else {
					successCallback(messages);
				}
			},
			error : function () {
				if (errorCallback !== undefined) {
					errorCallback();
				}
			}
		});
	};

	/**
	 * Returns the configuration for the specified form.
	 * The configuration contains all needed information to perform ajax-based validation.
	 * @param form jquery the jquery representation of the form
	 * @return object the configuration for the specified form.
	 */
	$.fn.yiiactiveform.getSettings = function (form) {
		return $(form).data('settings');
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 200,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		beforeValidateAttribute: undefined, // function (form, attribute) : boolean
		afterValidateAttribute: undefined,  // function (form, attribute, data, hasError)
		beforeValidate: undefined, // function (form) : boolean
		afterValidate: undefined,  // function (form, data, hasError) : boolean
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     id : 'ModelClass_attribute', // the unique attribute ID
		 *     model : 'ModelClass', // the model class name
		 *     name : 'name', // attribute name
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: empty, not entered before,  1: validated, 2: pending validation, 3: validating
		 *     focus : undefined,  // jquery selector that indicates which element to receive input focus initially
		 *     validationDelay: 200,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 *     enableAjaxValidation : true,
		 *     enableClientValidation : true,
		 *     clientValidation : undefined, // function (value, messages, attribute) : client-side validation
		 *     beforeValidateAttribute: undefined, // function (form, attribute) : boolean
		 *     afterValidateAttribute: undefined,  // function (form, attribute, data, hasError)
		 * }
		 */
		attributes : []
	};
})(jQuery);<|endoftext|>