<commit_before>(function($) {
  // Function normalizes height
  // looks for [data-equalizer], and equals
  // height for all [data-equalize-height] children
  //
  // var mq (optional)
  //     mq = a media query, test for true
  //     mq = null, then run resize for all windows
  $.fn.equalizeHeight = (function(mq, childrenSel) {

      var self      = this,
          $children = [],
          childrenSel = childrenSel || '[data-equalize-height]',
          mq = mq || false;

      self.init = function() {
          $children = $(this).find(childrenSel);
          if($children.length) {
              self.equalize(true);
              self.setWatch();
          }
      }

      self.reset = function() {
          $.each($children, function (index, element) {
              $(element).css("height", "");
          });
      }

      self.runCalc = function() {
        var largestHeight = 0;
        if(  !mq || window.matchMedia(mq).matches ) {
            $.each($children, function (index, element) {
                var thisHeight = $(element).outerHeight();
                if (thisHeight > largestHeight) {
                    largestHeight = thisHeight;
                }
            });
        }
        $.each($children, function(index, element) {
            $(element).css({ 'height': largestHeight + "px" });
            console.log(largestHeight + "px");
        });
      }

      self.equalize = function(firstCall) {
          if(firstCall) {
              $(self).waitForImages({
                  finished: function() {
                      self.runCalc();
                  },
                  each: $.noop,
                  waitForAll: true
              });
          }
          else {
              self.reset();
              self.runCalc();
          }

      }

      // Description:
      //    Executes a function a max of once every n milliseconds
      self.throttle = function (func, delay) {
          var timer = null;

          return function () {
              var context = this, args = arguments;

              if (timer == null) {
                timer = setTimeout(function () {
                  func.apply(context, args);
                  timer = null;
                }, delay);
              }
          };
      }

      self.setWatch = function() {
          // Set watch
          $(window).on('resize.equalizer', self.throttle(function () {
              self.equalize();
          }, 50));
      }

      return self.init();
  });
})(jQuery);
<commit_msg>fixing height call for equalize<commit_after>(function($) {
  // Function normalizes height
  // looks for [data-equalizer], and equals
  // height for all [data-equalize-height] children
  //
  // var mq (optional)
  //     mq = a media query, test for true
  //     mq = null, then run resize for all windows
  $.fn.equalizeHeight = (function(mq, childrenSel) {

      var self      = this,
          $children = [],
          childrenSel = childrenSel || '[data-equalize-height]',
          mq = mq || false;

      self.init = function() {
          $children = $(this).find(childrenSel);
          if($children.length) {
              self.equalize(true);
              self.setWatch();
          }
      }

      self.reset = function() {
          $.each($children, function (index, element) {
              $(element).css("height", "");
          });
      }

      self.runCalc = function() {
        var largestHeight = 0;
        if(  !mq || window.matchMedia(mq).matches ) {
            $.each($children, function (index, element) {
                var thisHeight = $(element).outerHeight();
                if (thisHeight > largestHeight) {
                    largestHeight = thisHeight;
                }
            });
        }
        $.each($children, function(index, element) {
            $(element).css({ 'height': largestHeight + "px" });
        });
      }

      self.equalize = function(firstCall) {
          if(firstCall) {
              $(self).waitForImages({
                  finished: function() {
                      self.runCalc();
                  },
                  each: $.noop,
                  waitForAll: true
              });
          }
          else {
              self.reset();
              self.runCalc();
          }

      }

      // Description:
      //    Executes a function a max of once every n milliseconds
      self.throttle = function (func, delay) {
          var timer = null;

          return function () {
              var context = this, args = arguments;

              if (timer == null) {
                timer = setTimeout(function () {
                  func.apply(context, args);
                  timer = null;
                }, delay);
              }
          };
      }

      self.setWatch = function() {
          // Set watch
          $(window).on('resize.equalizer', self.throttle(function () {
              self.equalize();
          }, 50));
      }

      return self.init();
  });
})(jQuery);
<|endoftext|>


<commit_before>var path = require('path');
var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.config.dev');

var app = express();
var compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));

app.use(express.static('public'));

app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});

app.listen(3000, 'localhost', function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log('Listening at http://localhost:3000');
});
<commit_msg>Production server<commit_after>var path = require('path');
var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.config.dev');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');

var isDevelopment = (process.env.NODE_ENV !== 'production');
var port = isDevelopment ? 3000 : process.env.PORT;
var app = express();

if (isDevelopment) {
  var compiler = webpack(config);
  var middleware = webpackMiddleware(compiler, {
    publicPath: config.output.publicPath,
    contentBase: 'src',
    stats: {
      colors: true,
      hash: false,
      timings: true,
      chunks: false,
      chunkModules: false,
      modules: false
    }
  });

  app.use(middleware);
  app.use(webpackHotMiddleware(compiler));
  app.get('*', function response(req, res) {
    res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
    res.end();
  });
} else {
  app.use(express.static(__dirname + '/dist'));
  app.get('*', function response(req, res) {
    res.sendFile(path.join(__dirname, 'dist/index.html'));
  });
}

app.listen(port, '0.0.0.0', function onStart(err) {
  if (err) {
    console.log(err);
  }
  console.info('==> 🌎 Listening on port %s. Open up http://0.0.0.0:%s/ in your browser.', port, port);
});
<|endoftext|>


<commit_before>const spawn = require('child_process').spawn;
const fs = require("fs");
const webpack = require('webpack');
const WebpackDev = require('webpack-dev-server');
const config = require('./webpack.config.dev.js');
const httpProxy = require('http-proxy');
const path = require('path');

const loadSettings = function () {
  let fileName = './settings.json.default.json';
  if (fs.existsSync('./settings.json')) {
    fileName = './settings.json';
  }

  return require(fileName).couchserver || {
    port: process.env.FAUXTON_PORT || 8000,
    contentSecurityPolicy: true,
    proxy: {
      target: process.env.COUCH_HOST || 'http://127.0.0.1:5984',
      changeOrigin: false
    }
  };
};

const settings = loadSettings();

const devSetup = function (cb) {
  console.log('setup dev environment');
  let cmd = 'devSetupWithClean';
  if (settings.noClean) {
    cmd = 'devSetup';
  }

  const grunt = spawn('grunt', [cmd]);

  grunt.stdout.on('data', (data) => {
    console.log(data.toString());
  });

  grunt.stderr.on('error', (data) => {
    console.log('Setup error:', data.toString());
  });

  grunt.on('close', (code) => {
    console.log('dev setup finished with code', code);
    if (code === 0) {
      cb();
    }
  });
};

const defaultHeaderValue = "default-src 'self'; child-src 'self' blob:; img-src 'self' data:; font-src 'self'; " +
                  "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
function getCspHeaders () {
  if (!settings.contentSecurityPolicy) {
    return;
  }

  const cspHeader = settings.contentSecurityPolicyHeader || defaultHeaderValue;

  return {
    'Content-Security-Policy': cspHeader
  };
};

const runWebpackServer = function () {
  const proxy = httpProxy.createServer({
    secure: false,
    changeOrigin: true,
    target: settings.proxy.target
  });

  proxy.on('proxyRes', function (proxyRes) {
    if (proxyRes.headers['set-cookie']) {
      proxyRes.headers['set-cookie'][0] = proxyRes.headers["set-cookie"][0].replace('Secure', '');
    }
  });

  proxy.on('error', function () {
    // don't explode on cancelled requests
  });

  const options = {
    contentBase: path.join(__dirname, '/dist/debug/'),
    host: 'localhost',
    port: process.env.FAUXTON_PORT || 8000,
    overlay: true,
    hot: false,
    historyApiFallback: false,
    stats: {
      colors: true,
    },
    headers: getCspHeaders(),
    setup: (app) => {
      app.all('*', (req, res, next) => {
        const accept = req.headers.accept ? req.headers.accept.split(',') : '';

        if (/application\/json/.test(accept[0])) {
          proxy.web(req, res);
          return;
        }

        next();
      });
    }
  };

  const compiler = webpack(config);
  const server = new WebpackDev(compiler, options);

  server.listen(options.port, '0.0.0.0', function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('listening on', options.host, options.port);
    console.log('Starting first compile. This will take about 10 seconds...');
  });
};

devSetup(runWebpackServer);
<commit_msg>also proxy multipart/form-data to dev server<commit_after>const spawn = require('child_process').spawn;
const fs = require("fs");
const webpack = require('webpack');
const WebpackDev = require('webpack-dev-server');
const config = require('./webpack.config.dev.js');
const httpProxy = require('http-proxy');
const path = require('path');

const loadSettings = function () {
  let fileName = './settings.json.default.json';
  if (fs.existsSync('./settings.json')) {
    fileName = './settings.json';
  }

  return require(fileName).couchserver || {
    port: process.env.FAUXTON_PORT || 8000,
    contentSecurityPolicy: true,
    proxy: {
      target: process.env.COUCH_HOST || 'http://127.0.0.1:5984',
      changeOrigin: false
    }
  };
};

const settings = loadSettings();

const devSetup = function (cb) {
  console.log('setup dev environment');
  let cmd = 'devSetupWithClean';
  if (settings.noClean) {
    cmd = 'devSetup';
  }

  const grunt = spawn('grunt', [cmd]);

  grunt.stdout.on('data', (data) => {
    console.log(data.toString());
  });

  grunt.stderr.on('error', (data) => {
    console.log('Setup error:', data.toString());
  });

  grunt.on('close', (code) => {
    console.log('dev setup finished with code', code);
    if (code === 0) {
      cb();
    }
  });
};

const defaultHeaderValue = "default-src 'self'; child-src 'self' blob:; img-src 'self' data:; font-src 'self'; " +
                  "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';";
function getCspHeaders () {
  if (!settings.contentSecurityPolicy) {
    return;
  }

  const cspHeader = settings.contentSecurityPolicyHeader || defaultHeaderValue;

  return {
    'Content-Security-Policy': cspHeader
  };
};

const runWebpackServer = function () {
  const proxy = httpProxy.createServer({
    secure: false,
    changeOrigin: true,
    target: settings.proxy.target
  });

  proxy.on('proxyRes', function (proxyRes) {
    if (proxyRes.headers['set-cookie']) {
      proxyRes.headers['set-cookie'][0] = proxyRes.headers["set-cookie"][0].replace('Secure', '');
    }
  });

  proxy.on('error', function () {
    // don't explode on cancelled requests
  });

  const options = {
    contentBase: path.join(__dirname, '/dist/debug/'),
    host: 'localhost',
    port: process.env.FAUXTON_PORT || 8000,
    overlay: true,
    hot: false,
    historyApiFallback: false,
    stats: {
      colors: true,
    },
    headers: getCspHeaders(),
    setup: (app) => {
      app.all('*', (req, res, next) => {
        const accept = req.headers.accept ? req.headers.accept.split(',') : '';

        if (/application\/json/.test(accept[0]) || /multipart\/form-data/.test(accept[0])) {
          proxy.web(req, res);
          return;
        }

        next();
      });
    }
  };

  const compiler = webpack(config);
  const server = new WebpackDev(compiler, options);

  server.listen(options.port, '0.0.0.0', function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('listening on', options.host, options.port);
    console.log('Starting first compile. This will take about 10 seconds...');
  });
};

