<commit_before>var _ = require('highland');

var pg = require('pg');

var ls = require('./lightstreamer');

var utils = require('./utils');

var notify = require('./notification');

// Postgres confg
var psql = process.env.DATABASE_URL;

if (utils.isReadOnly()) {
  console.log('Running in read-only mode.');

} else {
  console.log('Running in READ-WRITE mode.');
}

console.log('Connecting to', psql);

function selectMostRecentByIdx(idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select * from telemetry where idx = $1 and ts = (select max(ts) from telemetry where idx = $1) order by status desc',
      [idx],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

function selectMostRecentByIdxIntervalAgoCount(idx, intervalAgo, count, cb) {

  pg.connect(psql, function (err, client, done) {

    var now = Date.now() / 1000 | 0;

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    count = count || 1;
    if (count < 1) {

      count = 1;
      intervalAgo = now;

    } else {

      intervalAgo = intervalAgo || now;

      if (intervalAgo < 1) {

        intervalAgo = now;
      }
    }

    client.query('with top_x as (select * ' +
      'from telemetry where idx = $1 and ts >= to_timestamp($2) ' +
      'order by ts desc, status desc limit $3) select * from top_x order by ts asc',
      [idx, now - intervalAgo, count],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

function selectStatsByIdx(idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select avg(lag_avg) as lag_avg,' +
      ' avg(lag_stddev) as lag_stddev, avg(value_avg) as val_avg,' +
      ' avg(value_stddev) as val_sd from telemetry_session_stats where idx = $1',
      [idx],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

var previousSessionId = 0;

ls.dataStream.fork().each(function (data) {

  if (!utils.isReadOnly()) {

    pg.connect(psql, function (err, client, done) {

      if (err) {

        notify.error(err);

        return console.error('Error requesting postgres client', err);
      }

      client.query('insert into telemetry ' +
        '(idx, value, value_calibrated, ts, status, session_id) ' +
        'values ($1, $2, $3, to_timestamp($4), $5, $6)',
        [data.k, data.v, data.cv, data.t, data.s, data.sid],

        function (err2) {
          // suppress logging unique constraint violations
          // this can happen during normal ops
          // this occurs during a lightstreamer re-connect typically
          if (err2 && err2.code !== '23505') {

            done();

            notify.error(err2);

            return console.error('Error inserting data', err2);
          }

          if (previousSessionId !== data.sid) {

            client.query('insert into session (session_id) values ($1)',
            [data.sid],

            function (err3) {

              done();

              previousSessionId = data.sid;

              if (err3 && err3.code !== '23505') {

                notify.error(err3);

                return console.error('Error inserting data', err3);
              }

            });

          } else {

            done();
          }

        });
    });
  }
});

var previousIdxTimeHash = {};

exports.addCurrentStats = function (data, next) {

  selectStatsByIdx(data.k, function (err, res) {
    var lag_avg = 0,
    lag_stddev = 0,
    val_avg = 0,
    val_stddev = 0,
    previousTime = previousIdxTimeHash[data.k],
    lag_sd = 0,
    val_sd = 0;

    if (err) {
      // keep chugging along, even if there was an error
      data.m = 0;
      data.d = 0;
      data.vm = 0;
      data.vd = 0;

      next(null, [data]);
      return;
    }

    if (res.rows.length > 0 && previousTime) {

      lag_avg = res.rows[0].lag_avg;
      lag_stddev = res.rows[0].lag_stddev;
      val_avg = res.rows[0].val_avg;
      val_stddev = res.rows[0].val_sd;
      lag_sd = utils.calcStandardDeviationDistance(data.t - previousTime, lag_avg, lag_stddev);
      val_sd = utils.calcStandardDeviationDistance(data.v, val_avg, val_stddev);
    }

    previousIdxTimeHash[data.k] = data.t;

    data.m = lag_avg || 0;
    // check for Infinity (div by zero)
    data.d = lag_sd === Infinity ? 0 : lag_sd;

    data.vm = val_avg;
    data.vd = val_sd === Infinity ? 0 : val_sd;

    next(null, [data]);
  });
};

exports.getTelemetryData = function (idx) {

  return _.wrapCallback(function (params, next) {

    // unixtime of -1 indicates the client wants the latest record available
    if (params.count === -1) {

      selectMostRecentByIdx(idx, function (err, res) {

        if (err) {

          next(err);
          return;
        }

        next(null, res.rows);
      });

    } else {
      // get a list of records later than intervalAgo of a idx
      selectMostRecentByIdxIntervalAgoCount(idx, params.intervalAgo, params.count,
        function (err, res) {

          if (err) {

            next(err);
            return;
          }

          // if no records found, get the latest
          if (res.rows.length === 0) {

            selectMostRecentByIdx(idx, function (err2, res2) {

              if (err2) {

                next(err2);
                return;
              }

              next(null, res2.rows);
            });

          } else {

            next(null, res.rows);
          }
        });
    }
  });
};

exports.addStats = function (idx) {

  return _.wrapCallback(function (rows, next) {

    var data;
    var interval = null;

    data = rows.map(function (r) {
      return {
        k: idx,
        v: r.value,
        t: r.ts.getTime() / 1000 | 0,
        s: r.status,
        m: 0,
        d: 0,
        vm: 0,
        vd: 0
      };
    });

    if (data.length >= 2) {

      interval = data[data.length - 1].t - data[data.length - 2].t;
    }

    selectStatsByIdx(idx, function (err, res) {
      var lag_sd, val_sd;

      if (err) {

        next(err);
        return;
      }

      if (interval !== null && res.rows.length > 0) {

        lag_sd = utils.calcStandardDeviationDistance(
          interval, res.rows[0].lag_avg, res.rows[0].lag_stddev);

        data.forEach(function (d) {
          d.d = lag_sd || 0;
          d.m = res.rows[0].lag_avg || 0;
        });

      }

      val_sd = utils.calcStandardDeviationDistance(
        data[0].v, res.rows[0].val_avg, res.rows[0].val_sd);

      data.forEach(function (d) {
        d.vd = val_sd;
        d.vm = res.rows[0].val_avg;
      });

      next(null, data);
    });
  });
};

exports.selectStatsByIdx = selectStatsByIdx;

function getTelemetrySessionStatsGaps(cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select * from get_telemetry_session_stats_gaps()',
      [],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

exports.getTelemetrySessionStatsGaps = getTelemetrySessionStatsGaps;

function getTelemetrySessionStatsBySessionIdIdx(sessionId, idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select * from get_telemetry_session_stats($1, $2)',
      [sessionId, idx],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

exports.getTelemetrySessionStatsBySessionIdIdx = getTelemetrySessionStatsBySessionIdIdx;

function saveTelemetrySessionStatsBySessionIdIdx(
    session_id,
    idx,
    value_count,
    value_min,
    value_max,
    value_avg,
    value_stddev,
    lag_min,
    lag_max,
    lag_avg,
    lag_stddev,
    ts_min,
    ts_max,
    cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('insert into telemetry_session_stats(' +
    'idx, session_id, value_count, value_min, value_max,' +
    'value_avg, value_stddev, lag_min, lag_max, lag_avg,' +
    'lag_stddev, ts_min, ts_max) values ($1, $2, $3, $4,' +
    '$5, $6, $7, $8, $9, $10, $11, $12, $13)',
    [
      idx,
      session_id,
      value_count,
      value_min,
      value_max,
      value_avg,
      value_stddev,
      lag_min,
      lag_max,
      lag_avg,
      lag_stddev,
      ts_min,
      ts_max
    ],
    function (err2, res) {

      done();
      cb(err2, res);
    });
  });
}

exports.saveTelemetrySessionStatsBySessionIdIdx = saveTelemetrySessionStatsBySessionIdIdx;
<commit_msg>see if moving query out of function helps with perf<commit_after>var _ = require('highland');

var pg = require('pg');

var ls = require('./lightstreamer');

var utils = require('./utils');

var notify = require('./notification');

// Postgres confg
var psql = process.env.DATABASE_URL;

if (utils.isReadOnly()) {
  console.log('Running in read-only mode.');

} else {
  console.log('Running in READ-WRITE mode.');
}

console.log('Connecting to', psql);

function selectMostRecentByIdx(idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select * from telemetry where idx = $1 and ts = (select max(ts) from telemetry where idx = $1) order by status desc',
      [idx],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

function selectMostRecentByIdxIntervalAgoCount(idx, intervalAgo, count, cb) {

  pg.connect(psql, function (err, client, done) {

    var now = Date.now() / 1000 | 0;

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    count = count || 1;
    if (count < 1) {

      count = 1;
      intervalAgo = now;

    } else {

      intervalAgo = intervalAgo || now;

      if (intervalAgo < 1) {

        intervalAgo = now;
      }
    }

    client.query('with top_x as (select * ' +
      'from telemetry where idx = $1 and ts >= to_timestamp($2) ' +
      'order by ts desc, status desc limit $3) select * from top_x order by ts asc',
      [idx, now - intervalAgo, count],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

function selectStatsByIdx(idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select avg(lag_avg) as lag_avg,' +
      ' avg(lag_stddev) as lag_stddev, avg(value_avg) as val_avg,' +
      ' avg(value_stddev) as val_sd from telemetry_session_stats where idx = $1',
      [idx],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

var previousSessionId = 0;

ls.dataStream.fork().each(function (data) {

  if (!utils.isReadOnly()) {

    pg.connect(psql, function (err, client, done) {

      if (err) {

        notify.error(err);

        return console.error('Error requesting postgres client', err);
      }

      client.query('insert into telemetry ' +
        '(idx, value, value_calibrated, ts, status, session_id) ' +
        'values ($1, $2, $3, to_timestamp($4), $5, $6)',
        [data.k, data.v, data.cv, data.t, data.s, data.sid],

        function (err2) {
          // suppress logging unique constraint violations
          // this can happen during normal ops
          // this occurs during a lightstreamer re-connect typically
          if (err2 && err2.code !== '23505') {

            done();

            notify.error(err2);

            return console.error('Error inserting data', err2);
          }

          if (previousSessionId !== data.sid) {

            client.query('insert into session (session_id) values ($1)',
            [data.sid],

            function (err3) {

              done();

              previousSessionId = data.sid;

              if (err3 && err3.code !== '23505') {

                notify.error(err3);

                return console.error('Error inserting data', err3);
              }

            });

          } else {

            done();
          }

        });
    });
  }
});

var previousIdxTimeHash = {};

exports.addCurrentStats = function (data, next) {

  selectStatsByIdx(data.k, function (err, res) {
    var lag_avg = 0,
    lag_stddev = 0,
    val_avg = 0,
    val_stddev = 0,
    previousTime = previousIdxTimeHash[data.k],
    lag_sd = 0,
    val_sd = 0;

    if (err) {
      // keep chugging along, even if there was an error
      data.m = 0;
      data.d = 0;
      data.vm = 0;
      data.vd = 0;

      next(null, [data]);
      return;
    }

    if (res.rows.length > 0 && previousTime) {

      lag_avg = res.rows[0].lag_avg;
      lag_stddev = res.rows[0].lag_stddev;
      val_avg = res.rows[0].val_avg;
      val_stddev = res.rows[0].val_sd;
      lag_sd = utils.calcStandardDeviationDistance(data.t - previousTime, lag_avg, lag_stddev);
      val_sd = utils.calcStandardDeviationDistance(data.v, val_avg, val_stddev);
    }

    previousIdxTimeHash[data.k] = data.t;

    data.m = lag_avg || 0;
    // check for Infinity (div by zero)
    data.d = lag_sd === Infinity ? 0 : lag_sd;

    data.vm = val_avg;
    data.vd = val_sd === Infinity ? 0 : val_sd;

    next(null, [data]);
  });
};

exports.getTelemetryData = function (idx) {

  return _.wrapCallback(function (params, next) {

    // unixtime of -1 indicates the client wants the latest record available
    if (params.count === -1) {

      selectMostRecentByIdx(idx, function (err, res) {

        if (err) {

          next(err);
          return;
        }

        next(null, res.rows);
      });

    } else {
      // get a list of records later than intervalAgo of a idx
      selectMostRecentByIdxIntervalAgoCount(idx, params.intervalAgo, params.count,
        function (err, res) {

          if (err) {

            next(err);
            return;
          }

          // if no records found, get the latest
          if (res.rows.length === 0) {

            selectMostRecentByIdx(idx, function (err2, res2) {

              if (err2) {

                next(err2);
                return;
              }

              next(null, res2.rows);
            });

          } else {

            next(null, res.rows);
          }
        });
    }
  });
};

exports.addStats = function (idx) {

  return _.wrapCallback(function (rows, next) {

    var data;
    var interval = null;

    data = rows.map(function (r) {
      return {
        k: idx,
        v: r.value,
        t: r.ts.getTime() / 1000 | 0,
        s: r.status,
        m: 0,
        d: 0,
        vm: 0,
        vd: 0
      };
    });

    if (data.length >= 2) {

      interval = data[data.length - 1].t - data[data.length - 2].t;
    }

    selectStatsByIdx(idx, function (err, res) {
      var lag_sd, val_sd;

      if (err) {

        next(err);
        return;
      }

      if (interval !== null && res.rows.length > 0) {

        lag_sd = utils.calcStandardDeviationDistance(
          interval, res.rows[0].lag_avg, res.rows[0].lag_stddev);

        data.forEach(function (d) {
          d.d = lag_sd || 0;
          d.m = res.rows[0].lag_avg || 0;
        });

      }

      val_sd = utils.calcStandardDeviationDistance(
        data[0].v, res.rows[0].val_avg, res.rows[0].val_sd);

      data.forEach(function (d) {
        d.vd = val_sd;
        d.vm = res.rows[0].val_avg;
      });

      next(null, data);
    });
  });
};

exports.selectStatsByIdx = selectStatsByIdx;

function getTelemetrySessionStatsGaps(cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('select * from get_telemetry_session_stats_gaps()',
      [],
      function (err2, res) {

        done();
        cb(err2, res);
      });
  });
}

exports.getTelemetrySessionStatsGaps = getTelemetrySessionStatsGaps;

// function getTelemetrySessionStatsBySessionIdIdx(sessionId, idx, cb) {
//
//   pg.connect(psql, function (err, client, done) {
//
//     if (err) {
//
//       console.error('Error requesting postgres client', err);
//
//       notify.error(err);
//
//       return cb(err);
//     }
//
//     client.query('select * from get_telemetry_session_stats($1, $2)',
//       [sessionId, idx],
//       function (err2, res) {
//
//         done();
//         cb(err2, res);
//       });
//   });
// }

var telemetrySessionStatsQuery =
    'with ' +
    'time_series as ( ' +
      'select sec ' +
      'from generate_series( ' +
        '(select min(ts) from telemetry where session_id = $1 and idx = $2 and status = 24), ' +
        '(select max(ts) from telemetry where session_id = $1 and idx = $2 and status = 24), ' +
        '\'1 sec\' ' +
      ') as sec ' +
    '), ' +
    'telemetry_change_values as ( ' +
      'select ' +
        'date_trunc(\'sec\', ts) as sec, ' +
        'value ' +
      'from telemetry ' +
      'where session_id = $1 and idx = $2 and status = 24 ' +
    '), ' +
    'telemetry_filled_values as ( ' +
      'select ' +
        'time_series.sec as sec, ' +
        '(select telemetry_change_values.value ' +
         'from telemetry_change_values ' +
         'where telemetry_change_values.sec <= time_series.sec ' +
         'order by telemetry_change_values.sec DESC ' +
         'limit 1) ' +
      'from ' +
        'time_series left outer join telemetry_change_values ' +
          'on telemetry_change_values.sec = time_series.sec ' +
        'order by time_series.sec ' +
    '), ' +
    'telemetry_session_value_stats as ( ' +
      'select ' +
        '$2 as idx, ' +
        'min(value) as value_min, ' +
        'max(value) as value_max, ' +
        'avg(value) as value_avg, ' +
        'stddev_samp(value) as value_stddev ' +
      'from telemetry_filled_values ' +
    '), ' +
    'telemetry_session_lag_stats as ( ' +
      'select ' +
        '$2 as idx, ' +
        'count(*) as value_count, ' +
        'min(lag) as lag_min, ' +
        'max(lag) as lag_max, ' +
        'avg(lag) as lag_avg, ' +
        'stddev_samp(lag) as lag_stddev, ' +
        'min(ts) as ts_min, ' +
        'max(ts) as ts_max ' +
      'from ( ' +
        'select ' +
          'extract(epoch from (ts - lag(ts) over (partition by idx, session_id order by idx, session_id, ts))) as lag, ' +
          'ts ' +
        'from telemetry ' +
          'where session_id = $1 and idx = $2 and status = 24 ' +
      ') as temp_lag ' +
    ') ' +
    'select ' +
    '$1 as session_id, ' +
    '$2 as idx, ' +
    'coalesce(l.value_count, 0) as value_count, ' +
    'v.value_min as value_min, ' +
    'v.value_max as value_min, ' +
    'v.value_avg as value_avg, ' +
    'v.value_stddev as value_stddev, ' +
    'l.lag_min as lag_min, ' +
    'l.lag_max as lag_max, ' +
    'l.lag_avg as lag_avg, ' +
    'l.lag_stddev as lag_stddev, ' +
    'l.ts_min as ts_min, ' +
    'l.ts_max as ts_max ' +
    'from telemetry_session_value_stats v inner join telemetry_session_lag_stats l ' +
  'on v.idx = l.idx';

function getTelemetrySessionStatsBySessionIdIdx(sessionId, idx, cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query(telemetrySessionStatsQuery,
      [sessionId, idx],
      function (err2, res) {
        console.log(res.rows[0]);
        done();
        cb(err2, res);
      });
  });
}

exports.getTelemetrySessionStatsBySessionIdIdx = getTelemetrySessionStatsBySessionIdIdx;

function saveTelemetrySessionStatsBySessionIdIdx(
    session_id,
    idx,
    value_count,
    value_min,
    value_max,
    value_avg,
    value_stddev,
    lag_min,
    lag_max,
    lag_avg,
    lag_stddev,
    ts_min,
    ts_max,
    cb) {

  pg.connect(psql, function (err, client, done) {

    if (err) {

      console.error('Error requesting postgres client', err);

      notify.error(err);

      return cb(err);
    }

    client.query('insert into telemetry_session_stats(' +
    'idx, session_id, value_count, value_min, value_max,' +
    'value_avg, value_stddev, lag_min, lag_max, lag_avg,' +
    'lag_stddev, ts_min, ts_max) values ($1, $2, $3, $4,' +
    '$5, $6, $7, $8, $9, $10, $11, $12, $13)',
    [
      idx,
      session_id,
      value_count,
      value_min,
      value_max,
      value_avg,
      value_stddev,
      lag_min,
      lag_max,
      lag_avg,
      lag_stddev,
      ts_min,
      ts_max
    ],
    function (err2, res) {

      done();
      cb(err2, res);
    });
  });
}

exports.saveTelemetrySessionStatsBySessionIdIdx = saveTelemetrySessionStatsBySessionIdIdx;
<|endoftext|>


<commit_before>//var OSMS = require('openvox-sms');
var when = require('when');

var openvoxWrapper = function (osms) {

	this.connect = function () {
		var defer = when.defer();
		osms.on('connect', function (event) {
			defer.resolve(event);
		});
		return defer.promise;
	};

	this.isConnected = function () {
		var defer = when.defer();
		if (osms.isConnected()) {
			defer.resolve();
		} else {
			defer.reject();
		}
		return defer.promise;
	};

	this.keepConnected = function () {
		return when.resolve(osms.keepConnected());
	};

	this.sendSMS = function (object) {
		var defer = when.defer();
		osms.sendSMS(object, function (err, response) {
			if (err) {
				defer.reject(err);
			} else {
				defer.resolve(response);
			}
		});
		return defer.promise;
	};

	this.close = function () {
		return when.resolve(osms.close());
	};
};

module.exports = openvoxWrapper;<commit_msg>add error description<commit_after>//var OSMS = require('openvox-sms');
var when = require('when');

var openvoxWrapper = function (osms) {

	this.connect = function () {
		var defer = when.defer();
		osms.on('connect', function (event) {
			defer.resolve(event);
		});
		return defer.promise;
	};

	this.isConnected = function () {
		var defer = when.defer();
		if (osms.isConnected()) {
			defer.resolve('Connected');
		} else {
			defer.reject(new Error('openvox is not connected'));
		}
		return defer.promise;
	};

	this.keepConnected = function () {
		return when.resolve(osms.keepConnected());
	};

	this.sendSMS = function (object) {
		var defer = when.defer();
		osms.sendSMS(object, function (err, response) {
			if (err) {
				defer.reject(err);
			} else {
				defer.resolve(response);
			}
		});
		return defer.promise;
	};

	this.close = function () {
		return when.resolve(osms.close());
	};
};

module.exports = openvoxWrapper;<|endoftext|>


<commit_before>dojo.provide("dijit._base.popup");

dojo.require("dijit._base.focus");
dojo.require("dijit._base.place");
dojo.require("dijit._base.window");

dijit.popup = new function(){
	// summary:
	//		This class is used to show/hide widgets as popups.

	var stack = [],
		beginZIndex=1000,
		idGen = 1;

	this.prepare = function(/*DomNode*/ node){
		// summary:
		//		Prepares a node to be used as a popup
		//
		// description:
		//		Attaches node to dojo.doc.body, and
		//		positions it off screen, but not display:none, so that
		//		the widget doesn't appear in the page flow and/or cause a blank
		//		area at the bottom of the viewport (making scrollbar longer), but
		//		initialization of contained widgets works correctly

		var s = node.style;
		s.visibility = "hidden";	// so TAB key doesn't navigate to hidden popup
		s.position = "absolute";
		s.top = "-9999px";
		if(s.display == "none"){
			s.display="";
		}
		dojo.body().appendChild(node);
	};

/*=====
dijit.popup.__OpenArgs = function(){
	// popup: Widget
	//		widget to display
	// parent: Widget
	//		the button etc. that is displaying this popup
	// around: DomNode
	//		DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
	// x: Integer
	//		Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
	// y: Integer
	//		Absolute vertical position (in pixels) to place node at.  (Specity this *or* "around" parameter.)
	// orient: Object || String
	//		When the around parameter is specified, orient should be an
	//		ordered list of tuples of the form (around-node-corner, popup-node-corner).
	//		dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
	//		until the popup appears fully within the viewport.
	//
	//		The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
	//			1. (BL, TL)
	//			2. (TL, BL)
	//		where BL means "bottom left" and "TL" means "top left".
	//		So by default, it first tries putting the popup below the around node, left-aligning them,
	//		and then tries to put it above the around node, still left-aligning them.   Note that the
	//		default is horizontally reversed when in RTL mode.
	//
	//		When an (x,y) position is specified rather than an around node, orient is either
	//		"R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
	//		specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
	//		fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
	//		and the top-right corner.
	// onCancel: Function
	//		callback when user has canceled the popup by
	//			1. hitting ESC or
	//			2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
	//			   i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
	// onClose: Function
	//		callback whenever this popup is closed
	// onExecute: Function
	//		callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
	// padding: dijit.__Position
	//		adding a buffer around the opening position. This is only useful when around is not set.
	this.popup = popup;
	this.parent = parent;
	this.around = around;
	this.x = x;
	this.y = y;
	this.orient = orient;
	this.onCancel = onCancel;
	this.onClose = onClose;
	this.onExecute = onExecute;
	this.padding = padding;
}
=====*/
	this.open = function(/*dijit.popup.__OpenArgs*/ args){
		// summary:
		//		Popup the widget at the specified position
		//
		// example:
		//		opening at the mouse position
		//		|		dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
		//
		// example:
		//		opening the widget as a dropdown
		//		|		dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
		//
		//		Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
		//		(fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.

		var widget = args.popup,
			orient = args.orient || (
				dojo._isBodyLtr() ?
				{'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
				{'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
			),
			around = args.around,
			id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+idGen++);

		// make wrapper div to hold widget and possibly hold iframe behind it.
		// we can't attach the iframe as a child of the widget.domNode because
		// widget.domNode might be a <table>, <ul>, etc.
		var wrapper = dojo.create("div",{
			id: id,
			"class":"dijitPopup",
			style:{
				zIndex: beginZIndex + stack.length,
				visibility:"hidden",
				left: "0px", top: "0px"		// prevent transient scrollbar causing misalign (#5776)
			},
			dijitPopupParent: args.parent ? args.parent.id : ""
		}, dojo.body());
		dijit.setWaiRole(wrapper, "presentation");

		var s = widget.domNode.style;
		s.display = "";
		s.visibility = "";
		s.position = "";
		s.top = "0px";
		wrapper.appendChild(widget.domNode);

		var iframe = new dijit.BackgroundIframe(wrapper);

		// position the wrapper node
		var best = around ?
			dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
			dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);

		wrapper.style.visibility = "visible";
		// TODO: use effects to fade in wrapper

		var handlers = [];

		// Compute the closest ancestor popup that's *not* a child of another popup.
		// Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
		var getTopPopup = function(){
			for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
				/* do nothing, just trying to get right value for pi */
			}
			return stack[pi];
		}

		// provide default escape and tab key handling
		// (this will work for any widget, not just menu)
		handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
			if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
				dojo.stopEvent(evt);
				args.onCancel();
			}else if(evt.charOrCode === dojo.keys.TAB){
				dojo.stopEvent(evt);
				var topPopup = getTopPopup();
				if(topPopup && topPopup.onCancel){
					topPopup.onCancel();
				}
			}
		}));

		// watch for cancel/execute events on the popup and notify the caller
		// (for a menu, "execute" means clicking an item)
		if(widget.onCancel){
			handlers.push(dojo.connect(widget, "onCancel", null, args.onCancel));
		}

		handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", null, function(){
			var topPopup = getTopPopup();
			if(topPopup && topPopup.onExecute){
				topPopup.onExecute();
			}
		}));

		stack.push({
			wrapper: wrapper,
			iframe: iframe,
			widget: widget,
			parent: args.parent,
			onExecute: args.onExecute,
			onCancel: args.onCancel,
 			onClose: args.onClose,
			handlers: handlers
		});

		if(widget.onOpen){
			// TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
			widget.onOpen(best);
		}

		return best;
	};

	this.close = function(/*dijit._Widget*/ popup){
		// summary:
		//		Close specified popup and any popups that it parented
		while(dojo.some(stack, function(elem){return elem.widget == popup;})){
			var top = stack.pop(),
				wrapper = top.wrapper,
				iframe = top.iframe,
				widget = top.widget,
				onClose = top.onClose;

			if(widget.onClose){
				// TODO: in 2.0 standardize onHide() (used by StackContainer) and onHide() (used here)
				widget.onClose();
			}
			dojo.forEach(top.handlers, dojo.disconnect);

			// #2685: check if the widget still has a domNode so ContentPane can change its URL without getting an error
			if(!widget || !widget.domNode){ return; }

			this.prepare(widget.domNode);

			iframe.destroy();
			dojo.destroy(wrapper);

			if(onClose){
				onClose();
			}
		}
	};
}();