devSetup(runWebpackServer);
<|endoftext|>


<commit_before>/* eslint-disable no-underscore-dangle */
// WP REST API has many underscore dangle's, no point to fight against it
import defaultsDeep from 'lodash/defaultsDeep';
import nthArg from 'lodash/nthArg';
import mapKeys from 'lodash/mapKeys';
import forEach from 'lodash/forEach';
import find from 'lodash/find';
import Immutable from 'immutable';
import isArray from 'lodash/isArray';
import isUndefined from 'lodash/isUndefined';
import { selectQuery } from './selectors';
import WPAPIAdapter from './adapters/wpapi';
import { lastCacheUpdate as lastCacheUpdateSymbol } from './symbols';

import {
  REDUX_WP_API_CALL,
  REDUX_WP_API_REQUEST,
  REDUX_WP_API_SUCCESS,
  REDUX_WP_API_FAILURE,
  REDUX_WP_API_CACHE_HIT,
} from './constants/actions';

import {
  pending,
  resolved,
  rejected,
} from './constants/requestStatus';

export const initialReducerState = Immutable.fromJS({
  requestsByName: {},
  requestsByQuery: {},

  resources: [],
  resourcesIndexes: {},
});

export default class ReduxWPAPI {
  static displayName = '[REDUX-WP-API]';

  static defaultSettings = {
    transformResource: nthArg(0),
    timeout: 30000,
    ttl: 60000,
  }

  constructor({ adapter, ...settings }) {
    this.settings = defaultsDeep({}, settings, ReduxWPAPI.defaultSettings);

    if (!adapter) {
      this.adapter = new WPAPIAdapter(this.settings);
    } else {
      this.adapter = adapter;
    }

    this.settings.customCacheIndexes = {
       // reinforces demandatory indexes besides id
      ...(this.adapter.customCacheIndexes || {}),
      ...this.settings.customCacheIndexes,
    };
  }

  middleware = store => next => action => {
    if (!action || action.type !== REDUX_WP_API_CALL) return next(action);

    const request = this.adapter.buildRequest(action.payload);
    const meta = {
      name: action.payload.name,
      aggregator: this.adapter.getAggregator(this.adapter.getUrl(request)),
      operation: this.adapter.getOperation(request),
      requestAt: Date.now(),
    };

    const payload = {};
    if (meta.operation === 'get') {
      let cache;
      let lastCacheUpdate;
      let data;
      const state = store.getState().wp;
      const indexes = this.adapter.getIndexes(request);
      const resourceLocalID = this.getResourceLocalID(state, meta.aggregator, indexes);

      payload.cacheID = this.adapter.generateCacheID(request);
      payload.page = parseInt(this.adapter.getRequestedPage(request) || 1, 10);

      if (resourceLocalID) {
        cache = state.getIn(['resources', resourceLocalID]);
        data = [resourceLocalID];
      }

      if (cache) {
        lastCacheUpdate = cache[lastCacheUpdateSymbol];
      } else {
        cache = state.getIn(['requestsByQuery', payload.cacheID, payload.page]);
        data = state.get('data');
      }
      if (cache && (resourceLocalID || (isUndefined(resourceLocalID) && !cache.get('error')))) {
        lastCacheUpdate = lastCacheUpdate || cache.get('responseAt') || cache.get('requestAt');
        next({
          meta,
          type: REDUX_WP_API_CACHE_HIT,
          payload: {
            cacheID: payload.cacheID,
            page: payload.page,
            lastCacheUpdate,
            data,
          },
        });

        let ttl;
        if (this.adapter.getTTL) {
          ttl = this.adapter.getTTL(request);
        }

        if (ttl !== 0 && !ttl) {
          ttl = this.settings.ttl;
        }

        if (Date.now() - lastCacheUpdate < ttl) {
          return Promise.resolve(
            selectQuery(meta.name)(store.getState())
          );
        }
      }
    }

    next({
      type: REDUX_WP_API_REQUEST,
      payload,
      meta,
    });

    return (
      this.adapter.sendRequest(request)
      .then(
        response =>
          next({
            type: REDUX_WP_API_SUCCESS,
            payload: { ...payload, response },
            meta: { ...meta, responseAt: Date.now() },
          }),
        error =>
          next({
            type: REDUX_WP_API_FAILURE,
            payload,
            error,
            meta: { ...meta, responseAt: Date.now() },
          })
      )
      .then(() => selectQuery(meta.name)(store.getState()))
    );
  }

  reducer = (state = initialReducerState, action) => {
    switch (action.type) {
      case REDUX_WP_API_CACHE_HIT: {
        const { data, page, cacheID } = action.payload;
        let newState = state.mergeIn(
          ['requestsByName', action.meta.name],
          { page, cacheID }
        );

        if (!newState.getIn(['requestsByQuery', cacheID, page])) {
          newState = (
            newState
            .mergeIn(['requestsByQuery', cacheID, page], {
              status: resolved,
              operation: action.meta.operation,
              error: false,
              requestAt: action.payload.lastCacheUpdate,
              responseAt: action.payload.lastCacheUpdate,
            })
            .setIn(['requestsByQuery', cacheID, 1, 'data'], data)
          );
        }

        return newState;
      }

      case REDUX_WP_API_REQUEST: {
        const { name, operation, requestAt } = action.meta;

        const requestState = {
          status: pending,
          requestAt,
          operation,
        };

        if (operation === 'get') {
          const { page, cacheID } = action.payload;
          let pagination = state.getIn(['requestsByQuery', cacheID, 'pagination']);
          pagination = pagination || {};

          return (
            state
            .mergeIn(['requestsByName', name], { page, cacheID })
            .setIn(['requestsByQuery', cacheID, 'pagination'], pagination)
            .mergeIn(['requestsByQuery', cacheID, page], requestState)
          );
        }

        return (
          state
          .mergeIn(['requestsByName', name], requestState)
          .setIn(['requestsByName', name, 'data'], false)
        );
      }
      case REDUX_WP_API_SUCCESS: {
        let newState = state;
        const { payload, meta: { name, aggregator, requestAt, responseAt } } = action;
        const { cacheID, page, response } = payload;
        let body = response;

        const requestState = {
          responseAt,
          status: resolved,
          error: false,
        };

        if (!isArray(body)) {
          body = [body];
        }

        const data = [];
        const additionalData = { lastCacheUpdate: requestState.responseAt };

        body.forEach(resource => {
          newState = this.indexResource(newState, aggregator, resource, additionalData);
          data.push(this.getResourceLocalID(newState, aggregator, resource));
        });

        if (action.meta.operation === 'get') {
          const pagination = this.adapter.getPagination(response);
          newState = newState.mergeIn(['requestsByQuery', cacheID, page], requestState);
          newState = newState.setIn(['requestsByQuery', cacheID, page, 'data'], data);
          newState = newState.setIn(['requestsByQuery', cacheID, 'pagination'], pagination);
        } else if (newState.getIn(['requestsByName', name, 'requestAt']) === requestAt) {
          newState = (
            newState
            .setIn(['requestsByName', action.meta.name, 'data'], data)
            .mergeIn(['requestsByName', name], requestState)
          );
        }

        return newState;
      }
      case REDUX_WP_API_FAILURE: {
        const { error } = action;
        const { page, cacheID } = action.payload;
        const requestState = {
          status: rejected,
          error: {
            message: error.message,
            status: error.status,
          },
        };

        if (action.meta.operation === 'get') {
          return state.mergeIn(['requestsByQuery', cacheID, page], requestState);
        }

        return state.mergeIn(['requestsByName', action.meta.name], requestState);
      }
      default: return state;
    }
  }

  getResourceLocalID(state, aggregator, resource) {
    const indexers = this.settings.customCacheIndexes[aggregator];
    const indexBy = find(
      isArray(indexers) ? ['id'].concat(indexers) : ['id', indexers],
      indexer =>
        !isUndefined(resource[indexer]) &&
        !isUndefined(
          state.getIn(['resourcesIndexes', aggregator, indexer, resource[indexer]])
        )
    );

    return indexBy && state.getIn(['resourcesIndexes', aggregator, indexBy, resource[indexBy]]);
  }

  indexResource(state, aggregator, resource, meta) {
    let newState = state;
    let _embedded;
    const curies = (resource._links || {}).curies;
    const _links = this.resolveAliases(resource._links, curies) || {};
    delete _links.curies;

    let resourceLocalID = this.getResourceLocalID(state, aggregator, resource);
    let oldState = {};
    if (!isUndefined(resourceLocalID)) {
      oldState = newState.getIn(['resources', resourceLocalID]);
    }

    if (resource._embedded) {
      _embedded = { ...(oldState || { })._embedded };
      const alisedEmbedded = this.resolveAliases(resource._embedded, curies);
      forEach(alisedEmbedded, (embeddable, relName) => {
        forEach(_links[relName], (link, index) => {
          const embeddedAggregator = this.adapter.getAggregator(link.href);
          if (!embeddedAggregator) return;

          let toEmbed = alisedEmbedded[relName][index];
          toEmbed = (isArray(toEmbed) ? toEmbed : [toEmbed]).reduce((collection, toBeEmbedded) => {
            if (toBeEmbedded.code === 'rest_no_route') return collection;

            newState = this.indexResource(newState, embeddedAggregator, toBeEmbedded, meta);
            collection.push(this.getResourceLocalID(newState, embeddedAggregator, toBeEmbedded));

            return collection;
          }, []);

          if (!isArray(alisedEmbedded[relName][index])) {
            if (!toEmbed.length) {
              return;
            }

            toEmbed = toEmbed[0];
          }

          _embedded[this.adapter.embedLinkAs({ name: relName, ...link })] = toEmbed;
        });
      });
    }

    if (isUndefined(resourceLocalID)) {
      resourceLocalID = newState.get('resources').size;
    }

    let resourceTransformed = {
      ...oldState,
      ...resource,
      ...meta,
      _links,
      _embedded,
    };

    if (this.adapter.transformResource) {
      resourceTransformed = this.adapter.transformResource(resourceTransformed);
    }

    if (this.settings.transformResource) {
      resourceTransformed = this.settings.transformResource(resourceTransformed);
    }

    newState = newState.setIn(['resources', resourceLocalID], resourceTransformed);
    const indexers = this.settings.customCacheIndexes[aggregator];

    forEach(isArray(indexers) ? ['id'].concat(indexers) : ['id', indexers], indexer => {
      if (!isUndefined(resource[indexer])) {
        newState = newState.setIn(
          ['resourcesIndexes', aggregator, indexer, resource[indexer]],
          resourceLocalID
        );
      }
    });

    return newState;
  }