dijit._frames = new function(){
	// summary:
	//		cache of iframes
	var queue = [];

	this.pop = function(){
		var iframe;
		if(queue.length){
			iframe = queue.pop();
			iframe.style.display="";
		}else{
			if(dojo.isIE){
				var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
				var html="<iframe src='" + burl + "'"
					+ " style='position: absolute; left: 0px; top: 0px;"
					+ "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
				iframe = dojo.doc.createElement(html);
			}else{
			 	iframe = dojo.create("iframe");
				iframe.src = 'javascript:""';
				iframe.className = "dijitBackgroundIframe";
			}
			iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work.
			dojo.body().appendChild(iframe);
		}
		return iframe;
	};

	this.push = function(iframe){
		iframe.style.display="none";
		queue.push(iframe);
	}
}();

dijit.BackgroundIframe = function(/* DomNode */node){
	// summary:
	//		For IE z-index schenanigans. id attribute is required.
	//
	// description:
	//		new dijit.BackgroundIframe(node)
	//			Makes a background iframe as a child of node, that fills
	//			area (and position) of node

	if(!node.id){ throw new Error("no id"); }
	if(dojo.isIE || dojo.isMoz){
		var iframe = dijit._frames.pop();
		node.appendChild(iframe);
		if(dojo.isIE<7){
			dojo.style(iframe, {
				width: node.offsetWidth + 'px',
				height: node.offsetHeight + 'px'
			});
			this._conn = dojo.connect(node, 'onresize', function(){
				dojo.style(iframe, {
					width: node.offsetWidth + 'px',
					height: node.offsetHeight + 'px'
				});
			});
		}else{
			dojo.style(iframe, {
				width: '100%',
				height: '100%'
			});
		}
		this.iframe = iframe;
	}
};

dojo.extend(dijit.BackgroundIframe, {
	destroy: function(){
		// summary:
		//		destroy the iframe
		if(this._conn){
			dojo.disconnect(this._conn);
			this._conn = null;
		}
		if(this.iframe){
			dijit._frames.push(this.iframe);
			delete this.iframe;
		}
	}
});
<commit_msg>Workaround flashing problem on FF3.5 when dialog/menu/etc. is first shown.   Thanks Sam.  Fixes #10041, #10111 !strict.<commit_after>dojo.provide("dijit._base.popup");

dojo.require("dijit._base.focus");
dojo.require("dijit._base.place");
dojo.require("dijit._base.window");

dijit.popup = new function(){
	// summary:
	//		This class is used to show/hide widgets as popups.

	var stack = [],
		beginZIndex=1000,
		idGen = 1;

	this.prepare = function(/*DomNode*/ node){
		// summary:
		//		Prepares a node to be used as a popup
		//
		// description:
		//		Attaches node to dojo.doc.body, and
		//		positions it off screen, but not display:none, so that
		//		the widget doesn't appear in the page flow and/or cause a blank
		//		area at the bottom of the viewport (making scrollbar longer), but
		//		initialization of contained widgets works correctly

		var s = node.style;
		s.visibility = "hidden";	// so TAB key doesn't navigate to hidden popup
		s.position = "absolute";
		s.top = "-9999px";
		if(s.display == "none"){
			s.display="";
		}
		dojo.body().appendChild(node);
	};

/*=====
dijit.popup.__OpenArgs = function(){
	// popup: Widget
	//		widget to display
	// parent: Widget
	//		the button etc. that is displaying this popup
	// around: DomNode
	//		DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
	// x: Integer
	//		Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
	// y: Integer
	//		Absolute vertical position (in pixels) to place node at.  (Specity this *or* "around" parameter.)
	// orient: Object || String
	//		When the around parameter is specified, orient should be an
	//		ordered list of tuples of the form (around-node-corner, popup-node-corner).
	//		dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
	//		until the popup appears fully within the viewport.
	//
	//		The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
	//			1. (BL, TL)
	//			2. (TL, BL)
	//		where BL means "bottom left" and "TL" means "top left".
	//		So by default, it first tries putting the popup below the around node, left-aligning them,
	//		and then tries to put it above the around node, still left-aligning them.   Note that the
	//		default is horizontally reversed when in RTL mode.
	//
	//		When an (x,y) position is specified rather than an around node, orient is either
	//		"R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
	//		specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
	//		fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
	//		and the top-right corner.
	// onCancel: Function
	//		callback when user has canceled the popup by
	//			1. hitting ESC or
	//			2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
	//			   i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
	// onClose: Function
	//		callback whenever this popup is closed
	// onExecute: Function
	//		callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
	// padding: dijit.__Position
	//		adding a buffer around the opening position. This is only useful when around is not set.
	this.popup = popup;
	this.parent = parent;
	this.around = around;
	this.x = x;
	this.y = y;
	this.orient = orient;
	this.onCancel = onCancel;
	this.onClose = onClose;
	this.onExecute = onExecute;
	this.padding = padding;
}
=====*/
	this.open = function(/*dijit.popup.__OpenArgs*/ args){
		// summary:
		//		Popup the widget at the specified position
		//
		// example:
		//		opening at the mouse position
		//		|		dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
		//
		// example:
		//		opening the widget as a dropdown
		//		|		dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
		//
		//		Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
		//		(fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.

		var widget = args.popup,
			orient = args.orient || (
				dojo._isBodyLtr() ?
				{'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
				{'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
			),
			around = args.around,
			id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+idGen++);

		// make wrapper div to hold widget and possibly hold iframe behind it.
		// we can't attach the iframe as a child of the widget.domNode because
		// widget.domNode might be a <table>, <ul>, etc.
		var wrapper = dojo.create("div",{
			id: id,
			"class":"dijitPopup",
			style:{
				zIndex: beginZIndex + stack.length,
				visibility:"hidden",
				left: "0px", top: "0px"		// prevent transient scrollbar causing misalign (#5776)
			},
			dijitPopupParent: args.parent ? args.parent.id : ""
		}, dojo.body());
		dijit.setWaiRole(wrapper, "presentation");

		var s = widget.domNode.style;
		s.display = "";
		s.visibility = "";
		s.position = "";
		s.top = "0px";
		wrapper.appendChild(widget.domNode);

		var iframe = new dijit.BackgroundIframe(wrapper);

		// position the wrapper node
		var best = around ?
			dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
			dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);

		wrapper.style.visibility = "visible";
		// TODO: use effects to fade in wrapper

		var handlers = [];

		// Compute the closest ancestor popup that's *not* a child of another popup.
		// Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
		var getTopPopup = function(){
			for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
				/* do nothing, just trying to get right value for pi */
			}
			return stack[pi];
		}

		// provide default escape and tab key handling
		// (this will work for any widget, not just menu)
		handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
			if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
				dojo.stopEvent(evt);
				args.onCancel();
			}else if(evt.charOrCode === dojo.keys.TAB){
				dojo.stopEvent(evt);
				var topPopup = getTopPopup();
				if(topPopup && topPopup.onCancel){
					topPopup.onCancel();
				}
			}
		}));

		// watch for cancel/execute events on the popup and notify the caller
		// (for a menu, "execute" means clicking an item)
		if(widget.onCancel){
			handlers.push(dojo.connect(widget, "onCancel", null, args.onCancel));
		}

		handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", null, function(){
			var topPopup = getTopPopup();
			if(topPopup && topPopup.onExecute){
				topPopup.onExecute();
			}
		}));

		stack.push({
			wrapper: wrapper,
			iframe: iframe,
			widget: widget,
			parent: args.parent,
			onExecute: args.onExecute,
			onCancel: args.onCancel,
 			onClose: args.onClose,
			handlers: handlers
		});

		if(widget.onOpen){
			// TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
			widget.onOpen(best);
		}

		return best;
	};

	this.close = function(/*dijit._Widget*/ popup){
		// summary:
		//		Close specified popup and any popups that it parented
		while(dojo.some(stack, function(elem){return elem.widget == popup;})){
			var top = stack.pop(),
				wrapper = top.wrapper,
				iframe = top.iframe,
				widget = top.widget,
				onClose = top.onClose;

			if(widget.onClose){
				// TODO: in 2.0 standardize onHide() (used by StackContainer) and onHide() (used here)
				widget.onClose();
			}
			dojo.forEach(top.handlers, dojo.disconnect);

			// #2685: check if the widget still has a domNode so ContentPane can change its URL without getting an error
			if(!widget || !widget.domNode){ return; }

			this.prepare(widget.domNode);

			iframe.destroy();
			dojo.destroy(wrapper);

			if(onClose){
				onClose();
			}
		}
	};
}();