  resolveAliases(map, curies) {
    return (curies || []).reduce((remapped, cury) => {
      const alias = new RegExp(`^${cury.name}:`);
      return mapKeys(remapped, (value, key) =>
        cury.href.replace(/\{rel\}/g, key.replace(alias, ''))
      );
    }, map) || map;
  }
}
<commit_msg>Refactor SUCCESS so it doesn't relies on `getResourceLocalID`<commit_after>/* eslint-disable no-underscore-dangle */
// WP REST API has many underscore dangle's, no point to fight against it
import defaultsDeep from 'lodash/defaultsDeep';
import nthArg from 'lodash/nthArg';
import mapKeys from 'lodash/mapKeys';
import forEach from 'lodash/forEach';
import find from 'lodash/find';
import noop from 'lodash/noop';
import Immutable from 'immutable';
import isArray from 'lodash/isArray';
import isUndefined from 'lodash/isUndefined';
import { selectQuery } from './selectors';
import WPAPIAdapter from './adapters/wpapi';
import { lastCacheUpdate as lastCacheUpdateSymbol } from './symbols';

import {
  REDUX_WP_API_CALL,
  REDUX_WP_API_REQUEST,
  REDUX_WP_API_SUCCESS,
  REDUX_WP_API_FAILURE,
  REDUX_WP_API_CACHE_HIT,
} from './constants/actions';

import {
  pending,
  resolved,
  rejected,
} from './constants/requestStatus';

export const initialReducerState = Immutable.fromJS({
  requestsByName: {},
  requestsByQuery: {},

  resources: [],
  resourcesIndexes: {},
});

export default class ReduxWPAPI {
  static displayName = '[REDUX-WP-API]';

  static defaultSettings = {
    transformResource: nthArg(0),
    timeout: 30000,
    ttl: 60000,
  }

  constructor({ adapter, ...settings }) {
    this.settings = defaultsDeep({}, settings, ReduxWPAPI.defaultSettings);

    if (!adapter) {
      this.adapter = new WPAPIAdapter(this.settings);
    } else {
      this.adapter = adapter;
    }

    this.settings.customCacheIndexes = {
       // reinforces demandatory indexes besides id
      ...(this.adapter.customCacheIndexes || {}),
      ...this.settings.customCacheIndexes,
    };
  }

  middleware = store => next => action => {
    if (!action || action.type !== REDUX_WP_API_CALL) return next(action);

    const request = this.adapter.buildRequest(action.payload);
    const meta = {
      name: action.payload.name,
      aggregator: this.adapter.getAggregator(this.adapter.getUrl(request)),
      operation: this.adapter.getOperation(request),
      requestAt: Date.now(),
    };

    const payload = {};
    if (meta.operation === 'get') {
      let cache;
      let lastCacheUpdate;
      let data;
      const state = store.getState().wp;
      const indexes = this.adapter.getIndexes(request);
      const resourceLocalID = this.getResourceLocalID(state, meta.aggregator, indexes);

      payload.cacheID = this.adapter.generateCacheID(request);
      payload.page = parseInt(this.adapter.getRequestedPage(request) || 1, 10);

      if (resourceLocalID) {
        cache = state.getIn(['resources', resourceLocalID]);
        data = [resourceLocalID];
      }

      if (cache) {
        lastCacheUpdate = cache[lastCacheUpdateSymbol];
      } else {
        cache = state.getIn(['requestsByQuery', payload.cacheID, payload.page]);
        data = state.get('data');
      }
      if (cache && (resourceLocalID || (isUndefined(resourceLocalID) && !cache.get('error')))) {
        lastCacheUpdate = lastCacheUpdate || cache.get('responseAt') || cache.get('requestAt');
        next({
          meta,
          type: REDUX_WP_API_CACHE_HIT,
          payload: {
            cacheID: payload.cacheID,
            page: payload.page,
            lastCacheUpdate,
            data,
          },
        });

        let ttl;
        if (this.adapter.getTTL) {
          ttl = this.adapter.getTTL(request);
        }

        if (ttl !== 0 && !ttl) {
          ttl = this.settings.ttl;
        }

        if (Date.now() - lastCacheUpdate < ttl) {
          return Promise.resolve(
            selectQuery(meta.name)(store.getState())
          );
        }
      }
    }

    next({
      type: REDUX_WP_API_REQUEST,
      payload,
      meta,
    });

    return (
      this.adapter.sendRequest(request)
      .then(
        response =>
          next({
            type: REDUX_WP_API_SUCCESS,
            payload: { ...payload, response },
            meta: { ...meta, responseAt: Date.now() },
          }),
        error =>
          next({
            type: REDUX_WP_API_FAILURE,
            payload,
            error,
            meta: { ...meta, responseAt: Date.now() },
          })
      )
      .then(() => selectQuery(meta.name)(store.getState()))
    );
  }

  reducer = (state = initialReducerState, action) => {
    switch (action.type) {
      case REDUX_WP_API_CACHE_HIT: {
        const { data, page, cacheID } = action.payload;
        let newState = state.mergeIn(
          ['requestsByName', action.meta.name],
          { page, cacheID }
        );

        if (!newState.getIn(['requestsByQuery', cacheID, page])) {
          newState = (
            newState
            .mergeIn(['requestsByQuery', cacheID, page], {
              status: resolved,
              operation: action.meta.operation,
              error: false,
              requestAt: action.payload.lastCacheUpdate,
              responseAt: action.payload.lastCacheUpdate,
            })
            .setIn(['requestsByQuery', cacheID, 1, 'data'], data)
          );
        }

        return newState;
      }

      case REDUX_WP_API_REQUEST: {
        const { name, operation, requestAt } = action.meta;

        const requestState = {
          status: pending,
          requestAt,
          operation,
        };

        if (operation === 'get') {
          const { page, cacheID } = action.payload;
          let pagination = state.getIn(['requestsByQuery', cacheID, 'pagination']);
          pagination = pagination || {};

          return (
            state
            .mergeIn(['requestsByName', name], { page, cacheID })
            .setIn(['requestsByQuery', cacheID, 'pagination'], pagination)
            .mergeIn(['requestsByQuery', cacheID, page], requestState)
          );
        }

        return (
          state
          .mergeIn(['requestsByName', name], requestState)
          .setIn(['requestsByName', name, 'data'], false)
        );
      }
      case REDUX_WP_API_SUCCESS: {
        let newState = state;
        const { payload, meta: { name, aggregator, requestAt, responseAt } } = action;
        const { cacheID, page, response } = payload;
        let body = response;

        const requestState = {
          responseAt,
          status: resolved,
          error: false,
        };

        if (!isArray(body)) {
          body = [body];
        }

        const data = [];
        const additionalData = { lastCacheUpdate: requestState.responseAt };

        body.forEach(resource => {
          newState = this.indexResource(newState, aggregator, resource, additionalData, id => {
            data.push(id);
          });
        });

        if (action.meta.operation === 'get') {
          const pagination = this.adapter.getPagination(response);
          newState = newState.mergeIn(['requestsByQuery', cacheID, page], requestState);
          newState = newState.setIn(['requestsByQuery', cacheID, page, 'data'], data);
          newState = newState.setIn(['requestsByQuery', cacheID, 'pagination'], pagination);
        } else if (newState.getIn(['requestsByName', name, 'requestAt']) === requestAt) {
          newState = (
            newState
            .setIn(['requestsByName', action.meta.name, 'data'], data)
            .mergeIn(['requestsByName', name], requestState)
          );
        }

        return newState;
      }
      case REDUX_WP_API_FAILURE: {
        const { error } = action;
        const { page, cacheID } = action.payload;
        const requestState = {
          status: rejected,
          error: {
            message: error.message,
            status: error.status,
          },
        };

        if (action.meta.operation === 'get') {
          return state.mergeIn(['requestsByQuery', cacheID, page], requestState);
        }

        return state.mergeIn(['requestsByName', action.meta.name], requestState);
      }
      default: return state;
    }
  }

  getResourceLocalID(state, aggregator, resource) {
    const indexers = this.settings.customCacheIndexes[aggregator];
    const indexBy = find(
      isArray(indexers) ? ['id'].concat(indexers) : ['id', indexers],
      indexer =>
        !isUndefined(resource[indexer]) &&
        !isUndefined(
          state.getIn(['resourcesIndexes', aggregator, indexer, resource[indexer]])
        )
    );

    return indexBy && state.getIn(['resourcesIndexes', aggregator, indexBy, resource[indexBy]]);
  }

  indexResource(state, aggregator, resource, meta, onIndex = noop) {
    let newState = state;
    let _embedded;
    const curies = (resource._links || {}).curies;
    const _links = this.resolveAliases(resource._links, curies) || {};
    delete _links.curies;

    let resourceLocalID = this.getResourceLocalID(state, aggregator, resource);
    let oldState = {};
    if (!isUndefined(resourceLocalID)) {
      oldState = newState.getIn(['resources', resourceLocalID]);
    }

    if (resource._embedded) {
      _embedded = { ...(oldState || { })._embedded };
      const alisedEmbedded = this.resolveAliases(resource._embedded, curies);
      forEach(alisedEmbedded, (embeddable, relName) => {
        forEach(_links[relName], (link, index) => {
          const embeddedAggregator = this.adapter.getAggregator(link.href);
          if (!embeddedAggregator) return;

          let toEmbed = alisedEmbedded[relName][index];
          toEmbed = (isArray(toEmbed) ? toEmbed : [toEmbed]).reduce((collection, toBeEmbedded) => {
            if (toBeEmbedded.code === 'rest_no_route') return collection;

            newState = this.indexResource(newState, embeddedAggregator, toBeEmbedded, meta);
            collection.push(this.getResourceLocalID(newState, embeddedAggregator, toBeEmbedded));

            return collection;
          }, []);

          if (!isArray(alisedEmbedded[relName][index])) {
            if (!toEmbed.length) {
              return;
            }

            toEmbed = toEmbed[0];
          }

          _embedded[this.adapter.embedLinkAs({ name: relName, ...link })] = toEmbed;
        });
      });
    }

    if (isUndefined(resourceLocalID)) {
      resourceLocalID = newState.get('resources').size;
    }

    let resourceTransformed = {
      ...oldState,
      ...resource,
      ...meta,
      _links,
      _embedded,
    };

    if (this.adapter.transformResource) {
      resourceTransformed = this.adapter.transformResource(resourceTransformed);
    }

    if (this.settings.transformResource) {
      resourceTransformed = this.settings.transformResource(resourceTransformed);
    }

    newState = newState.setIn(['resources', resourceLocalID], resourceTransformed);
    const indexers = this.settings.customCacheIndexes[aggregator];

    forEach(isArray(indexers) ? ['id'].concat(indexers) : ['id', indexers], indexer => {
      if (!isUndefined(resource[indexer])) {
        newState = newState.setIn(
          ['resourcesIndexes', aggregator, indexer, resource[indexer]],
          resourceLocalID
        );
      }
    });

    onIndex(resourceLocalID);
    return newState;
  }

  resolveAliases(map, curies) {
    return (curies || []).reduce((remapped, cury) => {
      const alias = new RegExp(`^${cury.name}:`);
      return mapKeys(remapped, (value, key) =>
        cury.href.replace(/\{rel\}/g, key.replace(alias, ''))
      );
    }, map) || map;
  }
}
<|endoftext|>


<commit_before>/**
 * A HTML5 Validation replacement
 */
(function (document, window, $) {
    "use strict";

    var ERROR_CLASS_NAME = 'form-error', INPUT_ERROR_CLASS = 'invalid', LOADING_CLASS = 'loading',
        ARIA_INVALID = 'aria-invalid', REMOTE = 'data-remote', REMOTE_ACTION = 'data-remote-action';
    if (!window.FlexCss) {
        window.FlexCss = {};
    }

    var FlexCss = window.FlexCss;

    /**
     * jQuery helper to serialize a form to json
     * @returns {{}}
     */
    $.fn.serializeFormJSON = function () {

        var o = {};
        var a = this.serializeArray();
        $.each(a, function () {
            if (o[this.name]) {
                if (!o[this.name].push) {
                    o[this.name] = [o[this.name]];
                }
                o[this.name].push(this.value || '');
            } else {
                o[this.name] = this.value || '';
            }
        });
        return o;
    };

    /**
     * Enhanced flexcss forms
     * @param {HTMLElement} form
     * @param {Object} options
     * @constructor
     */
    FlexCss.Form = function (form, options) {

        var self = this;

        self.tooltips = null;

        self.options = {
            createTooltips: true,
            appendError: false,
            ajaxSubmitType: 'POST',
            ajaxJsonContentType: 'application/json; charset=utf-8'
        };

        self.options = $.extend(self.options, options);

        // set form class as widget
        // Forms are very different to classical widgets,
        // we will not use our base widget class for this but just self
        self.hfWidgetInstance = self;

        /**
         * Submits this form, either via ajax or just classical (default)
         * @param {HTMLElement} thisForm
         * @param {Event} e
         * @returns {*}
         */
        self.submitFunction = function (thisForm, e) {
            var shouldUseAjax = thisForm.getAttribute(REMOTE), ajaxPostUrl =
                    thisForm.getAttribute(REMOTE_ACTION) ||
                    thisForm.getAttribute('action') || window.location.href,
                useJson = 'json' === shouldUseAjax;
            thisForm.classList.add(LOADING_CLASS);

            $(thisForm).trigger('flexcss.form.submit', e, [self, thisForm]);

            if (null === shouldUseAjax) {
                return thisForm.submit();
            }

            // prevent form from submit normally
            e.preventDefault();

            // support either JSON request payload or normal payload submission
            var serverCall = useJson ? $.ajax({
                contentType: self.options.ajaxJsonContentType,
                type: self.options.ajaxSubmitType,
                url: ajaxPostUrl,
                data: JSON.stringify($(thisForm).serializeFormJSON())
            }) : $.ajax({
                type: self.options.ajaxSubmitType,
                url: ajaxPostUrl,
                data: $(thisForm).serialize()
            });

            serverCall.then(function (r) {
                self._remoteValidationFunction.apply(self, [r]);
            }).always(function (r) {
                $(thisForm).trigger('flexcss.form.ajaxCompleted', e, [self, thisForm, r]);
                // always remove error class
                thisForm.classList.remove(LOADING_CLASS);
            });
        };

        /**
         * This form
         * @returns {HTMLElement}
         */
        self.getForm = function () {
            return form;
        };

        /**
         * A List of Validators
         * @type {Object}
         * @private
         */
        self._validators = FlexCss.Form.globalValidators;

        /**
         * @type {Function}
         * @private
         */
        self._remoteValidationFunction = FlexCss.Form.globalRemoteValidationFunction;

        /**
         * Registers a function that handles remote validation
         * @param {Function} func
         * @returns {window.FlexCss.Form}
         */
        self.registerRemoteValidation = function (func) {
            self._remoteValidationFunction = func;
            return self;
        };

        /**
         * @param {HTMLElement} field
         * @param {ValidityState} validity
         * @returns {*}
         * @private
         */
        function _setupErrorMessages(field, validity) {
            return FlexCss.Form.globalErrorMessageHandler ?
                FlexCss.Form.globalErrorMessageHandler.apply(self, [field, validity]) : false;
        }

        /**
         * @param {HTMLElement} thisParent
         * @private
         */
        function _removeElementErrors(thisParent) {
            var errors = thisParent.querySelectorAll('.' + ERROR_CLASS_NAME), inputsWithErrorClasses =
                thisParent.querySelectorAll('.' + INPUT_ERROR_CLASS);
            for (var elementErrorIndex = 0; elementErrorIndex < errors.length; elementErrorIndex++) {
                errors[elementErrorIndex].parentNode.removeChild(errors[elementErrorIndex]);
            }
            for (var inputErrorIndex = 0; inputErrorIndex < inputsWithErrorClasses.length; inputErrorIndex++) {
                inputsWithErrorClasses[inputErrorIndex].classList.remove(INPUT_ERROR_CLASS);
                if (self.tooltips) {
                    self.tooltips.removeTooltip(inputsWithErrorClasses[inputErrorIndex]);
                }
            }
        }

        /**
         * Registers a custom validator
         * @param {String} name
         * @param {Function} validator a validation function should always return either a Future(true) or Future(false)
         * even when the field has been invalidated with `setCustomValidity`, because of different browser bugs
         * we can't rely on that
         * @returns {window.FlexCss.Form}
         */
        self.registerValidator = function (name, validator) {
            self._validators[name] = validator;
            return self;
        };

        /**
         * Runs async validation
         * @param {String} validationRef
         * @param {HTMLElement} field
         * @returns {*}
         * @private
         */
        function _runValidation(validationRef, field) {
            if (!self._validators[validationRef]) {
                throw 'Could not found validator: ' + validationRef;
            }
            var cl = field.classList, future = self._validators[validationRef].apply(self, [field, form]);
            cl.add(LOADING_CLASS);
            future.done(function () {
                cl.remove(LOADING_CLASS);
            });
            return future;
        }

        /**
         * Run custom validations for elements, validations are done async do support XHR Requests or other stuff
         *
         * @param {Array|NodeList} fields
         * @returns {$.Deferred} contains either true if validations passed or false if something went wrong
         * @private
         */
        function _customValidationsForElements(fields) {
            var futures = [], fieldsLength = fields.length, checkedFields = [];
            for (var iVal = 0; iVal < fieldsLength; iVal++) {
                var field = fields[iVal], validationRef = field.getAttribute('data-validate'), validity = field.validity;
                if (self._validators[validationRef]) {
                    // use local validation first and then continue with custom validations
                    if (!validity.customError && !validity.valid) {
                        continue;
                    }
                    checkedFields.push(field);
                    futures.push(_runValidation(validationRef, field));
                }
            }
            return $.when.apply(self, futures).then(function () {
                var allFutures = arguments, l = allFutures.length;
                var result = {
                    checkedFields: checkedFields,
                    foundAnyError: false
                };

                for (var fI = 0; fI < l; fI++) {
                    if (!allFutures[fI]) {
                        result.foundAnyError = true;
                    }
                }
                return $.Deferred().resolve(result);
            });
        }

        /**
         * Will handle errors for given fields
         * @param {Array|NodeList} fields
         * @param {Boolean} removeAllErrors
         */
        self.prepareErrors = function (fields, removeAllErrors) {
            if (removeAllErrors) {
                _removeElementErrors(form);
            }
            // We save all validations in an extra property because we need to reset the validity due some
            // implementation errors in other browsers then chrome
            for (var i = 0; i < fields.length; i++) {
                var field = fields[i], parent = field.parentNode, validity = field.validity;
                field.flexFormsSavedValidity = JSON.parse(JSON.stringify(validity));
                if (validity && !validity.valid) {
                    if (!removeAllErrors) {
                        // Remove current errors:
                        _removeElementErrors(parent);
                    }
                    // setup custom error messages:
                    _setupErrorMessages(field, validity);
                    field.classList.add('invalid');
                    field.setAttribute(ARIA_INVALID, 'true');
                    if (self.options.appendError) {
                        parent.insertAdjacentHTML("beforeend", '
' +
                        field.validationMessage +
                        "
");
                    }
                    field.flexFormsSavedValidationMessage = field.validationMessage;
                } else {
                    field.classList.remove('invalid');
                    field.setAttribute(ARIA_INVALID, 'false');

                    _removeElementErrors(parent);
                }
                // FIXME: We have to reset the custom validity here to allow native validations work again
                field.setCustomValidity('');
            }
        };

        function validateCustomFields() {
            return _customValidationsForElements(
                form.querySelectorAll("[data-validate]"));
        }

        /**
         * Creates an array from a node list with invalid items
         * On Firefox also Fieldset's seems to be invalid, remove them
         * @param list
         * @returns {Array}
         * @private
         */
        function _createArrayFromInvalidFieldList(list) {
            var arr = [];
            for (var i = 0; i < list.length; ++i) {
                var n = list[i];
                if (!(n instanceof HTMLFieldSetElement)) {
                    arr.push(n);
                }
            }
            return arr;
        }

        /**
         * Formats the error content for the tooltip
         * @param {String} error
         * @returns {String}
         * @private
         */
        function _formatErrorTooltip(error) {
            return ' ' + error;
        }

        /**
         * Creates a tooltip at given element, will create a new instance if not created
         * @param {HTMLElement} target
         * @param {Boolean} [remove]
         * @private
         */
        function _showAndOrCreateTooltip(target, remove) {
            if (!self.tooltips && self.options.createTooltips) {
                self.tooltips = new FlexCss.Tooltip(form, {
                    containerClass: 'error-tooltip'
                });
            }

            setTimeout(function () {
                if (!target.flexFormsSavedValidity) {
                    return;
                }
                if (!target.flexFormsSavedValidity.valid && target.classList.contains(INPUT_ERROR_CLASS)) {
                    self.tooltips.createTooltip(target,
                        _formatErrorTooltip(target.flexFormsSavedValidationMessage), false);
                } else {
                    if (remove) {
                        self.tooltips.removeTooltip(target);
                    }
                }
            }, 0);

        }

        /**
         * Initializes validation for a given form, registers event handlers
         */
        function initFormValidation() {
            // Suppress the default bubbles
            var invalidFormFired = false, currentValidationFuture;
            form.addEventListener("invalid", function (e) {
                e.preventDefault();
                var invalidFields = form.querySelectorAll(":invalid");
                var arr = _createArrayFromInvalidFieldList(invalidFields);
                // Prevent fire this N times:
                if (arr.indexOf(e.target) > 0) {
                    return;
                }
                // focus the first field:
                if (arr.length > 0) {
                    setTimeout(function () {
                        arr[0].focus();
                        _showAndOrCreateTooltip(arr[0]);
                    }, 0);
                }
                currentValidationFuture = $.Deferred();

                var validation = validateCustomFields();
                self.prepareErrors(arr, true);
                validation.done(function (r) {
                    self.prepareErrors(r.checkedFields, false);
                    currentValidationFuture.resolve(r);
                    invalidFormFired = false;
                });

            }, true);

            // handle focus out for text elements
            // Will show an error if field was invalid the first time
            form.addEventListener("blur", function (e) {
                if (self.tooltips) {
                    self.tooltips.removeTooltip(e.target);
                }
                var target = e.target, hasError = false;

                if (!_checkIsValidBlurFocusElement(target)) {
                    return;
                }

                if (target.classList.contains(INPUT_ERROR_CLASS)) {
                    hasError = true;
                }
                _customValidationsForElements([e.target]).done(function () {
                    self.prepareErrors([e.target], false);
                    if (!hasError) {
                        _showAndOrCreateTooltip(e.target);
                    }
                });

            }, true);

            /**
             * Validates if target is a valid input field to check blur and focus events
             * @param {HTMLElement} target
             * @returns {boolean}
             */
            function _checkIsValidBlurFocusElement(target) {
                var attr = target.getAttribute('type');
                return !((attr === 'checkbox' || attr === 'option' || attr === 'submit' ||
                target instanceof HTMLSelectElement || !(target instanceof HTMLInputElement ||
                target instanceof HTMLTextAreaElement)));
            }

            // handle focus on input elements
            // will show an error if field is invalid
            form.addEventListener("focus", function (e) {
                // do not track errors for checkbox and radios on focus:
                if (!_checkIsValidBlurFocusElement(e.target)) {
                    return;
                }
                _showAndOrCreateTooltip(e.target);
            }, true);

            // Handle change for checkbox, radios and selects
            form.addEventListener("change", function (e) {
                var name = e.target.getAttribute('name');
                if (name) {
                    var inputs = form.querySelectorAll('[name="' + name + '"]');
                    _customValidationsForElements(form, inputs).done(function () {
                        self.prepareErrors(inputs, false);
                        _showAndOrCreateTooltip(e.target, true);
                    });
                }
            });

            // prevent default if form is invalid
            var submitListener = function (e) {

                if (form.classList.contains(LOADING_CLASS)) {
                    e.preventDefault();
                    return false;
                }

                form.classList.add(LOADING_CLASS);
                form.removeEventListener("submit", submitListener);
                _removeElementErrors(form);
                e.preventDefault();
                // reset:
                if (form.checkValidity()) {
                    form.addEventListener("submit", submitListener);
                    // Custom validations did never pass
                    currentValidationFuture = $.Deferred();
                    var validation = validateCustomFields();
                    validation.done(function (r) {
                        // focus first invalid field:
                        for (var i = 0; i < r.checkedFields.length; i++) {
                            var f = r.checkedFields[i];
                            if (!f.validity.valid) {
                                f.focus();
                                break;
                            }
                        }
                        self.prepareErrors(r.checkedFields, false);
                        currentValidationFuture.resolve(r);
                    });
                    currentValidationFuture.done(function (r) {
                        form.classList.remove(LOADING_CLASS);
                        if (!r.foundAnyError) {
                            // Handle submitting the form to server:
                            self._handleSubmit(e);
                        }
                    });
                } else {
                    form.classList.remove(LOADING_CLASS);
                    form.addEventListener("submit", submitListener);
                }
            };
            form.addEventListener("submit", submitListener);
        }

        initFormValidation();

        /**
         * Handles submitting, optionally allows to stop submitting
         * @param e
         * @private
         */
        self._handleSubmit = function (e) {
            $(form).trigger('flexcss.form.beforeSubmit', e, [self, form]);
            self.submitFunction.apply(self, [form, e]);
        };

    };

    FlexCss.Form.globalErrorMessageHandler = function () {
    };

    /**
     * Registers a global event Handler
     * @param errorFunc
     */
    FlexCss.Form.registerErrorMessageHandler = function (errorFunc) {
        FlexCss.Form.globalErrorMessageHandler = errorFunc;
    };

    /**
     * Initialize forms for a specific selector
     * @param {String} selector
     * @param {Object} options
     */
    FlexCss.Form.init = function (selector, options) {
        var forms = document.querySelectorAll(selector);
        for (var i = 0; i < forms.length; i++) {
            new FlexCss.Form(forms[i], options);
        }
    };

    FlexCss.Form.globalValidators = [];
    FlexCss.Form.globalRemoteValidationFunction = function () {
    };

    /**
     * Registers a global validator that is usable on all form instasnces
     * @param {String} name
     * @param {Function} validator
     * @returns {Function}
     */
    FlexCss.Form.registerValidator = function (name, validator) {
        FlexCss.Form.globalValidators[name] = validator;
        return FlexCss.Form;
    };

    /**
     * Registers a global function that is called when a form should validate the response of a server
     * @param {Function} func
     * @returns {Function}
     */
    FlexCss.Form.registerGlobalRemoteValidationFunction = function (func) {
        FlexCss.Form.globalRemoteValidationFunction = func;
        return FlexCss.Form;
    };

    // initilize forms for all forms
    FlexCss.Form.init("form", {});

})(document, window, jQuery);<commit_msg>now using the right remote function instance<commit_after>/**
 * A HTML5 Validation replacement
 */
(function (document, window, $) {
    "use strict";

    var ERROR_CLASS_NAME = 'form-error', INPUT_ERROR_CLASS = 'invalid', LOADING_CLASS = 'loading',
        ARIA_INVALID = 'aria-invalid', REMOTE = 'data-remote', REMOTE_ACTION = 'data-remote-action';
    if (!window.FlexCss) {
        window.FlexCss = {};
    }

    var FlexCss = window.FlexCss;

    /**
     * jQuery helper to serialize a form to json
     * @returns {{}}
     */
    $.fn.serializeFormJSON = function () {

        var o = {};
        var a = this.serializeArray();
        $.each(a, function () {
            if (o[this.name]) {
                if (!o[this.name].push) {
                    o[this.name] = [o[this.name]];
                }
                o[this.name].push(this.value || '');
            } else {
                o[this.name] = this.value || '';
            }
        });
        return o;
    };

    /**
     * Enhanced flexcss forms
     * @param {HTMLElement} form
     * @param {Object} options
     * @constructor
     */
    FlexCss.Form = function (form, options) {

        var self = this;

        self.tooltips = null;

        self.options = {
            createTooltips: true,
            appendError: false,
            ajaxSubmitType: 'POST',
            ajaxJsonContentType: 'application/json; charset=utf-8'
        };

        self.options = $.extend(self.options, options);

        // set form class as widget
        // Forms are very different to classical widgets,
        // we will not use our base widget class for this but just self
        self.hfWidgetInstance = self;

        /**
         * Submits this form, either via ajax or just classical (default)
         * @param {HTMLElement} thisForm
         * @param {Event} e
         * @returns {*}
         */
        self.submitFunction = function (thisForm, e) {
            var shouldUseAjax = thisForm.getAttribute(REMOTE), ajaxPostUrl =
                    thisForm.getAttribute(REMOTE_ACTION) ||
                    thisForm.getAttribute('action') || window.location.href,
                useJson = 'json' === shouldUseAjax;
            thisForm.classList.add(LOADING_CLASS);

            $(thisForm).trigger('flexcss.form.submit', e, [self, thisForm]);

            if (null === shouldUseAjax) {
                return thisForm.submit();
            }

            // prevent form from submit normally
            e.preventDefault();

            // support either JSON request payload or normal payload submission
            var serverCall = useJson ? $.ajax({
                contentType: self.options.ajaxJsonContentType,
                type: self.options.ajaxSubmitType,
                url: ajaxPostUrl,
                data: JSON.stringify($(thisForm).serializeFormJSON())
            }) : $.ajax({
                type: self.options.ajaxSubmitType,
                url: ajaxPostUrl,
                data: $(thisForm).serialize()
            });

            serverCall.then(function (r) {
                (self._remoteValidationFunction || FlexCss.Form.globalRemoteValidationFunction).apply(self, [r]);
            }).always(function (r) {
                $(thisForm).trigger('flexcss.form.ajaxCompleted', e, [self, thisForm, r]);
                // always remove error class
                thisForm.classList.remove(LOADING_CLASS);
            });
        };

        /**
         * This form
         * @returns {HTMLElement}
         */
        self.getForm = function () {
            return form;
        };

        /**
         * A List of Validators
         * @type {Object}
         * @private
         */
        self._validators = FlexCss.Form.globalValidators;

        /**
         * @type {Function}
         * @private
         */
        self._remoteValidationFunction = null;

        /**
         * Registers a function that handles remote validation
         * @param {Function} func
         * @returns {window.FlexCss.Form}
         */
        self.registerRemoteValidation = function (func) {
            self._remoteValidationFunction = func;
            return self;
        };

        /**
         * @param {HTMLElement} field
         * @param {ValidityState} validity
         * @returns {*}
         * @private
         */
        function _setupErrorMessages(field, validity) {
            return FlexCss.Form.globalErrorMessageHandler ?
                FlexCss.Form.globalErrorMessageHandler.apply(self, [field, validity]) : false;
        }

        /**
         * @param {HTMLElement} thisParent
         * @private
         */
        function _removeElementErrors(thisParent) {
            var errors = thisParent.querySelectorAll('.' + ERROR_CLASS_NAME), inputsWithErrorClasses =
                thisParent.querySelectorAll('.' + INPUT_ERROR_CLASS);
            for (var elementErrorIndex = 0; elementErrorIndex < errors.length; elementErrorIndex++) {
                errors[elementErrorIndex].parentNode.removeChild(errors[elementErrorIndex]);
            }
            for (var inputErrorIndex = 0; inputErrorIndex < inputsWithErrorClasses.length; inputErrorIndex++) {
                inputsWithErrorClasses[inputErrorIndex].classList.remove(INPUT_ERROR_CLASS);
                if (self.tooltips) {
                    self.tooltips.removeTooltip(inputsWithErrorClasses[inputErrorIndex]);
                }
            }
        }

        /**
         * Registers a custom validator
         * @param {String} name
         * @param {Function} validator a validation function should always return either a Future(true) or Future(false)
         * even when the field has been invalidated with `setCustomValidity`, because of different browser bugs
         * we can't rely on that
         * @returns {window.FlexCss.Form}
         */
        self.registerValidator = function (name, validator) {
            self._validators[name] = validator;
            return self;
        };

        /**
         * Runs async validation
         * @param {String} validationRef
         * @param {HTMLElement} field
         * @returns {*}
         * @private
         */
        function _runValidation(validationRef, field) {
            if (!self._validators[validationRef]) {
                throw 'Could not found validator: ' + validationRef;
            }
            var cl = field.classList, future = self._validators[validationRef].apply(self, [field, form]);
            cl.add(LOADING_CLASS);
            future.done(function () {
                cl.remove(LOADING_CLASS);
            });
            return future;
        }

        /**
         * Run custom validations for elements, validations are done async do support XHR Requests or other stuff
         *
         * @param {Array|NodeList} fields
         * @returns {$.Deferred} contains either true if validations passed or false if something went wrong
         * @private
         */
        function _customValidationsForElements(fields) {
            var futures = [], fieldsLength = fields.length, checkedFields = [];
            for (var iVal = 0; iVal < fieldsLength; iVal++) {
                var field = fields[iVal], validationRef = field.getAttribute('data-validate'), validity = field.validity;
                if (self._validators[validationRef]) {
                    // use local validation first and then continue with custom validations
                    if (!validity.customError && !validity.valid) {
                        continue;
                    }
                    checkedFields.push(field);
                    futures.push(_runValidation(validationRef, field));
                }
            }
            return $.when.apply(self, futures).then(function () {
                var allFutures = arguments, l = allFutures.length;
                var result = {
                    checkedFields: checkedFields,
                    foundAnyError: false
                };

                for (var fI = 0; fI < l; fI++) {
                    if (!allFutures[fI]) {
                        result.foundAnyError = true;
                    }
                }
                return $.Deferred().resolve(result);
            });
        }

        /**
         * Will handle errors for given fields
         * @param {Array|NodeList} fields
         * @param {Boolean} removeAllErrors
         */
        self.prepareErrors = function (fields, removeAllErrors) {
            if (removeAllErrors) {
                _removeElementErrors(form);
            }
            // We save all validations in an extra property because we need to reset the validity due some
            // implementation errors in other browsers then chrome
            for (var i = 0; i < fields.length; i++) {
                var field = fields[i], parent = field.parentNode, validity = field.validity;
                field.flexFormsSavedValidity = JSON.parse(JSON.stringify(validity));
                if (validity && !validity.valid) {
                    if (!removeAllErrors) {
                        // Remove current errors:
                        _removeElementErrors(parent);
                    }
                    // setup custom error messages:
                    _setupErrorMessages(field, validity);
                    field.classList.add('invalid');
                    field.setAttribute(ARIA_INVALID, 'true');
                    if (self.options.appendError) {
                        parent.insertAdjacentHTML("beforeend", '
' +
                        field.validationMessage +
                        "
");
                    }
                    field.flexFormsSavedValidationMessage = field.validationMessage;
                } else {
                    field.classList.remove('invalid');
                    field.setAttribute(ARIA_INVALID, 'false');

                    _removeElementErrors(parent);
                }
                // FIXME: We have to reset the custom validity here to allow native validations work again
                field.setCustomValidity('');
            }
        };

        function validateCustomFields() {
            return _customValidationsForElements(
                form.querySelectorAll("[data-validate]"));
        }

        /**
         * Creates an array from a node list with invalid items
         * On Firefox also Fieldset's seems to be invalid, remove them
         * @param list
         * @returns {Array}
         * @private
         */
        function _createArrayFromInvalidFieldList(list) {
            var arr = [];
            for (var i = 0; i < list.length; ++i) {
                var n = list[i];
                if (!(n instanceof HTMLFieldSetElement)) {
                    arr.push(n);
                }
            }
            return arr;
        }

        /**
         * Formats the error content for the tooltip
         * @param {String} error
         * @returns {String}
         * @private
         */
        function _formatErrorTooltip(error) {
            return ' ' + error;
        }

        /**
         * Creates a tooltip at given element, will create a new instance if not created
         * @param {HTMLElement} target
         * @param {Boolean} [remove]
         * @private
         */
        function _showAndOrCreateTooltip(target, remove) {
            if (!self.tooltips && self.options.createTooltips) {
                self.tooltips = new FlexCss.Tooltip(form, {
                    containerClass: 'error-tooltip'
                });
            }

            setTimeout(function () {
                if (!target.flexFormsSavedValidity) {
                    return;
                }
                if (!target.flexFormsSavedValidity.valid && target.classList.contains(INPUT_ERROR_CLASS)) {
                    self.tooltips.createTooltip(target,
                        _formatErrorTooltip(target.flexFormsSavedValidationMessage), false);
                } else {
                    if (remove) {
                        self.tooltips.removeTooltip(target);
                    }
                }
            }, 0);

        }

        /**
         * Initializes validation for a given form, registers event handlers
         */
        function initFormValidation() {
            // Suppress the default bubbles
            var invalidFormFired = false, currentValidationFuture;
            form.addEventListener("invalid", function (e) {
                e.preventDefault();
                var invalidFields = form.querySelectorAll(":invalid");
                var arr = _createArrayFromInvalidFieldList(invalidFields);
                // Prevent fire this N times:
                if (arr.indexOf(e.target) > 0) {
                    return;
                }
                // focus the first field:
                if (arr.length > 0) {
                    setTimeout(function () {
                        arr[0].focus();
                        _showAndOrCreateTooltip(arr[0]);
                    }, 0);
                }
                currentValidationFuture = $.Deferred();

                var validation = validateCustomFields();
                self.prepareErrors(arr, true);
                validation.done(function (r) {
                    self.prepareErrors(r.checkedFields, false);
                    currentValidationFuture.resolve(r);
                    invalidFormFired = false;
                });

            }, true);

            // handle focus out for text elements
            // Will show an error if field was invalid the first time
            form.addEventListener("blur", function (e) {
                if (self.tooltips) {
                    self.tooltips.removeTooltip(e.target);
                }
                var target = e.target, hasError = false;

                if (!_checkIsValidBlurFocusElement(target)) {
                    return;
                }

                if (target.classList.contains(INPUT_ERROR_CLASS)) {
                    hasError = true;
                }
                _customValidationsForElements([e.target]).done(function () {
                    self.prepareErrors([e.target], false);
                    if (!hasError) {
                        _showAndOrCreateTooltip(e.target);
                    }
                });

            }, true);

            /**
             * Validates if target is a valid input field to check blur and focus events
             * @param {HTMLElement} target
             * @returns {boolean}
             */
            function _checkIsValidBlurFocusElement(target) {
                var attr = target.getAttribute('type');
                return !((attr === 'checkbox' || attr === 'option' || attr === 'submit' ||
                target instanceof HTMLSelectElement || !(target instanceof HTMLInputElement ||
                target instanceof HTMLTextAreaElement)));
            }

            // handle focus on input elements
            // will show an error if field is invalid
            form.addEventListener("focus", function (e) {
                // do not track errors for checkbox and radios on focus:
                if (!_checkIsValidBlurFocusElement(e.target)) {
                    return;
                }
                _showAndOrCreateTooltip(e.target);
            }, true);

            // Handle change for checkbox, radios and selects
            form.addEventListener("change", function (e) {
                var name = e.target.getAttribute('name');
                if (name) {
                    var inputs = form.querySelectorAll('[name="' + name + '"]');
                    _customValidationsForElements(form, inputs).done(function () {
                        self.prepareErrors(inputs, false);
                        _showAndOrCreateTooltip(e.target, true);
                    });
                }
            });

            // prevent default if form is invalid
            var submitListener = function (e) {

                if (form.classList.contains(LOADING_CLASS)) {
                    e.preventDefault();
                    return false;
                }

                form.classList.add(LOADING_CLASS);
                form.removeEventListener("submit", submitListener);
                _removeElementErrors(form);
                e.preventDefault();
                // reset:
                if (form.checkValidity()) {
                    form.addEventListener("submit", submitListener);
                    // Custom validations did never pass
                    currentValidationFuture = $.Deferred();
                    var validation = validateCustomFields();
                    validation.done(function (r) {
                        // focus first invalid field:
                        for (var i = 0; i < r.checkedFields.length; i++) {
                            var f = r.checkedFields[i];
                            if (!f.validity.valid) {
                                f.focus();
                                break;
                            }
                        }
                        self.prepareErrors(r.checkedFields, false);
                        currentValidationFuture.resolve(r);
                    });
                    currentValidationFuture.done(function (r) {
                        form.classList.remove(LOADING_CLASS);
                        if (!r.foundAnyError) {
                            // Handle submitting the form to server:
                            self._handleSubmit(e);
                        }
                    });
                } else {
                    form.classList.remove(LOADING_CLASS);
                    form.addEventListener("submit", submitListener);
                }
            };
            form.addEventListener("submit", submitListener);
        }

        initFormValidation();

        /**
         * Handles submitting, optionally allows to stop submitting
         * @param e
         * @private
         */
        self._handleSubmit = function (e) {
            $(form).trigger('flexcss.form.beforeSubmit', e, [self, form]);
            self.submitFunction.apply(self, [form, e]);
        };

    };

    FlexCss.Form.globalErrorMessageHandler = function () {
    };

    /**
     * Registers a global event Handler
     * @param errorFunc
     */
    FlexCss.Form.registerErrorMessageHandler = function (errorFunc) {
        FlexCss.Form.globalErrorMessageHandler = errorFunc;
    };

    /**
     * Initialize forms for a specific selector
     * @param {String} selector
     * @param {Object} options
     */
    FlexCss.Form.init = function (selector, options) {
        var forms = document.querySelectorAll(selector);
        for (var i = 0; i < forms.length; i++) {
            new FlexCss.Form(forms[i], options);
        }
    };

    FlexCss.Form.globalValidators = [];
    FlexCss.Form.globalRemoteValidationFunction = function () {
    };

    /**
     * Registers a global validator that is usable on all form instasnces
     * @param {String} name
     * @param {Function} validator
     * @returns {Function}
     */
    FlexCss.Form.registerValidator = function (name, validator) {
        FlexCss.Form.globalValidators[name] = validator;
        return FlexCss.Form;
    };

    /**
     * Registers a global function that is called when a form should validate the response of a server
     * @param {Function} func
     * @returns {Function}
     */
    FlexCss.Form.registerGlobalRemoteValidationFunction = function (func) {
        FlexCss.Form.globalRemoteValidationFunction = func;
        return FlexCss.Form;
    };

    // initilize forms for all forms
    FlexCss.Form.init("form", {});

})(document, window, jQuery);<|endoftext|>


<commit_before>/*
 * MediaCrush JavaScript Library
 * https://mediacru.sh
 * https://github.com/MediaCrush/MediaCrush
 * MIT License http://opensource.org/licenses/MIT
 */
window.MediaCrush = (function() {
    var self = this;
    self.version = 1;
    self.domain = 'https://mediacru.sh';
    self.maxMediaWidth = -1;
    self.maxMediaHeight = -1;
    self.preserveAspectRatio = true;

    /*
     * Private methods/properties
     */
    var createRequest = function(method, url) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, self.domain + url);
        xhr.setRequestHeader('X-CORS-Status', 'true');
        return xhr;
    };

    var createMediaObject = function(blob) {
        blob.status = null;
        blob.url = self.domain + '/' + blob.hash;
        blob.update = function(callback) {
            self.checkStatus(blob.hash, function(value, result) {
                if (value == 'done') {
                    for (prop in result[blob.hash]) {
                        blob[prop] = result[blob.hash][prop];
                    }
                }
                blob.status = value;
                if (callback)
                    callback(blob);
            });
        };
        blob.delete = function(callback) {
            self.delete(blob.hash, function(value) {
                if (callback)
                    callback(value);
            });
        };
        blob.wait = function(callback) {
            if (blob.status != 'processing') {
                if (callback)
                    callback(blob);
            } else {
                setTimeout(function() {
                    blob.update(function(a) { a.wait(callback); });
                }, 1000);
            }
        };
        return blob;
    };

    var renderImage = function(target, media, callback) {
        var image = document.createElement('img');
        image.src = self.domain + media.files[0].file;
        if (self.maxMediaWidth != -1) {
            image.style.maxWidth = self.maxMediaWidth;
        }
        if (self.maxMediaHeight != -1) {
            image.style.maxHeight = self.maxMediaHeight;
        }
        target.appendChild(image);
        target.classList.remove('mediacrush');
        target.classList.add('mediacrush-processed');
        if (callback)
            callback(target, media);
    };

    var iframes = {};
    var renderMedia = function(target, media, callback) {
        var iframe = document.createElement('iframe');
        iframe.src = self.domain + '/' + media.hash + '/frame';
        iframe.setAttribute('frameborder', 0);
        iframe.allowFullscreen = true;
        iframes[media.hash] = { frame: iframe, media: media, callback: callback };
        target.appendChild(iframe);
        target.classList.remove('mediacrush');
        target.classList.add('mediacrush-processed');
    };

    window.addEventListener('message', function(e) {
        var frame = iframes[e.data.media];
        if (frame) {
            var width = e.data.width;
            var height = e.data.height;
            if (self.maxMediaWidth != -1) {
                if (width > maxMediaWidth) {
                    var difference = maxMediaWidth / width;
                    width = maxMediaWidth;
                    if (self.preserveAspectRatio) {
                        height = height * difference;
                    }
                }
            }
            if (self.maxMediaHeight != -1) {
                if (height > maxMediaHeight) {
                    var difference = maxMediaHeight / height;
                    height = maxMediaHeight;
                    if (self.preserveAspectRatio) {
                        width = width * difference;
                    }
                }
            }
            frame.frame.width = width;
            frame.frame.height = height;
            if (frame.callback)
                frame.callback(frame.frame.parentElement, frame.media);
        }
    }, false);

    /*
     * Retrieves information for the specified hashes.
     */
    self.get = function(hashes, callback) {
        var xhr;
        if (hashes instanceof Array) {
            xhr = createRequest('GET', '/api/info?list=' + hashes.join(','));
        } else {
            xhr = createRequest('GET', '/api/' + hashes);
        }
        xhr.onload = function() {
            if (callback) {
                var result = JSON.parse(this.responseText);
                if (hashes instanceof Array) {
                    var array = [];
                    var dictionary = {};
                    for (blob in result) {
                        if (blob.length != 12)
                            continue;
                        result[blob].hash = blob;
                        if (result[blob] == null) {
                            array.push(result[blob]);
                            dictionary[blob] = result[blob];
                        } else {
                            var media = createMediaObject(result[blob]);
                            array.push(media);
                            dictionary[blob] = media;
                        }
                    }
                    if (callback)
                        callback(array, dictionary);
                } else {
                    if (callback)
                        callback(createMediaObject(result));
                }
            }
        };
        xhr.send();
    };

    /*
     * Checks if the specified hash exists on MediaCrush.
     */
    self.exists = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/exists');
        xhr.onload = function() {
            if (callback)
                callback(JSON.parse(this.responseText).exists);
        };
        xhr.send();
    };

    /*
     * Deletes the specified media blob from MediaCrush, if the user is allowed to.
     */
    self.delete = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/delete');
        xhr.onload = function() {
            var result = JSON.parse(this.responseText);
            if (callback)
                callback(result['x-status'] == 200);
        };
        xhr.onerror = function() {
            if (callback)
                callback(false);
        };
        xhr.send();
    };

    /*
     * Checks the processing status of the specified hash.
     */
    self.checkStatus = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/status');
        xhr.onload = function() {
            var result = JSON.parse(this.responseText);
            if (callback)
                callback(result['x-status'], result);
        };
        xhr.send();
    };

    /*
     * Uploads a file or URL to MediaCrush.
     */
    self.upload = function(file, callback, onprogress) {
        var xhr;
        var formData = new FormData();
        if (file instanceof File) {
            xhr = createRequest('POST', '/api/upload/file');
            formData.append('file', file);
        } else {
            xhr = createRequest('POST', '/api/upload/url');
            formData.append('url', file);
        }
        xhr.onprogress = onprogress;
        xhr.onload = function() {
            var json = JSON.parse(this.responseText);
            var blob = { hash: json.hash };
            if (json['x-status'] == 409) {
                blob.status = 'done';
            } else {
                blob.status = 'processing';
            }
            if (callback)
                callback(createMediaObject(blob));
        };
        xhr.send(formData);
    };

    /*
     * Translates a .mediacrush div into an embedded MediaCrush object.
     */
    self.render = function(element, callback) {
        var hash = element.getAttribute('data-media');
        self.get(hash, function(media) {
            if (media.type.indexOf('image/') == 0 && media.type != 'image/gif') {
                renderImage(element, media, callback);
            } else {
                renderMedia(element, media, callback);
            }
        });
    };

    self.renderAll = function() {
        var elements = document.querySelectorAll('div.mediacrush');
        var hashes = [];
        for (var i = 0; i < elements.length; i++) {
            var hash = elements[i].getAttribute('data-media');
            if (hash) {
                hashes.push(hash);
            }
        }
        if (hashes.length == 0)
            return;
        self.get(hashes, function(array, result) {
            for (var i = 0; i < elements.length; i++) {
                var hash = elements[i].getAttribute('data-media');
                if (hash && result[hash]) {
                    if (result[hash].type.indexOf('image/') == 0 && result[hash].type != 'image/gif') {
                        renderImage(elements[i], result[hash]);
                    } else {
                        renderMedia(elements[i], result[hash]);
                    }
                }
            }
        });
    };

    return self;
}());