dijit._frames = new function(){
	// summary:
	//		cache of iframes
	var queue = [];

	this.pop = function(){
		var iframe;
		if(queue.length){
			iframe = queue.pop();
			iframe.style.display="";
		}else{
			if(dojo.isIE){
				var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
				var html="<iframe src='" + burl + "'"
					+ " style='position: absolute; left: 0px; top: 0px;"
					+ "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
				iframe = dojo.doc.createElement(html);
			}else{
			 	iframe = dojo.create("iframe");
				iframe.src = 'javascript:""';
				iframe.className = "dijitBackgroundIframe";
			}
			iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work.
			dojo.body().appendChild(iframe);
		}
		return iframe;
	};

	this.push = function(iframe){
		iframe.style.display="none";
		queue.push(iframe);
	}
}();

dijit.BackgroundIframe = function(/* DomNode */node){
	// summary:
	//		For IE z-index schenanigans. id attribute is required.
	//
	// description:
	//		new dijit.BackgroundIframe(node)
	//			Makes a background iframe as a child of node, that fills
	//			area (and position) of node

	if(!node.id){ throw new Error("no id"); }
	if(dojo.isIE || dojo.isMoz){
		var iframe = dijit._frames.pop();
		dojo.style(iframe, "opacity", 0.1);		// prevent flash on FF3.5, see #10041, #10111
		node.appendChild(iframe);
		if(dojo.isIE<7){
			dojo.style(iframe, {
				width: node.offsetWidth + 'px',
				height: node.offsetHeight + 'px'
			});
			this._conn = dojo.connect(node, 'onresize', function(){
				dojo.style(iframe, {
					width: node.offsetWidth + 'px',
					height: node.offsetHeight + 'px'
				});
			});
		}else{
			dojo.style(iframe, {
				width: '100%',
				height: '100%'
			});
		}
		this.iframe = iframe;
	}
};

dojo.extend(dijit.BackgroundIframe, {
	destroy: function(){
		// summary:
		//		destroy the iframe
		if(this._conn){
			dojo.disconnect(this._conn);
			this._conn = null;
		}
		if(this.iframe){
			dijit._frames.push(this.iframe);
			delete this.iframe;
		}
	}
});
<|endoftext|>


<commit_before>const { DOM, PropTypes } = React;
const { bind } = _;

const posts = [
  {
   id: 0,
   image: {
     src: 'http://maksim.pw/wp-content/uploads/2017/01/yXa_Zvmys_-ToDOhci2TupdbratAP4cvUQFXdxKTLScDJmxS-508x381.jpg',
     alt: 'kek'
   },
    details: {
      author: 'Andrey',
      createdAt: moment("20170101", "YYYYMMDD").fromNow(),
      updatedAt: moment("20170401", "YYYYMMDD").fromNow(),
      likes: 10,
    }
  },
  {
   id: 1,
   image: {
    src: 'http://maksim.pw/wp-content/uploads/2016/08/kurs-973x730.jpg'
   },
   text: 'Отзыв о курсе',
   details: {
      author: 'Zahar',
      createdAt: moment("20160101", "YYYYMMDD").fromNow(),
      updatedAt: moment("20160901", "YYYYMMDD").fromNow(),
      likes: 5,
   }
  },
  {
   id: 2,
   text: 'Полумарафон'
  }
];

const Image = (props) => (

);

Image.defaultProps = {
  src: 'http://maksim.pw/wp-content/uploads/2016/09/05OB6lDjCQvsXlwMc-C_T_qMMU3qjtEZ3Uz18-9f3kiewcVM-508x381.jpg',
  alt: 'default alt',
  width: '200px',
  height: '200px'
};

Image.propTypes = {
  src: PropTypes.string,
  alt: PropTypes.string,
  width: PropTypes.string,
  height: PropTypes.string
}

const TextBox = (props) => (
  {props.children}
);

TextBox.defaultProps = {
  children: 'default text',
}

TextBox.propTypes = {
  children: PropTypes.string
}

const DetailsBox = (props) => (

Author: {props.author}

createdAt: {props.createdAt}

updatedAt: {props.updatedAt}

    <Like count ={props.likes} />

)

DetailsBox.defaultProps = {
  author: 'Admin',
  createdAt: moment("19700101", "YYYYMMDD").fromNow(),
  updatedAt: moment("20170420", "YYYYMMDD").fromNow(),
  likes: 0
}

DetailsBox.propTypes = {
  author: PropTypes.string,
  createdAt: PropTypes.string,
  updatedAt: PropTypes.string,
  likes: PropTypes.number
}

class Like extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.count };

    this.handleClick = bind(this.handleClick, this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 })
  }

  render() {
    return (

Count: {this.state.count}

        <button onClick={this.handleClick}>Like +</button>

    )
  }
}

Like.defaultProps = {
  count: 0
}

Like.propTypes = {
  count: PropTypes.number
}

const BlogItem = (props) => (
  DOM.div(
    { },
    React.createElement(Image, props.image),
    React.createElement(DetailsBox, props.details),
    React.createElement(TextBox, {}, props.text ),
  )
)

BlogItem.propTypes = {
  image: PropTypes.object,
  details: PropTypes.object,
  text: PropTypes.string
}

const BlogList = ( { posts } ) => (
  DOM.div(
    { },
    _.map(
      posts,
      (post) => (
        React.createElement(BlogItem, { text: post.text, details: post.details, image: post.image, key: post.id })
      )
    )
  )
)

BlogList.propTypes = {
  posts:  PropTypes.arrayOf(PropTypes.object)
}

ReactDOM.render(
  React.createElement(BlogList, { posts }),
  document.getElementById('app')
);
<commit_msg>lesson-2 | assign for createElement BlogItem<commit_after>const { DOM, PropTypes } = React;
const { bind } = _;

const posts = [
  {
   id: 0,
   image: {
     src: 'http://maksim.pw/wp-content/uploads/2017/01/yXa_Zvmys_-ToDOhci2TupdbratAP4cvUQFXdxKTLScDJmxS-508x381.jpg',
     alt: 'kek'
   },
    details: {
      author: 'Andrey',
      createdAt: moment("20170101", "YYYYMMDD").fromNow(),
      updatedAt: moment("20170401", "YYYYMMDD").fromNow(),
      likes: 10,
    }
  },
  {
   id: 1,
   image: {
    src: 'http://maksim.pw/wp-content/uploads/2016/08/kurs-973x730.jpg'
   },
   text: 'Отзыв о курсе',
   details: {
      author: 'Zahar',
      createdAt: moment("20160101", "YYYYMMDD").fromNow(),
      updatedAt: moment("20160901", "YYYYMMDD").fromNow(),
      likes: 5,
   }
  },
  {
   id: 2,
   text: 'Полумарафон'
  }
];

const Image = (props) => (

);