window.addEventListener('load', function() {
    if (window.preventMediaCrushAutoload) {
        return;
    }
    if (window.beforeMediaCrushLoad) {
        window.beforeMediaCrushLoad();
    }
    MediaCrush.renderAll();
}, false);
<commit_msg>Include Media.hash on MediaCrush.get<commit_after>/*
 * MediaCrush JavaScript Library
 * https://mediacru.sh
 * https://github.com/MediaCrush/MediaCrush
 * MIT License http://opensource.org/licenses/MIT
 */
window.MediaCrush = (function() {
    var self = this;
    self.version = 1;
    self.domain = 'https://mediacru.sh';
    self.maxMediaWidth = -1;
    self.maxMediaHeight = -1;
    self.preserveAspectRatio = true;

    /*
     * Private methods/properties
     */
    var createRequest = function(method, url) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, self.domain + url);
        xhr.setRequestHeader('X-CORS-Status', 'true');
        return xhr;
    };

    var createMediaObject = function(blob) {
        blob.status = null;
        blob.url = self.domain + '/' + blob.hash;
        blob.update = function(callback) {
            self.checkStatus(blob.hash, function(value, result) {
                if (value == 'done') {
                    for (prop in result[blob.hash]) {
                        blob[prop] = result[blob.hash][prop];
                    }
                }
                blob.status = value;
                if (callback)
                    callback(blob);
            });
        };
        blob.delete = function(callback) {
            self.delete(blob.hash, function(value) {
                if (callback)
                    callback(value);
            });
        };
        blob.wait = function(callback) {
            if (blob.status != 'processing') {
                if (callback)
                    callback(blob);
            } else {
                setTimeout(function() {
                    blob.update(function(a) { a.wait(callback); });
                }, 1000);
            }
        };
        return blob;
    };

    var renderImage = function(target, media, callback) {
        var image = document.createElement('img');
        image.src = self.domain + media.files[0].file;
        if (self.maxMediaWidth != -1) {
            image.style.maxWidth = self.maxMediaWidth;
        }
        if (self.maxMediaHeight != -1) {
            image.style.maxHeight = self.maxMediaHeight;
        }
        target.appendChild(image);
        target.classList.remove('mediacrush');
        target.classList.add('mediacrush-processed');
        if (callback)
            callback(target, media);
    };

    var iframes = {};
    var renderMedia = function(target, media, callback) {
        var iframe = document.createElement('iframe');
        iframe.src = self.domain + '/' + media.hash + '/frame';
        iframe.setAttribute('frameborder', 0);
        iframe.allowFullscreen = true;
        iframes[media.hash] = { frame: iframe, media: media, callback: callback };
        target.appendChild(iframe);
        target.classList.remove('mediacrush');
        target.classList.add('mediacrush-processed');
    };

    window.addEventListener('message', function(e) {
        var frame = iframes[e.data.media];
        if (frame) {
            var width = e.data.width;
            var height = e.data.height;
            if (self.maxMediaWidth != -1) {
                if (width > maxMediaWidth) {
                    var difference = maxMediaWidth / width;
                    width = maxMediaWidth;
                    if (self.preserveAspectRatio) {
                        height = height * difference;
                    }
                }
            }
            if (self.maxMediaHeight != -1) {
                if (height > maxMediaHeight) {
                    var difference = maxMediaHeight / height;
                    height = maxMediaHeight;
                    if (self.preserveAspectRatio) {
                        width = width * difference;
                    }
                }
            }
            frame.frame.width = width;
            frame.frame.height = height;
            if (frame.callback)
                frame.callback(frame.frame.parentElement, frame.media);
        }
    }, false);

    /*
     * Retrieves information for the specified hashes.
     */
    self.get = function(hashes, callback) {
        var xhr;
        if (hashes instanceof Array) {
            xhr = createRequest('GET', '/api/info?list=' + hashes.join(','));
        } else {
            xhr = createRequest('GET', '/api/' + hashes);
        }
        xhr.onload = function() {
            if (callback) {
                var result = JSON.parse(this.responseText);
                if (hashes instanceof Array) {
                    var array = [];
                    var dictionary = {};
                    for (blob in result) {
                        if (blob.length != 12)
                            continue;
                        result[blob].hash = blob;
                        if (result[blob] == null) {
                            array.push(result[blob]);
                            dictionary[blob] = result[blob];
                        } else {
                            var media = createMediaObject(result[blob]);
                            media.hash = blob;
                            array.push(media);
                            dictionary[blob] = media;
                        }
                    }
                    if (callback)
                        callback(array, dictionary);
                } else {
                    if (callback)
                        callback(createMediaObject(result));
                }
            }
        };
        xhr.send();
    };

    /*
     * Checks if the specified hash exists on MediaCrush.
     */
    self.exists = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/exists');
        xhr.onload = function() {
            if (callback)
                callback(JSON.parse(this.responseText).exists);
        };
        xhr.send();
    };

    /*
     * Deletes the specified media blob from MediaCrush, if the user is allowed to.
     */
    self.delete = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/delete');
        xhr.onload = function() {
            var result = JSON.parse(this.responseText);
            if (callback)
                callback(result['x-status'] == 200);
        };
        xhr.onerror = function() {
            if (callback)
                callback(false);
        };
        xhr.send();
    };

    /*
     * Checks the processing status of the specified hash.
     */
    self.checkStatus = function(hash, callback) {
        var xhr = createRequest('GET', '/api/' + hash + '/status');
        xhr.onload = function() {
            var result = JSON.parse(this.responseText);
            if (callback)
                callback(result['x-status'], result);
        };
        xhr.send();
    };

    /*
     * Uploads a file or URL to MediaCrush.
     */
    self.upload = function(file, callback, onprogress) {
        var xhr;
        var formData = new FormData();
        if (file instanceof File) {
            xhr = createRequest('POST', '/api/upload/file');
            formData.append('file', file);
        } else {
            xhr = createRequest('POST', '/api/upload/url');
            formData.append('url', file);
        }
        xhr.onprogress = onprogress;
        xhr.onload = function() {
            var json = JSON.parse(this.responseText);
            var blob = { hash: json.hash };
            if (json['x-status'] == 409) {
                blob.status = 'done';
            } else {
                blob.status = 'processing';
            }
            if (callback)
                callback(createMediaObject(blob));
        };
        xhr.send(formData);
    };

    /*
     * Translates a .mediacrush div into an embedded MediaCrush object.
     */
    self.render = function(element, callback) {
        var hash = element.getAttribute('data-media');
        self.get(hash, function(media) {
            if (media.type.indexOf('image/') == 0 && media.type != 'image/gif') {
                renderImage(element, media, callback);
            } else {
                renderMedia(element, media, callback);
            }
        });
    };

    self.renderAll = function() {
        var elements = document.querySelectorAll('div.mediacrush');
        var hashes = [];
        for (var i = 0; i < elements.length; i++) {
            var hash = elements[i].getAttribute('data-media');
            if (hash) {
                hashes.push(hash);
            }
        }
        if (hashes.length == 0)
            return;
        self.get(hashes, function(array, result) {
            for (var i = 0; i < elements.length; i++) {
                var hash = elements[i].getAttribute('data-media');
                if (hash && result[hash]) {
                    if (result[hash].type.indexOf('image/') == 0 && result[hash].type != 'image/gif') {
                        renderImage(elements[i], result[hash]);
                    } else {
                        renderMedia(elements[i], result[hash]);
                    }
                }
            }
        });
    };

    return self;
}());

window.addEventListener('load', function() {
    if (window.preventMediaCrushAutoload) {
        return;
    }
    if (window.beforeMediaCrushLoad) {
        window.beforeMediaCrushLoad();
    }
    MediaCrush.renderAll();
}, false);
<|endoftext|>


<commit_before>const fs = require('fs');
const http = require('http');
const shell = require('shelljs');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const env = require('./env.js');
const { exec } = require('./helper.js');

function writeFile(url, destination, resolve, reject) {
    const file = fs.createWriteStream(destination);

    const req = http.get(url, (res) => {
        res.pipe(file);
    });

    req.on('error', (e) => {
        file.unlink(destination);
        reject(e);
    });

    file.on('error', () => {
        file.unlink(destination);
        reject(new Error('File error'));
    });

    file.on('finish', resolve);
}

function download(url, destination) {
    if (fs.existsSync(destination)) {
        shell.echo(`${destination} already exists. Skipping download.`);

        return Promise.resolve();
    }

    const executor = writeFile.bind(null, url, destination);
    return new Promise(executor);
}

function validateFile(fileName) {
    let res;

    if (env.isWindows) {
        const output = exec(`certutil -hashfile ${fileName} sha1`).split('\r\n');

        const arr = output[0].replace(':', '').split(' ');
        const name = arr.find(n => n === fileName);

        const sha1 = output[1];
        res = `${sha1}  ${name}`;
    } else {
        res = exec(`shasum -a 1 ${fileName}`).trim();
    }

    if (res !== env.ZK_VERSION_SHA1) {
        throw new Error(`Wrong sha1 for ${fileName}! Expected "${env.ZK_VERSION_SHA1}", got "${res}".`);
    }
}