Image.defaultProps = {
  src: 'http://maksim.pw/wp-content/uploads/2016/09/05OB6lDjCQvsXlwMc-C_T_qMMU3qjtEZ3Uz18-9f3kiewcVM-508x381.jpg',
  alt: 'default alt',
  width: '200px',
  height: '200px'
};

Image.propTypes = {
  src: PropTypes.string,
  alt: PropTypes.string,
  width: PropTypes.string,
  height: PropTypes.string
}

const TextBox = (props) => (
  {props.children}
);

TextBox.defaultProps = {
  children: 'default text',
}

TextBox.propTypes = {
  children: PropTypes.string
}

const DetailsBox = (props) => (

Author: {props.author}

createdAt: {props.createdAt}

updatedAt: {props.updatedAt}

    <Like count ={props.likes} />

)

DetailsBox.defaultProps = {
  author: 'Admin',
  createdAt: moment("19700101", "YYYYMMDD").fromNow(),
  updatedAt: moment("20170420", "YYYYMMDD").fromNow(),
  likes: 0
}

DetailsBox.propTypes = {
  author: PropTypes.string,
  createdAt: PropTypes.string,
  updatedAt: PropTypes.string,
  likes: PropTypes.number
}

class Like extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.count };

    this.handleClick = bind(this.handleClick, this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 })
  }

  render() {
    return (

Count: {this.state.count}

        <button onClick={this.handleClick}>Like +</button>

    )
  }
}

Like.defaultProps = {
  count: 0
}

Like.propTypes = {
  count: PropTypes.number
}

const BlogItem = ({ id, image, details, text }) => (
  DOM.div(
    { },
    React.createElement(Image, image),
    React.createElement(DetailsBox, details),
    React.createElement(TextBox, {}, text ),
  )
)

BlogItem.propTypes = {
  id: PropTypes.number,
  image: PropTypes.object,
  details: PropTypes.object,
  text: PropTypes.string
}

const BlogList = ( { posts } ) => (
  DOM.div(
    { },
    _.map(
      posts,
      (post) => (
        React.createElement(BlogItem, _.assign({}, post, { key: post.id }))
      )
    )
  )
)

BlogList.propTypes = {
  posts:  PropTypes.arrayOf(PropTypes.object)
}

ReactDOM.render(
  React.createElement(BlogList, { posts }),
  document.getElementById('app')
);
<|endoftext|>


<commit_before>/**
 *  ------------------------------
 *  upload.base 上传基础类
 *  ------------------------------
 */

const Component = require('../../../../../ui-base/component');
const _ = require('../../../../../ui-base/_');
const utils = require('../../utils');
const Config = require('../../config');
const FileUnit = require('../FileUnit');
const KLImagePreview = require('../KLImagePreview');

/**
 * @class UploadBase
 * @extend Component
 * @param {object}     [options.data]                = 绑定属性
 * @param {string}     [options.data.action]         => 必选，上传地址
 * @param {array}      [options.data.file-list]      => 上传的文件列表, 可以指定初始值，代表已经上传的文件，见demo，每次操作文件后，
 *                                                       都可以通过该参数绑定的变量，得到最新的文件列表，其中每个文件项包含下面的字段:
 *                                                       name: 文件名称
 *                                                       url: 文件的路径
 *                                                       flag: 0, 新增的文件; 1, 已经上传未被删除的文件，2，已经上传被删除的文件
 * @param {string}     [options.data.name]           => 可选，上传的文件字段名, 默认为'file'
 * @param {boolean}    [options.data.multiple]       => 可选，是否支持多选, 可选值true/false，默认false单选
 * @param {object}     [options.data.data]           => 可选，上传时附带的额外参数
 * @param {boolean}    [options.data.drag]           => 可选，是否支持拖拽上传，可选值true/false，默认false不支持拖拽
 * @param {string}     [options.data.accept]         => 可选，接受上传的文件类型, 同input的accept属性
 * @param {string}     [options.data.list-type]      => 可选，上传组件的展示形式, 可选值list/card，默认list
 * @param {number}     [options.data.num-min]        => 可选，指定的至少上传的文件个数，默认无限制
 * @param {number}     [options.data.num-max]        => 可选，最大允许上传文件的个数，默认无限制
 * @param {number}     [options.data.num-perline]    => 可选，每行展示的文件个数，对于列表形式，默认无限制，根据父容器自动折行；
 *                                                       对于表单形式，默认每行展示5个
 * @param {number}     [options.data.max-size]       => 可选，上传文件大小的最大允许值, 支持数值大小以及KB,MB,GB为单元的指定
 * @param {boolean}    [options.data.readonly]       => 可选，是否开启预览模式，可选值true/false，true预览模式，只能预览和下载图片，
 *                                                       默认false，允许上传和删除图片
 * @param {number}     [options.data.image-width]    => 可选，指定上传图片文件的宽度, 值为数值，单位为px，如800
 * @param {number}     [options.data.image-height]   => 可选，指定上传图片文件的高度, 值为数值，单位为px, 如600
 * @param {string}     [options.data.image-scale]    => 可选，指定上传图片文件的宽高比, 值为冒号分隔的宽高比例字符串，如'4:3'
 * @param {boolean}    [options.data.readonly]       => 可选，是否开启预览模式，可选值true/false，true预览模式，只能预览和下载图片，
 */