function patch() {
    if (env.isWindows) {
        const destination = `${env.ZK_DEPS}/src/c/src`;
        shell.cp(`${env.ROOT}/patches/windows/zookeeper.c`, `${destination}/zookeeper.c`);
        shell.cp(`${env.ROOT}/patches/windows/zk_log.c`, `${destination}/zk_log.c`);
        shell.cp(`${env.ROOT}/patches/windows/zk_adaptor.c`, `${destination}/zk_adaptor.c`);
        return;
    }

    exec(`patch -p0 < ${env.ROOT}/patches/ZOOKEEPER-642.patch`);
}

if (env.isAlreadyBuilt) {
    shell.echo('Zookeeper has already been built');
    shell.exit(0);
    return;
}

shell.cd(env.DEPS);

download(env.ZK_URL, env.ZK_FILE)
    .then(() => {
        validateFile(env.ZK_FILE);

        shell.rm('-rf', env.ZK_DEPS);

        decompress(env.ZK_FILE, './', {
            plugins: [
                decompressTargz()
            ]
        }).then(() => {
            shell.mv(env.ZK, env.ZK_DEPS);
            patch();
        }).catch((e) => {
            shell.echo(`Error: ${e.message}`);
            shell.exit(1);
        });

    })
    .catch((e) => {
        shell.echo(`Unable to download zookeeper library. Error: ${e.message}`);
        shell.exit(1);
    });