const UploadBase = Component.extend({
  config(data) {
    this.supr(data);

    _.extend(data, {
      action: '',
      name: 'file',
      multiple: false,
      data: {},
      drag: false,
      accept: '*',
      listType: 'list',
      fileList: [],
      numMin: -Infinity,
      numMax: Infinity,
      numPerline: Infinity,
      maxSize: Config.sizeMap.GB,
      readonly: false,
      imageWidth: Infinity,
      imageHeight: Infinity,
      imageScale: '',
      data: {},
      encType: 'multipart/form-data'
    });

    _.extend(data, {
      fileUnitList: []
    });

    this.initWatchers();
    this.initUploadedFileUnitList();
  },

  initWatchers: function() {
    const self = this;
    const data = this.data;

    function filterDeleted(file) {
      return file.flag !== Config.flagMap['DELETED'];
    }

    this.$watch('fileList', function(newVal, oldVal) {
      if (oldVal !== undefined) {
        if (newVal.length >= oldVal.length) {
          self.extendFileList(newVal);
        } else {
          self.reduceFileList(newVal, oldVal);
        }
      }
    }, true);
  },

  extendFileList: function(fileList) {
    const self = this;
    const data = this.data;

    function filterDeleted(file) {
      return file.flag !== Config.flagMap['DELETED'];
    }

    fileList.filter(filterDeleted).forEach(function(file, index) {
      if (!file.uid) {
        const options = self.setOptions(data);
        const uid = utils.genUid();
        file.uid = uid;
        file.flag = Config.flagMap['ADDED'];
        const type = self.getFileType(file);
        const fileunit = {
          name: file.name,
          url: file.url,
          type: self.getFileType(file),
          flag: 'ADDED',
          uid: file.uid,
          status: 'success'
        };
        data.fileUnitList.splice(index, index, fileunit);
      }
    });
  },

  reduceFileList: function(deletedFileList, srcFileList) {
    const self = this;
    const data = this.data;

    function filterDeleted(file, srcIndex) {
      const index = deletedFileList.findIndex(function(item) {
        let isEqual = item.name === file.name && item.url === file.url;
        if (item.uid && file.uid) {
          isEqual = isEqual && item.uid === file.uid;
        }
        return isEqual;
      });

      if (index === -1 && (file.flag === Config.flagMap['ORIGINAL'] || file.flag === Config.flagMap['DELETED'])) {
        file.flag = Config.flagMap['DELETED'];
        data.fileList.splice(srcIndex, 0, file);
      }

      return index === -1;
    }

    srcFileList.filter(filterDeleted).forEach(function(file, index) {
      if (file.uid) {
        let visualIndex = data.fileUnitList.findIndex(function(item) {
          return item.uid === file.uid;
        });

        if (visualIndex !== -1) {
          data.fileUnitList.splice(visualIndex, 1);
        }
      }
    });
  },

  init(data) {
    this.supr(data);
  },

  initUploadedFileUnitList() {
    const self = this;
    const data = this.data;
    const fileList = data.fileList;
    const fileUnitList = data.fileUnitList;

    if (data.fileList.length > 0) {
      const options = this.setOptions(data);
      fileList.forEach((file) => {
        let uid = utils.genUid();
        file.uid = uid;
        file.flag = Config.flagMap['ORIGINAL'];
        const fileunit = {
          name: file.name,
          url: file.url,
          type: self.getFileType(file),
          flag: 'ORIGINAL',
          uid: file.uid,
          status: 'success'
        };
        fileUnitList.push(fileunit);
      });
    }
  },

  updateList() {
    // setTimeout((function() { this.updateFileList(); }).bind(this), 0);
    this.updateFileList();
  },

  updateFileList() {
    const self = this;
    const data = this.data;
    const fileList = data.fileList;
    const fileUnitList = data.fileUnitList;
    const newFileList = [];

    for (var index = fileUnitList.length - 1; index >= 0; index--) {
      let file = fileUnitList[index];
      let uid = file.uid;
      let flag = file.flag;
      let destroyed = file.destroyed;
      let fileIndex = fileList.findIndex(function(file) {
        return uid == file.uid;
      });

      if (fileIndex === -1) {
        newFileList.push({
          name: file.name,
          url: file.url,
          flag: Config.flagMap[flag],
          uid
        });
      } else {
        if (flag === 'DELETED') {
          fileList[fileIndex].flag = Config.flagMap['DELETED'];
          fileUnitList.splice(index, 1);
        } else if (destroyed) {
          fileUnitList.splice(index, 1);
          fileList.splice(fileIndex, 1);
        }
      }
    }

    [].push.apply(fileList, newFileList.reverse());

    this.$update();
  },

  fileDialogOpen() {
    var inputNode = this.$refs.file;
    inputNode && inputNode.click();
  },

  fileSelect() {
    const inputNode = this.$refs.file;
    const files = inputNode.files;

    this.handleFiles(files);

    inputNode.value = '';
  },

  onDragEnter(e) {
    e.stopPropagation();
    e.preventDefault();
  },

  onDragOver(e) {
    e.stopPropagation();
    e.preventDefault();
  },

  onDrop(e) {
    e.stopPropagation();
    e.preventDefault();

    if (!this.data.drag) {
      return;
    }

    const dt = e.event && e.event.dataTransfer;
    const files = dt.files;

    this.handleFiles(files);
  },

  handleFiles(files) {
    const self = this;
    const data = this.data;

    const options = this.setOptions(data);

    data.preCheckInfo = '';

    files = [].slice.call(files);
    files.forEach(function(file) {
      if (data.fileUnitList.length < data.numMax) {
        const checker = self.preCheck(file);
        checker.then(function(preCheckInfo) {
          data.preCheckInfo = preCheckInfo;
          // self.$update();
          if (!data.preCheckInfo) {
            const fileunit = {
              rawFile: file,
              name: file.name,
              url: window.URL.createObjectURL(file),
              type: self.getFileType(file),
              flag: 'ADDED',
              uid: utils.genUid(),
              status: 'ready'
            };
            data.fileUnitList.push(fileunit);
            self.$update();
          }
        });
      }
    });
  },

  onPreview(info) {
    const current = info.file;

    function filterImgFile(file) {
      return file.type === 'image';
    }

    function mapCurrentFlag(img) {
      if (current === img) {
        img.current = true;
      }
      return img;
    }

    const imageList = this.data.fileUnitList
      .filter(filterImgFile)
      .map(mapCurrentFlag);

    const preview = createImagePreview(imageList);

    preview.$inject(this.$refs.imagepreview);

    function createImagePreview(imgFileList) {
      function findHelper(img) {
        return img.current;
      }

      const curIndex = imgFileList.findIndex(findHelper);

      function mapHelper(img) {
        delete img.current;
        return {
          src: img.url,
          name: img.name,
          status: img.status
        };
      }

      const imageList = imgFileList.map(mapHelper);
      const imagePreview = new KLImagePreview({
        data: {
          imageList,
          curIndex,
        },
      });

      imagePreview.$on('remove', (imgInfo) => {
        const index = imgInfo.index;
        const imgInst = imgFileList[index];

        if (imgInst) {
          imgInst.$emit('remove');
        }
      });

      imagePreview.$on('$destroy', () => {
        imgFileList.splice(0);
      });

      return imagePreview;
    }
  },

  onProgress(info) {
    this.$emit(
      'progress',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onSuccess(info) {
    this.updateList();
    this.$emit(
      'success',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onError(info) {
    this.updateList();
    this.$emit(
      'error',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onRemove(info) {
    const inst = info.sender;
    const file = info.file;
    file.destroyed = true;
    if (file.flag === 'ORIGINAL') {
      file.flag = 'DELETED';
    }
    inst.destroy();
    this.updateList();

    this.$emit(
        'remove',
        _.extend(info, {
          fileList: this.data.fileList
        })
    );
  },

  setOptions(options) {
    const opts = options || {};

    return {
      url: opts.action,
      name: opts.name,
      readonly: opts.readonly,
      data: opts.data
    };
  },

  preCheck(file) {
    const self = this;
    const onPass = function(resolve) {
      let preCheckInfo = '';
      if (!self.isAcceptedFileSize(file)) {
        preCheckInfo = self.$trans('FILE_TOO_LARGE');
        return resolve(preCheckInfo);
      }
      if (!self.isAcceptedFileType(file)) {
        preCheckInfo = self.$trans('FILE_TYPE_ERROR');
        return resolve(preCheckInfo);
      }

      const imageChecker = self.preCheckImage(file);
      imageChecker && imageChecker.then(function(imageCheckInfo) {
        return resolve(imageCheckInfo);
      });
    };

    const onError = function(reject) {};

    return new Promise(onPass, onError);
  },

  preCheckImage(file) {
    const self = this;
    const data = this.data;
    const type = this.getFileType(file).toLowerCase();

    if (type === 'image') {
      const imageWidth = data.imageWidth;
      const imageHeight = data.imageHeight;
      const imageScale = data.imageScale;

      const onResolve = function(resolve) {
        const img = new Image();
        img.onload = function() {
          window.URL.revokeObjectURL(img.src);
          const width = img.width;
          const height = img.height;
          let checkInfo = '';
          if (isFinite(imageWidth) && imageWidth !== width) {
            checkInfo = self.$trans('IMAGE_WIDTH_ERROR');
          }
          if (isFinite(imageHeight) && imageHeight !== height) {
            checkInfo = self.$trans('IMAGE_HEIGHT_ERROR');
          }
          if (!!imageScale) {
            const scaleList = imageScale.split(':');
            const scaleW = scaleList[0];
            const scaleH = scaleList[1];
            if (Math.abs(width / height - scaleW / scaleH) > 0.01) {
              checkInfo = self.$trans('IMAGE_SCALE_ERROR');
            }
          }

          resolve(checkInfo);
        }

        img.src = window.URL.createObjectURL(file);
      };

      const onReject = function(reject) {};

      return new Promise(onResolve, onReject);
    }
  },

  isAcceptedFileType(file) {
    const data = this.data;
    const accept = data.accept;
    const type = this.getFileType(file).toLowerCase();
    let isValid = false;

    accept.split(',').forEach((cond) => {
      if (cond === '*') {
        isValid = true;
      } else if (/image\/.*/.test(cond)) {
        isValid = isValid || type === 'image';
      } else if (/audio\/.*/.test(cond)) {
        isValid = isValid || type === 'audio';
      } else if (/video\/.*/.test(cond)) {
        isValid = isValid || type === 'video';
      } else {
        isValid = isValid || type === Config.typeMap[cond];
      }
    });

    return isValid;
  },

  getFileType(file) {
    const type = file.type || '';
    const name = file.name || '';
    const typeMap = Config.typeMap;
    let typeStr = 'unknown';

    Object.keys(typeMap).forEach(function(key) {
      const reg = new RegExp(key + '$');
      if (reg.test(type) || !type && reg.test(name)) {
        typeStr = typeMap[key];
      }
    });

    return typeStr;
  },

  isAcceptedFileSize(file) {
    const data = this.data;
    const maxSize = data.maxSize;
    const fileSize = file.size;

    const patterns = maxSize.match(/(\d+)(\D+)?/i);
    let size = patterns[1];
    const unit = patterns[2];

    if (unit) {
      size *= Config.sizeMap[unit.toUpperCase()];
    }

    return size >= fileSize;
  }
})
  .component('file-unit', FileUnit);

module.exports = UploadBase;
<commit_msg>misc: fix upload issue for files otherthan image in klupload<commit_after>/**
 *  ------------------------------
 *  upload.base 上传基础类
 *  ------------------------------
 */

const Component = require('../../../../../ui-base/component');
const _ = require('../../../../../ui-base/_');
const utils = require('../../utils');
const Config = require('../../config');
const FileUnit = require('../FileUnit');
const KLImagePreview = require('../KLImagePreview');

/**
 * @class UploadBase
 * @extend Component
 * @param {object}     [options.data]                = 绑定属性
 * @param {string}     [options.data.action]         => 必选，上传地址
 * @param {array}      [options.data.file-list]      => 上传的文件列表, 可以指定初始值，代表已经上传的文件，见demo，每次操作文件后，
 *                                                       都可以通过该参数绑定的变量，得到最新的文件列表，其中每个文件项包含下面的字段:
 *                                                       name: 文件名称
 *                                                       url: 文件的路径
 *                                                       flag: 0, 新增的文件; 1, 已经上传未被删除的文件，2，已经上传被删除的文件
 * @param {string}     [options.data.name]           => 可选，上传的文件字段名, 默认为'file'
 * @param {boolean}    [options.data.multiple]       => 可选，是否支持多选, 可选值true/false，默认false单选
 * @param {object}     [options.data.data]           => 可选，上传时附带的额外参数
 * @param {boolean}    [options.data.drag]           => 可选，是否支持拖拽上传，可选值true/false，默认false不支持拖拽
 * @param {string}     [options.data.accept]         => 可选，接受上传的文件类型, 同input的accept属性
 * @param {string}     [options.data.list-type]      => 可选，上传组件的展示形式, 可选值list/card，默认list
 * @param {number}     [options.data.num-min]        => 可选，指定的至少上传的文件个数，默认无限制
 * @param {number}     [options.data.num-max]        => 可选，最大允许上传文件的个数，默认无限制
 * @param {number}     [options.data.num-perline]    => 可选，每行展示的文件个数，对于列表形式，默认无限制，根据父容器自动折行；
 *                                                       对于表单形式，默认每行展示5个
 * @param {number}     [options.data.max-size]       => 可选，上传文件大小的最大允许值, 支持数值大小以及KB,MB,GB为单元的指定
 * @param {boolean}    [options.data.readonly]       => 可选，是否开启预览模式，可选值true/false，true预览模式，只能预览和下载图片，
 *                                                       默认false，允许上传和删除图片
 * @param {number}     [options.data.image-width]    => 可选，指定上传图片文件的宽度, 值为数值，单位为px，如800
 * @param {number}     [options.data.image-height]   => 可选，指定上传图片文件的高度, 值为数值，单位为px, 如600
 * @param {string}     [options.data.image-scale]    => 可选，指定上传图片文件的宽高比, 值为冒号分隔的宽高比例字符串，如'4:3'
 * @param {boolean}    [options.data.readonly]       => 可选，是否开启预览模式，可选值true/false，true预览模式，只能预览和下载图片，
 */
const UploadBase = Component.extend({
  config(data) {
    this.supr(data);

    _.extend(data, {
      action: '',
      name: 'file',
      multiple: false,
      data: {},
      drag: false,
      accept: '*',
      listType: 'list',
      fileList: [],
      numMin: -Infinity,
      numMax: Infinity,
      numPerline: Infinity,
      maxSize: Config.sizeMap.GB,
      readonly: false,
      imageWidth: Infinity,
      imageHeight: Infinity,
      imageScale: '',
      data: {},
      encType: 'multipart/form-data'
    });

    _.extend(data, {
      fileUnitList: []
    });

    this.initWatchers();
    this.initUploadedFileUnitList();
  },

  initWatchers: function() {
    const self = this;
    const data = this.data;

    function filterDeleted(file) {
      return file.flag !== Config.flagMap['DELETED'];
    }

    this.$watch('fileList', function(newVal, oldVal) {
      if (oldVal !== undefined) {
        if (newVal.length >= oldVal.length) {
          self.extendFileList(newVal);
        } else {
          self.reduceFileList(newVal, oldVal);
        }
      }
    }, true);
  },

  extendFileList: function(fileList) {
    const self = this;
    const data = this.data;

    function filterDeleted(file) {
      return file.flag !== Config.flagMap['DELETED'];
    }

    fileList.filter(filterDeleted).forEach(function(file, index) {
      if (!file.uid) {
        const options = self.setOptions(data);
        const uid = utils.genUid();
        file.uid = uid;
        file.flag = Config.flagMap['ADDED'];
        const type = self.getFileType(file);
        const fileunit = {
          name: file.name,
          url: file.url,
          type: self.getFileType(file),
          flag: 'ADDED',
          uid: file.uid,
          status: 'success'
        };
        data.fileUnitList.splice(index, index, fileunit);
      }
    });
  },

  reduceFileList: function(deletedFileList, srcFileList) {
    const self = this;
    const data = this.data;

    function filterDeleted(file, srcIndex) {
      const index = deletedFileList.findIndex(function(item) {
        let isEqual = item.name === file.name && item.url === file.url;
        if (item.uid && file.uid) {
          isEqual = isEqual && item.uid === file.uid;
        }
        return isEqual;
      });

      if (index === -1 && (file.flag === Config.flagMap['ORIGINAL'] || file.flag === Config.flagMap['DELETED'])) {
        file.flag = Config.flagMap['DELETED'];
        data.fileList.splice(srcIndex, 0, file);
      }

      return index === -1;
    }

    srcFileList.filter(filterDeleted).forEach(function(file, index) {
      if (file.uid) {
        let visualIndex = data.fileUnitList.findIndex(function(item) {
          return item.uid === file.uid;
        });

        if (visualIndex !== -1) {
          data.fileUnitList.splice(visualIndex, 1);
        }
      }
    });
  },

  init(data) {
    this.supr(data);
  },

  initUploadedFileUnitList() {
    const self = this;
    const data = this.data;
    const fileList = data.fileList;
    const fileUnitList = data.fileUnitList;

    if (data.fileList.length > 0) {
      const options = this.setOptions(data);
      fileList.forEach((file) => {
        let uid = utils.genUid();
        file.uid = uid;
        file.flag = Config.flagMap['ORIGINAL'];
        const fileunit = {
          name: file.name,
          url: file.url,
          type: self.getFileType(file),
          flag: 'ORIGINAL',
          uid: file.uid,
          status: 'success'
        };
        fileUnitList.push(fileunit);
      });
    }
  },

  updateList() {
    // setTimeout((function() { this.updateFileList(); }).bind(this), 0);
    this.updateFileList();
  },

  updateFileList() {
    const self = this;
    const data = this.data;
    const fileList = data.fileList;
    const fileUnitList = data.fileUnitList;
    const newFileList = [];

    for (var index = fileUnitList.length - 1; index >= 0; index--) {
      let file = fileUnitList[index];
      let uid = file.uid;
      let flag = file.flag;
      let destroyed = file.destroyed;
      let fileIndex = fileList.findIndex(function(file) {
        return uid == file.uid;
      });

      if (fileIndex === -1) {
        newFileList.push({
          name: file.name,
          url: file.url,
          flag: Config.flagMap[flag],
          uid
        });
      } else {
        if (flag === 'DELETED') {
          fileList[fileIndex].flag = Config.flagMap['DELETED'];
          fileUnitList.splice(index, 1);
        } else if (destroyed) {
          fileUnitList.splice(index, 1);
          fileList.splice(fileIndex, 1);
        }
      }
    }

    [].push.apply(fileList, newFileList.reverse());

    this.$update();
  },

  fileDialogOpen() {
    var inputNode = this.$refs.file;
    inputNode && inputNode.click();
  },

  fileSelect() {
    const inputNode = this.$refs.file;
    const files = inputNode.files;

    this.handleFiles(files);

    inputNode.value = '';
  },

  onDragEnter(e) {
    e.stopPropagation();
    e.preventDefault();
  },

  onDragOver(e) {
    e.stopPropagation();
    e.preventDefault();
  },

  onDrop(e) {
    e.stopPropagation();
    e.preventDefault();

    if (!this.data.drag) {
      return;
    }

    const dt = e.event && e.event.dataTransfer;
    const files = dt.files;

    this.handleFiles(files);
  },

  handleFiles(files) {
    const self = this;
    const data = this.data;

    const options = this.setOptions(data);

    data.preCheckInfo = '';

    files = [].slice.call(files);
    files.forEach(function(file) {
      if (data.fileUnitList.length < data.numMax) {
        const checker = self.preCheck(file);
        checker.then(function(preCheckInfo) {
          data.preCheckInfo = preCheckInfo;
          self.$update();
          if (!data.preCheckInfo) {
            const fileunit = {
              rawFile: file,
              name: file.name,
              url: window.URL.createObjectURL(file),
              type: self.getFileType(file),
              flag: 'ADDED',
              uid: utils.genUid(),
              status: 'ready'
            };
            data.fileUnitList.push(fileunit);
            self.$update();
          }
        });
      }
    });
  },

  onPreview(info) {
    const current = info.file;

    function filterImgFile(file) {
      return file.type === 'image';
    }

    function mapCurrentFlag(img) {
      if (current === img) {
        img.current = true;
      }
      return img;
    }

    const imageList = this.data.fileUnitList
      .filter(filterImgFile)
      .map(mapCurrentFlag);

    const preview = createImagePreview(imageList);

    preview.$inject(this.$refs.imagepreview);

    function createImagePreview(imgFileList) {
      function findHelper(img) {
        return img.current;
      }

      const curIndex = imgFileList.findIndex(findHelper);

      function mapHelper(img) {
        delete img.current;
        return {
          src: img.url,
          name: img.name,
          status: img.status
        };
      }

      const imageList = imgFileList.map(mapHelper);
      const imagePreview = new KLImagePreview({
        data: {
          imageList,
          curIndex,
        },
      });

      imagePreview.$on('remove', (imgInfo) => {
        const index = imgInfo.index;
        const imgInst = imgFileList[index];

        if (imgInst) {
          imgInst.$emit('remove');
        }
      });

      imagePreview.$on('$destroy', () => {
        imgFileList.splice(0);
      });

      return imagePreview;
    }
  },

  onProgress(info) {
    this.$emit(
      'progress',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onSuccess(info) {
    this.updateList();
    this.$emit(
      'success',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onError(info) {
    this.updateList();
    this.$emit(
      'error',
      _.extend(info, {
        fileList: this.data.fileList
      })
    );
  },

  onRemove(info) {
    const inst = info.sender;
    const file = info.file;
    file.destroyed = true;
    if (file.flag === 'ORIGINAL') {
      file.flag = 'DELETED';
    }
    inst.destroy();
    this.updateList();

    this.$emit(
        'remove',
        _.extend(info, {
          fileList: this.data.fileList
        })
    );
  },

  setOptions(options) {
    const opts = options || {};

    return {
      url: opts.action,
      name: opts.name,
      readonly: opts.readonly,
      data: opts.data
    };
  },

  preCheck(file) {
    const self = this;
    const onPass = function(resolve) {
      const type = self.getFileType(file).toLowerCase();
      let preCheckInfo = '';

      if (!self.isAcceptedFileSize(file)) {
        preCheckInfo = self.$trans('FILE_TOO_LARGE');
        return resolve(preCheckInfo);
      }
      if (!self.isAcceptedFileType(file)) {
        preCheckInfo = self.$trans('FILE_TYPE_ERROR');
        return resolve(preCheckInfo);
      }

      if (type === 'image') {
        const imageChecker = self.preCheckImage(file);
        imageChecker && imageChecker.then(function (imageCheckInfo) {
          return resolve(imageCheckInfo);
        });
      } else {
        return resolve(preCheckInfo);
      }
    };

    const onError = function(reject) {};

    return new Promise(onPass, onError);
  },

  preCheckImage(file) {
    const self = this;
    const data = this.data;
    const type = this.getFileType(file).toLowerCase();

    if (type === 'image') {
      const imageWidth = data.imageWidth;
      const imageHeight = data.imageHeight;
      const imageScale = data.imageScale;

      const onResolve = function(resolve) {
        const img = new Image();
        img.onload = function() {
          window.URL.revokeObjectURL(img.src);
          const width = img.width;
          const height = img.height;
          let checkInfo = '';
          if (isFinite(imageWidth) && imageWidth !== width) {
            checkInfo = self.$trans('IMAGE_WIDTH_ERROR');
          }
          if (isFinite(imageHeight) && imageHeight !== height) {
            checkInfo = self.$trans('IMAGE_HEIGHT_ERROR');
          }
          if (!!imageScale) {
            const scaleList = imageScale.split(':');
            const scaleW = scaleList[0];
            const scaleH = scaleList[1];
            if (Math.abs(width / height - scaleW / scaleH) > 0.01) {
              checkInfo = self.$trans('IMAGE_SCALE_ERROR');
            }
          }

          resolve(checkInfo);
        }

        img.src = window.URL.createObjectURL(file);
      };

      const onReject = function(reject) {};

      return new Promise(onResolve, onReject);
    }
  },

  isAcceptedFileType(file) {
    const data = this.data;
    const accept = data.accept;
    const type = this.getFileType(file).toLowerCase();
    let isValid = false;

    accept.split(',').forEach((cond) => {
      if (cond === '*') {
        isValid = true;
      } else if (/image\/.*/.test(cond)) {
        isValid = isValid || type === 'image';
      } else if (/audio\/.*/.test(cond)) {
        isValid = isValid || type === 'audio';
      } else if (/video\/.*/.test(cond)) {
        isValid = isValid || type === 'video';
      } else {
        isValid = isValid || type === Config.typeMap[cond];
      }
    });

    return isValid;
  },

  getFileType(file) {
    const type = file.type || '';
    const name = file.name || '';
    const typeMap = Config.typeMap;
    let typeStr = 'unknown';

    Object.keys(typeMap).forEach(function(key) {
      const reg = new RegExp(key + '$');
      if (reg.test(type) || !type && reg.test(name)) {
        typeStr = typeMap[key];
      }
    });

    return typeStr;
  },

  isAcceptedFileSize(file) {
    const data = this.data;
    const maxSize = data.maxSize;
    const fileSize = file.size;

    const patterns = maxSize.match(/(\d+)(\D+)?/i);
    let size = patterns[1];
    const unit = patterns[2];

    if (unit) {
      size *= Config.sizeMap[unit.toUpperCase()];
    }

    return size >= fileSize;
  }
})
  .component('file-unit', FileUnit);

module.exports = UploadBase;
<|endoftext|>


<commit_before>var url = require('url');
var inherits = require('util').inherits;
var links = require('../utils').links;

var path = require('path');

var marked = require('marked');

var rendererId = 0;

function GitBookRenderer(options, extra_options) {
    if(!(this instanceof GitBookRenderer)) {
        return new GitBookRenderer(options, extra_options);
    }
    GitBookRenderer.super_.call(this, options);

    this._extra_options = extra_options;
    this.quizRowId = 0;
    this.id = rendererId++;
    this.quizIndex = 0;
}
inherits(GitBookRenderer, marked.Renderer);

GitBookRenderer.prototype._unsanitized = function(href) {
    var prot = '';
    try {
        prot = decodeURIComponent(unescape(href))
            .replace(/[^\w:]/g, '')
            .toLowerCase();

    } catch (e) {
        return true;
    }

    if(prot.indexOf('javascript:') === 0) {
        return true;
    }

    return false;
};

GitBookRenderer.prototype.link = function(href, title, text) {
    // Our "fixed" href
    var _href = href;

    // Don't build if it looks malicious
    if (this.options.sanitize && this._unsanitized(href)) {
        return text;
    }

    // Parsed version of the url
    var parsed = url.parse(href);

    var o = this._extra_options;

    // Relative link, rewrite it to point to github repo
    if(links.isRelative(_href)) {
        if (path.extname(_href) == ".md") {
            _href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");

            if (o.singleFile) {
                _href = "#"+_href;
            } else {
                _href = _href.replace(".md", ".html");
            }
        } else if (o && o.repo && o.dir) {
            href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
            parsed = url.parse(href);
            _href = parsed.href;
        }
    }

    // Generate HTML for link
    var out = '' + text + '';
    return out;
};

GitBookRenderer.prototype.image = function(href, title, text) {
    // Our "fixed" href
    var _href = href;

    // Parsed version of the url
    var parsed = url.parse(href);

    // Options
    var o = this._extra_options;

    // Relative image, rewrite it depending output
    if(links.isRelative(href) && o && o.dir && o.outdir) {
        // o.dir: directory parent of the file currently in rendering process
        // o.outdir: directory parent from the html output

        _href = links.toAbsolute(_href, o.dir, o.outdir);
    }

    return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
};

GitBookRenderer.prototype.tablerow = function(content) {
    this.quizRowId += 1;
    return GitBookRenderer.super_.prototype.tablerow(content);
};

var fieldRegex = /^([(\[])([ x])[\])]/;
GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
    var match = fieldRegex.exec(text);
    if (!match) {
        return text;
    }
    var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
    var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
    field += match[1] === '(' ? "radio" : "checkbox";
    field += match[2] === 'x' ? "' checked/>" : "'/>";
    var splittedText = text.split(fieldRegex);
    var length = splittedText.length;
    var label = '' + splittedText[length - 1] + '';
    return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
}

GitBookRenderer.prototype.tablecell = function(content, flags) {
    return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
};

GitBookRenderer.prototype.listitem = function(text) {
    return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
};

// Exports
module.exports = GitBookRenderer;
<commit_msg>Convert links with fragments from md to html<commit_after>var url = require('url');
var inherits = require('util').inherits;
var links = require('../utils').links;

var path = require('path');

var marked = require('marked');

var rendererId = 0;

function GitBookRenderer(options, extra_options) {
    if(!(this instanceof GitBookRenderer)) {
        return new GitBookRenderer(options, extra_options);
    }
    GitBookRenderer.super_.call(this, options);

    this._extra_options = extra_options;
    this.quizRowId = 0;
    this.id = rendererId++;
    this.quizIndex = 0;
}
inherits(GitBookRenderer, marked.Renderer);

GitBookRenderer.prototype._unsanitized = function(href) {
    var prot = '';
    try {
        prot = decodeURIComponent(unescape(href))
            .replace(/[^\w:]/g, '')
            .toLowerCase();

    } catch (e) {
        return true;
    }

    if(prot.indexOf('javascript:') === 0) {
        return true;
    }

    return false;
};

GitBookRenderer.prototype.link = function(href, title, text) {
    // Our "fixed" href
    var _href = href;

    // Don't build if it looks malicious
    if (this.options.sanitize && this._unsanitized(href)) {
        return text;
    }

    // Parsed version of the url
    var parsed = url.parse(href);

    var o = this._extra_options;

    // Relative link, rewrite it to point to github repo
    if(links.isRelative(_href)) {
        if (path.extname(parsed.path) == ".md") {
            _href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");

            if (o.singleFile) {
                _href = "#"+_href;
            } else {
                _href = _href.replace(".md", ".html");
            }
        } else if (o && o.repo && o.dir) {
            href = url.resolve('https://github.com/' + o.repo + '/blob/', [o.dir, _href].join("/"));
            parsed = url.parse(href);
            _href = parsed.href;
        }
    }

    // Generate HTML for link
    var out = '' + text + '';
    return out;
};

GitBookRenderer.prototype.image = function(href, title, text) {
    // Our "fixed" href
    var _href = href;

    // Parsed version of the url
    var parsed = url.parse(href);

    // Options
    var o = this._extra_options;

    // Relative image, rewrite it depending output
    if(links.isRelative(href) && o && o.dir && o.outdir) {
        // o.dir: directory parent of the file currently in rendering process
        // o.outdir: directory parent from the html output

        _href = links.toAbsolute(_href, o.dir, o.outdir);
    }

    return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
};

GitBookRenderer.prototype.tablerow = function(content) {
    this.quizRowId += 1;
    return GitBookRenderer.super_.prototype.tablerow(content);
};

var fieldRegex = /^([(\[])([ x])[\])]/;
GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
    var match = fieldRegex.exec(text);
    if (!match) {
        return text;
    }
    var quizIdentifier = 'quiz-row-' + this.id + '-' + this.quizRowId + '-' + this.quizIndex++;
    var field = "<input name='" + quizIdentifier + "' id='" + quizIdentifier + "' type='";
    field += match[1] === '(' ? "radio" : "checkbox";
    field += match[2] === 'x' ? "' checked/>" : "'/>";
    var splittedText = text.split(fieldRegex);
    var length = splittedText.length;
    var label = '' + splittedText[length - 1] + '';
    return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
}

GitBookRenderer.prototype.tablecell = function(content, flags) {
    return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
};

GitBookRenderer.prototype.listitem = function(text) {
    return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
};

// Exports
module.exports = GitBookRenderer;
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('foundation.iconic', [])
    .service('Iconic', iconic)
    .directive('zfIconic', zfIconic)
  ;

  //iconic wrapper
  function iconic() {
    var iconicObject = IconicJS();

    var service = {};

    service.getAccess = getAccess;

    return service;

    function getAccess() {
      return iconicObject;
    }
  }

  zfIconic.$inject = ['Iconic', 'FoundationApi', '$location']

  function zfIconic(iconic, foundationApi, $location) {
    var directive = {
      restrict: 'A',
      template: '<img ng-transclude>',
      transclude: true,
      replace: true,
      scope: {
        dynSrc: '=?',
        size: '@?',
        icon: '@'
      },
      compile: compile
    };

    return directive;

    function compile() {
      return {
        pre: preLink,
        post: postLink
      };

      function preLink(scope, iElement, iAttrs, ctrl, transclude) {
        var assetPath = 'assets/img/iconic/';

        if(scope.dynSrc) {
          iAttrs.$set('data-src', scope.dynSrc);
        } else {
          // To support expressions on data-src
          if (scope.icon) {
            iAttrs.$set('data-src', assetPath + scope.icon + '.svg');
          } else {
            iAttrs.$set('data-src', iAttrs.src);
          }
        };

        var iconicClass;
        switch (scope.size) {
          case 'small':
            iconicClass = 'iconic-sm'
            break;
          case 'medium':
            iconicClass = 'iconic-md'
            break;
          case 'large':
            iconicClass = 'iconic-lg'
            break;
          default:
            iconicClass = 'iconic-fluid'
        }
        angular.element(iElement).addClass(iconicClass);
      }

      function postLink(scope, element, attrs) {
        var ico = iconic.getAccess();

        ico.inject(element[0], {
          each: function(injectedElem) {
            if ($location.$$html5) {
              scope.$emit('$zfIconicInjected', injectedElem);
            }
          }
        });

        foundationApi.subscribe('resize', function() {
          ico.update();
        });
      }
    }
  }

})();
<commit_msg>Updates for handling dynamic icon changes<commit_after>(function() {
  'use strict';

  angular.module('foundation.iconic', [])
    .service('Iconic', iconic)
    .directive('zfIconic', zfIconic)
  ;

  //iconic wrapper
  function iconic() {
    var iconicObject = IconicJS();

    var service = {};

    service.getAccess = getAccess;

    return service;

    function getAccess() {
      return iconicObject;
    }
  }

  zfIconic.$inject = ['Iconic', 'FoundationApi', '$compile', '$location']

  function zfIconic(iconic, foundationApi, $compile, $location) {
    var directive = {
      restrict: 'A',
      template: '<img ng-transclude>',
      transclude: true,
      replace: true,
      scope: {
        dynSrc: '=?',
        dynIcon: '=?',
        size: '@?',
        icon: '@'
      },
      compile: compile
    };

    return directive;

    function compile() {
      var contents, assetPath = 'assets/img/iconic/';

      return {
        pre: preLink,
        post: postLink
      };

      function preLink(scope, iElement, iAttrs, ctrl, transclude) {
        if(scope.dynSrc) {
          iAttrs.$set('data-src', scope.dynSrc);
        } else if(scope.dynIcon) {
          iAttrs.$set('data-src', assetPath + scope.dynIcon + '.svg');
        } else {
          if (scope.icon) {
            iAttrs.$set('data-src', assetPath + scope.icon + '.svg');
          } else {
            // To support expressions on data-src
            iAttrs.$set('data-src', iAttrs.src);
          }
        }

        var iconicClass;
        switch (scope.size) {
          case 'small':
            iconicClass = 'iconic-sm'
            break;
          case 'medium':
            iconicClass = 'iconic-md'
            break;
          case 'large':
            iconicClass = 'iconic-lg'
            break;
          default:
            iconicClass = 'iconic-fluid'
        }
        angular.element(iElement).addClass(iconicClass);

        // save contents of un-inject html, to use for dynamic re-injection
        contents = iElement[0].outerHTML;
      }

      function postLink(scope, element, attrs) {
        var svgElement, ico = iconic.getAccess();

        injectSvg(element[0]);

        foundationApi.subscribe('resize', function() {
          // only run update on current element
          ico.update(element[0]);
        });

        // handle dynamic updating of icon
        if(scope.dynSrc) {
          scope.$watch('dynSrc', function(newVal, oldVal) {
            if (newVal && newVal != oldVal) {
              reinjectSvg(scope.dynSrc);
            }
          });
        }
        if (scope.dynIcon) {
          scope.$watch('dynIcon', function(newVal, oldVal) {
            if (newVal && newVal != oldVal) {
              reinjectSvg(assetPath + scope.dynIcon + '.svg');
            }
          });
        }

        function reinjectSvg(newSrc) {
          if (svgElement) {
            // set html
            svgElement.empty();
            svgElement.append(angular.element(contents));

            // set new source
            svgElement.attr('data-src', newSrc);

            // reinject
            injectSvg(svgElement[0]);
          }
        }

        function injectSvg(element) {
          ico.inject(element, {
            each: function(injectedElem) {
              var angElem = angular.element(injectedElem);
              svgElement = $compile(angElem)(angElem.scope());
            }
          });
        }
      }
    }
  }

})();<|endoftext|>