<commit_msg>fix: typo<commit_after>const fs = require('fs');
const http = require('http');
const shell = require('shelljs');
const decompress = require('decompress');
const decompressTargz = require('decompress-targz');
const env = require('./env.js');
const { exec } = require('./helper.js');

function writeFile(url, destination, resolve, reject) {
    const file = fs.createWriteStream(destination);

    const req = http.get(url, (res) => {
        res.pipe(file);
    });

    req.on('error', (e) => {
        file.unlink(destination);
        reject(e);
    });

    file.on('error', () => {
        file.unlink(destination);
        reject(new Error('File error'));
    });

    file.on('finish', resolve);
}

function download(url, destination) {
    if (fs.existsSync(destination)) {
        shell.echo(`${destination} already exists. Skipping download.`);

        return Promise.resolve();
    }

    const executor = writeFile.bind(null, url, destination);
    return new Promise(executor);
}

function validateFile(fileName) {
    let res;

    if (env.isWindows) {
        const output = exec(`certutil -hashfile ${fileName} sha1`).split('\r\n');

        const arr = output[0].replace(':', '').split(' ');
        const name = arr.find(n => n === fileName);

        const sha1 = output[1];
        res = `${sha1}  ${name}`;
    } else {
        res = exec(`shasum -a 1 ${fileName}`).trim();
    }

    if (res !== env.ZK_VERSION_SHA1) {
        throw new Error(`Wrong sha1 for ${fileName}! Expected "${env.ZK_VERSION_SHA1}", got "${res}".`);
    }
}

function patch() {
    if (env.isWindows) {
        const destination = `${env.ZK_DEPS}/src/c/src`;
        shell.cp(`${env.ROOT}/patches/windows/zookeeper.c`, `${destination}/zookeeper.c`);
        shell.cp(`${env.ROOT}/patches/windows/zk_log.c`, `${destination}/zk_log.c`);
        shell.cp(`${env.ROOT}/patches/windows/zk_adaptor.h`, `${destination}/zk_adaptor.h`);
        return;
    }

    exec(`patch -p0 < ${env.ROOT}/patches/ZOOKEEPER-642.patch`);
}

if (env.isAlreadyBuilt) {
    shell.echo('Zookeeper has already been built');
    shell.exit(0);
    return;
}

shell.cd(env.DEPS);

download(env.ZK_URL, env.ZK_FILE)
    .then(() => {
        validateFile(env.ZK_FILE);

        shell.rm('-rf', env.ZK_DEPS);

        decompress(env.ZK_FILE, './', {
            plugins: [
                decompressTargz()
            ]
        }).then(() => {
            shell.mv(env.ZK, env.ZK_DEPS);
            patch();
        }).catch((e) => {
            shell.echo(`Error: ${e.message}`);
            shell.exit(1);
        });

    })
    .catch((e) => {
        shell.echo(`Unable to download zookeeper library. Error: ${e.message}`);
        shell.exit(1);
    });
<|endoftext|>