<commit_before>import { fnapp, fnbind, fnstate, findElement, h } from 'https://cdn.jsdelivr.net/npm/fntags@0.2.3/src/fntags.min.js'

let data = fnstate( [] )

function random( max ) { return Math.round( Math.random() * 1000 ) % max }

const A = [ 'pretty', 'large', 'big', 'small', 'tall', 'short', 'long', 'handsome', 'plain', 'quaint', 'clean',
    'elegant', 'easy', 'angry', 'crazy', 'helpful', 'mushy', 'odd', 'unsightly', 'adorable', 'important', 'inexpensive',
    'cheap', 'expensive', 'fancy' ]
const C = [ 'red', 'yellow', 'blue', 'green', 'pink', 'brown', 'purple', 'brown', 'white', 'black', 'orange' ]
const N = [ 'table', 'chair', 'house', 'bbq', 'desk', 'car', 'pony', 'cookie', 'sandwich', 'burger', 'pizza', 'mouse',
    'keyboard' ]

let nextId = 1

function buildData( count ) {
    const newData = new Array( count )
    for( let i = 0; i < count; i++ ) {
        newData[ i ] = fnstate( {
                                    id: nextId++,
                                    label: `${A[ random( A.length ) ]} ${C[ random( C.length ) ]} ${N[ random( N.length ) ]}`
                                } )
    }
    return newData
}

const Button = ( id, title, onclick ) =>
    h( 'div', { class: 'col-sm-6 smallpad' },
       h( 'button', { id, type: 'button', class: 'btn btn-primary btn-block', onclick: onclick }, title )
    )

const cache = new Map()

let selected = fnstate(null)

const row = ( item ) => {
    if( !item || !item() ) return ''
    if( cache.has( item().id ) ) return cache.get(item().id)
    let label = h( 'a', {
        onclick: () => {
            if( selected() ) selected().className = ''
            tr.className = 'danger'
            selected(tr)
        }
    }, item().label )
    let id = h( 'td', { class: 'col-md-1' }, item().id )
    let tr = h( 'tr', { id: item().id.toString(), class: selected() && selected().getAttribute("id") === item().id ? 'danger' : '' },
                id,
                h( 'td', { class: 'col-md-4' }, label ),
                h( 'td', {
                       class: 'col-md-1',
                       onclick: ( e ) => {
                           e.preventDefault()
                           tr.replaceWith( '' )
                           item( null )
                           tr = null
                       }
                   },
                   h( 'a',
                      h( 'span', { class: 'glyphicon glyphicon-remove', 'aria-hidden': 'true' } )
                   )
                ),
                h( 'td', { class: 'col-md-6' } )
    )
    cache.set( item().id, fnbind(
        item,
        tr,
        () => {
            label.innerText = item().label
            id.innerText = item().id
        }
    ) )
    return cache.get( item().id )
}

fnapp( document.body,
       h( 'div', { class: 'container' },
          h( 'div', { class: 'jumbotron' },
             h( 'div', { class: 'row' },
                h( 'div', { class: 'col-md-6' },
                   h( 'h1', 'fntags keyed' )
                ),
                h( 'div', { class: 'col-md-6' },
                   h( 'div', { class: 'row' },
                      Button( 'run', 'Create 1,000 rows', () => data( buildData( 1000 ) ) ),
                      Button( 'runlots', 'Create 10,000 rows', () => data( buildData( 10000 ) ) ),
                      Button( 'add', 'Append 1,000 rows', () => {
                          data( data().concat( buildData( 1000 ) ) )
                      } ),
                      Button( 'update', 'Update every 10th row', () => {
                          for( let i = 0; i < data().length; i += 10 ) {
                              data()[ i ].patch( { label: data()[ i ]().label + ' !!!' } )
                          }
                      } ),
                      Button( 'clear', 'Clear', () => data( [] ) && cache.clear() ) ,
                      Button( 'swaprows', 'Swap Rows', () => {
                          const theData = data()
                          if( theData.length > 998 ) {

                              let rowa = findElement( theData[ 1 ], el => el.getAttribute( 'id' ) == theData[ 1 ]().id )
                              let rowb = findElement( theData[ 998 ], el => el.getAttribute( 'id' ) == theData[ 998 ]().id )

                              const a = theData[ 1 ]
                              theData[ 1 ] = theData[ 998 ]
                              theData[ 998 ] = a
                              let sib = rowa.nextSibling
                              let parent = rowa.parentNode
                              parent.insertBefore( rowa, rowb )
                              parent.insertBefore( rowb, sib )
                          }
                      } )
                   )
                )
             )
          )
       ),
       h( 'table', { class: 'table table-hover table-striped test-data' },
          fnbind( data, h( 'tbody' ), ( el ) => {
              while( el.firstChild ) {
                  el.removeChild( el.firstChild )
              }
              el.append( ...data().map( row ) )
          } )
       ),
       h( 'span', { class: 'preloadicon glyphicon glyphicon-remove', 'aria-hidden': 'true' } )
)<commit_msg>put back resetStat<commit_after>import { findElement, fnapp, fnbind, fnstate, h, resetState } from 'https://cdn.jsdelivr.net/npm/fntags@0.2.3/src/fntags.min.js'

let data = fnstate( [] )

function random( max ) { return Math.round( Math.random() * 1000 ) % max }

const A = [ 'pretty', 'large', 'big', 'small', 'tall', 'short', 'long', 'handsome', 'plain', 'quaint', 'clean',
    'elegant', 'easy', 'angry', 'crazy', 'helpful', 'mushy', 'odd', 'unsightly', 'adorable', 'important', 'inexpensive',
    'cheap', 'expensive', 'fancy' ]
const C = [ 'red', 'yellow', 'blue', 'green', 'pink', 'brown', 'purple', 'brown', 'white', 'black', 'orange' ]
const N = [ 'table', 'chair', 'house', 'bbq', 'desk', 'car', 'pony', 'cookie', 'sandwich', 'burger', 'pizza', 'mouse',
    'keyboard' ]

let nextId = 1

function buildData( count ) {
    const newData = new Array( count )
    for( let i = 0; i < count; i++ ) {
        newData[ i ] = fnstate( {
                                    id: nextId++,
                                    label: `${A[ random( A.length ) ]} ${C[ random( C.length ) ]} ${N[ random( N.length ) ]}`
                                } )
    }
    return newData
}

const Button = ( id, title, onclick ) =>
    h( 'div', { class: 'col-sm-6 smallpad' },
       h( 'button', { id, type: 'button', class: 'btn btn-primary btn-block', onclick: onclick }, title )
    )

const cache = new Map()

let selected = fnstate(null)

const row = ( item ) => {
    if( !item || !item() ) return ''
    if( cache.has( item().id ) ) return cache.get(item().id)
    let label = h( 'a', {
        onclick: () => {
            if( selected() ) selected().className = ''
            tr.className = 'danger'
            selected(tr)
        }
    }, item().label )
    let id = h( 'td', { class: 'col-md-1' }, item().id )
    let tr = h( 'tr', { id: item().id.toString(), class: selected() && selected().getAttribute("id") === item().id ? 'danger' : '' },
                id,
                h( 'td', { class: 'col-md-4' }, label ),
                h( 'td', {
                       class: 'col-md-1',
                       onclick: ( e ) => {
                           e.preventDefault()
                           tr.replaceWith( '' )
                           item( null )
                           resetState(item)
                           tr = null
                       }
                   },
                   h( 'a',
                      h( 'span', { class: 'glyphicon glyphicon-remove', 'aria-hidden': 'true' } )
                   )
                ),
                h( 'td', { class: 'col-md-6' } )
    )
    cache.set( item().id, fnbind(
        item,
        tr,
        () => {
            label.innerText = item().label
            id.innerText = item().id
        }
    ) )
    return cache.get( item().id )
}

fnapp( document.body,
       h( 'div', { class: 'container' },
          h( 'div', { class: 'jumbotron' },
             h( 'div', { class: 'row' },
                h( 'div', { class: 'col-md-6' },
                   h( 'h1', 'fntags keyed' )
                ),
                h( 'div', { class: 'col-md-6' },
                   h( 'div', { class: 'row' },
                      Button( 'run', 'Create 1,000 rows', () => data( buildData( 1000 ) ) ),
                      Button( 'runlots', 'Create 10,000 rows', () => data( buildData( 10000 ) ) ),
                      Button( 'add', 'Append 1,000 rows', () => {
                          data( data().concat( buildData( 1000 ) ) )
                      } ),
                      Button( 'update', 'Update every 10th row', () => {
                          for( let i = 0; i < data().length; i += 10 ) {
                              data()[ i ].patch( { label: data()[ i ]().label + ' !!!' } )
                          }
                      } ),
                      Button( 'clear', 'Clear', () => data( [] ) && cache.clear() ) ,
                      Button( 'swaprows', 'Swap Rows', () => {
                          const theData = data()
                          if( theData.length > 998 ) {

                              let rowa = findElement( theData[ 1 ], el => el.getAttribute( 'id' ) == theData[ 1 ]().id )
                              let rowb = findElement( theData[ 998 ], el => el.getAttribute( 'id' ) == theData[ 998 ]().id )

                              const a = theData[ 1 ]
                              theData[ 1 ] = theData[ 998 ]
                              theData[ 998 ] = a
                              let sib = rowa.nextSibling
                              let parent = rowa.parentNode
                              parent.insertBefore( rowa, rowb )
                              parent.insertBefore( rowb, sib )
                          }
                      } )
                   )
                )
             )
          )
       ),
       h( 'table', { class: 'table table-hover table-striped test-data' },
          fnbind( data, h( 'tbody' ), ( el ) => {
              while( el.firstChild ) {
                  el.removeChild( el.firstChild )
              }
              el.append( ...data().map( row ) )
          } )
       ),
       h( 'span', { class: 'preloadicon glyphicon glyphicon-remove', 'aria-hidden': 'true' } )
)<|endoftext|>


<commit_before>const babelLoader = function (conf) {
  return conf.loader === 'babel'
}

function rewire(config, env) {
  const babelrc = config.module.loaders.find(babelLoader).query
  babelrc.plugins = [
    'styled-jsx/babel',
    ['module-resolver', { 'root': ['src'] }],
  ].concat(babelrc.plugins || [])

  return config
}

module.exports = rewire
<commit_msg>Добавить tcomb<commit_after>const babelLoader = function (conf) {
  return conf.loader === 'babel'
}

function rewire(config, env) {
  const babelrc = config.module.loaders.find(babelLoader).query
  babelrc.plugins = [
    'styled-jsx/babel',
    ['module-resolver', { 'root': ['src'] }],
    'tcomb',
  ].concat(babelrc.plugins || [])

  return config
}

module.exports = rewire
<|endoftext|>


<commit_before>module("event");

(function(){
	var notYetReady, noEarlyExecution,
		order = [],
		args = {};

	notYetReady = !jQuery.isReady;

	test("jQuery.isReady", function() {
		expect(2);

		equal(notYetReady, true, "jQuery.isReady should not be true before DOM ready");
		equal(jQuery.isReady, true, "jQuery.isReady should be true once DOM is ready");
	});

	// Create an event handler.
	function makeHandler( testId ) {
		// When returned function is executed, push testId onto `order` array
		// to ensure execution order. Also, store event handler arg to ensure
		// the correct arg is being passed into the event handler.
		return function( arg ) {
			order.push(testId);
			args[testId] = arg;
		};
	}

	// Bind to the ready event in every possible way.
	jQuery(makeHandler("a"));
	jQuery(document).ready(makeHandler("b"));
	jQuery(document).on("ready.readytest", makeHandler("c"));

	// Do it twice, just to be sure.
	jQuery(makeHandler("d"));
	jQuery(document).ready(makeHandler("e"));
	jQuery(document).on("ready.readytest", makeHandler("f"));

	noEarlyExecution = order.length === 0;

	// This assumes that QUnit tests are run on DOM ready!
	test("jQuery ready", function() {
		expect(10);

		ok(noEarlyExecution, "Handlers bound to DOM ready should not execute before DOM ready");

		// Ensure execution order.
		deepEqual(order, ["a", "b", "d", "e", "c", "f"], "Bound DOM ready handlers should execute in on-order, but those bound with jQuery(document).on( 'ready', fn ) will always execute last");

		// Ensure handler argument is correct.
		equal(args["a"], jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery");
		equal(args["b"], jQuery, "Argument passed to fn in jQuery(document).ready( fn ) should be jQuery");
		ok(args["c"] instanceof jQuery.Event, "Argument passed to fn in jQuery(document).on( 'ready', fn ) should be an event object");

		order = [];

		// Now that the ready event has fired, again bind to the ready event
		// in every possible way. These event handlers should execute immediately.
		jQuery(makeHandler("g"));
		equal(order.pop(), "g", "Event handler should execute immediately");
		equal(args["g"], jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery");

		jQuery(document).ready(makeHandler("h"));
		equal(order.pop(), "h", "Event handler should execute immediately");
		equal(args["h"], jQuery, "Argument passed to fn in jQuery(document).ready( fn ) should be jQuery");

		jQuery(document).on("ready.readytest", makeHandler("never"));
		equal(order.length, 0, "Event handler should never execute since DOM ready has already passed");

		// Cleanup.
		jQuery(document).off("ready.readytest");
	});

})();
<commit_msg>Refine isReady tests for D's reduced complexity<commit_after>module('event');

(function() {
	var order = [];

	// Create an event handler.
	var _makeHandler = function(testId) {
		// When returned function is executed, push testId onto `order` array
		// to ensure execution order.
		return function(arg) {
			order.push(testId);
		};
	};

	D(_makeHandler('a'));
	D(_makeHandler('b'));

	noEarlyExecution = order.length === 0;

	// This assumes that QUnit tests are run on DOM ready!
	test('Ready', function() {
		expect(10);

		ok(noEarlyExecution, 'Handlers bound to DOM ready should not execute before DOM ready');

		// Ensure execution order.
		deepEqual(order, ['a', 'b'], 'Bound DOM ready handlers should execute in on-order');

		// Ensure handler argument is correct.
		equal(args.a, D, 'Argument passed to fn in D(fn) should be D');
		equal(args.b, D, 'Argument passed to fn in D(fn) should be D');

		order = [];

		// Now that the ready event has fired, again bind to the ready event.
		// These event handlers should execute immediately.
		D(_makeHandler('g'));
		equal(order.pop(), 'g', 'Event handler should execute immediately');
		equal(args.g, D, 'Argument passed to fn in D(fn) should be D');

	});

})();
<|endoftext|>


<commit_before>jsio('import Class, log, jsio.logging');
jsio('import jsio.std.utf8 as utf8');
jsio('from jsio.protocols.buffered import BufferedProtocol');

var loggers = {};
loggers.stream = jsio.logging.getLogger('MSPPStream');
loggers.protocol = jsio.logging.getLogger('MSPPProtocol');
loggers.stream.setLevel(0);
loggers.protocol.setLevel(0);

var frames = {
    'OPEN':  0,
    'CLOSE': 1,
    'DATA':  2
};

exports.MSPPStream = Class(function() {
    this.setMultiplexer = function(multiplexer) {
        loggers.stream.debug('setMultiplexer: '+multiplexer);
        this.multiplexer = multiplexer;
    }

    this.setEncoding = function(encoding) {
        loggers.stream.debug('setEncoding: '+encoding);
        this.encoding = encoding;
    }

    this.open = function(host, port, isBinary) {
        if (isBinary)
            this.encoding = 'utf8';
        this.id = this.multiplexer.openStream(this, host, port);
        loggers.stream.debug('open '+this.id+": "+host+" "+port+" "+isBinary);
    }

    this.close = function() {
        loggers.stream.debug('close '+this.id);
        this.multiplexer.close(this.id);
    }

    this.send = function(data, encoding) {
        loggers.stream.debug('send '+this.id+": "+data+" "+encoding);
        if ((encoding || this.encoding) == 'utf8')
            data = utf8.encode(data);
        this.multiplexer.writeToStream(this.id, data);
    }

    this._onreadraw = function(data) {
        if (this.encoding == 'utf8')
            data = utf8.decode(data);
        this.onread(data);
    }

    this.onopen = function() {}
    this.onclose = function(err) {}
    this.onread = function(data) {}
});

var state = {};
state.closed = 0;
state.connecting = 1;
state.consuming = 2;

exports.MSPPProtocol = Class(BufferedProtocol, function(supr) {
    this.init = function() {
        loggers.protocol.debug('new MSPPProtocol');
        supr(this, 'init', []);
        this.state = state.closed;
        this.transportType = null;
        this.transportOptions = null;
        this.currentId = 0;
        this.streams = {};
        this.writeBuffer = [];
    }

    this.setTransport = function(transportType, transportOptions) {
        this.transportType = transportType;
        this.transportOptions = transportOptions;
    }

    this.connectionMade = function(isReconnect) {
        loggers.protocol.debug('connectionMade');
        this.state = state.consuming;
        for (var i = 0; i < this.writeBuffer.length; i++)
            this._write(this.writeBuffer[i]);
        writeBuffer = [];
    }

    this.connectionLost = this.connectionFailed = function(reason) {
        loggers.protocol.debug('closed: '+reason);
        this.state = state.closed;
        for (stream in this.streams)
            this.streams[stream].onclose(reason);
    }

    this.openStream = function(stream, host, port) {
        if (this.state == state.closed) {
            this.state = state.connecting;
            jsio.connect(this, this.transportType, this.transportOptions);
        }
        var id = ++this.currentId;
        this.streams[id] = stream;
        this._write([id, frames.OPEN, host, port]);
        return id;
    }

    this.closeStream = function(id) {
        this._write([id, frames.CLOSE]);
    }

    this.writeToStream = function(id, data) {
        this._write([id, frames.DATA, data]);
    }

    this.bufferUpdated = function() {
        if (this.state != state.consuming)
            throw new Error("buffer update in invalid MSPP state: "+this.state);
        while (1) {
            if (! this.buffer.hasDelimiter('['))
                break;
            var frameLength = this.buffer.peekToDelimiter('[');
            var frameLengthLength = frameLength.length;
            frameLength = parseInt(frameLength);
            if (! this.buffer.hasBytes(frameLengthLength + frameLength))
                break;
            this.buffer.consumeBytes(frameLengthLength);
            var data = this.buffer.consumeBytes(frameLength);
            loggers.protocol.debug('read: '+data);
            data = JSON.parse(data);
            switch(data[1]) {
                case frames.OPEN:
                    this.streams[data[0]].onopen();
                    break;
                case frames.CLOSE:
                    this.streams[data[0]].onclose(data[2]);
                    break;
                case frames.DATA:
                    this.streams[data[0]]._onreadraw(data[2]);
                    break;
                default:
                    throw new Error('invalid MSPP data type!');
            }
        }
    }

    this._write = function(data) {
        if (this.state != state.consuming) {
            loggers.protocol.debug("buffering write: "+data);
            this.writeBuffer.push(data);
            return;
        }
        data = JSON.stringify(data);
        data = data.length + data;
        loggers.protocol.debug('write: '+data);
        this.transport.write(data);
    }
});
<commit_msg>mspp protocol update: fixes unicode; reduces bandwidth.<commit_after>jsio('import Class, log, jsio.logging');
jsio('import jsio.std.utf8 as utf8');
jsio('from jsio.protocols.buffered import BufferedProtocol');

/*
works like this:
OPEN
upstream:
length_after_colon:id,0host,port

downstream:
length_after_colon:id,0
-----
CLOSE
upstream:
length_after_colon:id,1

downstream:
length_after_colon:id,1errcode
-----
DATA
upstream/downstream:
length_after_colon:id,2datadatadata
*/

var loggers = {};
loggers.stream = jsio.logging.getLogger('MSPPStream');
loggers.protocol = jsio.logging.getLogger('MSPPProtocol');
loggers.stream.setLevel(0);
loggers.protocol.setLevel(0);

var frames = {
    'OPEN':  0,
    'CLOSE': 1,
    'DATA':  2
};

exports.MSPPStream = Class(function() {
    this.setMultiplexer = function(multiplexer) {
        loggers.stream.debug('setMultiplexer: '+multiplexer);
        this.multiplexer = multiplexer;
    }

    this.setEncoding = function(encoding) {
        loggers.stream.debug('setEncoding: '+encoding);
        this.encoding = encoding;
    }

    this.open = function(host, port, isBinary) {
        if (isBinary)
            this.encoding = 'utf8';
        this.id = this.multiplexer.openStream(this, host, port);
        loggers.stream.debug('open '+this.id+": "+host+" "+port+" "+isBinary);
    }

    this.close = function() {
        loggers.stream.debug('close '+this.id);
        this.multiplexer.close(this.id);
    }

    this.send = function(data, encoding) {
        loggers.stream.debug('send '+this.id+": "+data+" "+encoding);
        if ((encoding || this.encoding) == 'utf8')
            data = utf8.encode(data);
        this.multiplexer.writeToStream(this.id, data);
    }

    this._onreadraw = function(data) {
        if (this.encoding == 'utf8')
            data = utf8.decode(data);
		loggers.stream.debug('_onreadraw '+data);
        this.onread(data);
    }

    this.onopen = function() {}
    this.onclose = function(err) {}
    this.onread = function(data) {}
});

var state = {};
state.closed = 0;
state.connecting = 1;
state.consuming = 2;

exports.MSPPProtocol = Class(BufferedProtocol, function(supr) {
    this.init = function() {
        loggers.protocol.debug('new MSPPProtocol');
        supr(this, 'init', []);
        this.state = state.closed;
        this.transportType = null;
        this.transportOptions = null;
        this.currentId = 0;
        this.streams = {};
        this.writeBuffer = [];
    }

    this.setTransport = function(transportType, transportOptions) {
        this.transportType = transportType;
        this.transportOptions = transportOptions;
    }

    this.connectionMade = function(isReconnect) {
        loggers.protocol.debug('connectionMade');
        this.state = state.consuming;
        for (var i = 0; i < this.writeBuffer.length; i++)
            this._write(this.writeBuffer[i]);
        writeBuffer = [];
    }

    this.connectionLost = this.connectionFailed = function(reason) {
        loggers.protocol.debug('closed: '+reason);
        this.state = state.closed;
        for (stream in this.streams)
            this.streams[stream].onclose(reason);
    }

    this.openStream = function(stream, host, port) {
        if (this.state == state.closed) {
            this.state = state.connecting;
            jsio.connect(this, this.transportType, this.transportOptions);
        }
        var id = ++this.currentId;
        this.streams[id] = stream;
        this._write([id, frames.OPEN, host+","+port]);
        return id;
    }

    this.closeStream = function(id) {
        this._write([id, frames.CLOSE, ""]);
    }

    this.writeToStream = function(id, data) {
        this._write([id, frames.DATA, data]);
    }

    this.bufferUpdated = function() {
		loggers.protocol.debug("bufferUpdated. state: "+this.state+". buffer: "+this.buffer._rawBuffer);
        if (this.state != state.consuming)
            throw new Error("buffer update in invalid MSPP state: "+this.state);
		if (! this.buffer.hasDelimiter(':'))
			return;
		var lStr = this.buffer.peekToDelimiter(':');
		var len = parseInt(lStr);
		if (! this.buffer.hasBytes(len + lStr.length + 1))
			return;
		this.buffer.consumeThroughDelimiter(':');
		var streamId = this.buffer.consumeToDelimiter(',');
		this.buffer.consumeBytes(1);
		var frameType = parseInt(this.buffer.consumeBytes(1));
		len -= (streamId.length + 2);
		streamId = parseInt(streamId);
		var data = this.buffer.consumeBytes(len);
		switch(frameType) {
			case frames.OPEN:
				this.streams[streamId].onopen();
				break;
			case frames.CLOSE:
				this.streams[streamId].onclose(data);
				break;
			case frames.DATA:
				this.streams[streamId]._onreadraw(data);
				break;
			default:
				throw new Error('invalid MSPP data type!');
		}
    }

    this._write = function(data) {
        if (this.state != state.consuming) {
            loggers.protocol.debug("buffering write: "+data);
            this.writeBuffer.push(data);
            return;
        }
		var s = data[0] + "," + data[1] + data[2];
		s = s.length + ":" + s;
        loggers.protocol.debug('write: '+s);
        this.transport.write(s);
    }
});
<|endoftext|>


<commit_before>APP_CONFIG = {
	autoCompleteSources: {
		to: [
			'window',
			'.container-box',
			'.navbar',
			'ul li',
			'p',
			'.content-head',
			'.pure-u-1-2',
			'input',
			'button',
			'pre',
			'#welcome',
			'#tryit',
			'.splash-head',
			'.splash-subhead',
			'l-box-lrg',
			'.content-subhead'
		],
		if: [
			'click',
			'dblclick',
			'mousedown',
			'mouseenter',
			'mouseleave',
			'mouseover',
			'mouseup',
			'scroll',
			'resize',
			'focus',
			'change',
			'contextmenu',
			'keypress',
			'animationend',
			'orientationchange'
		],
		to: [

		],
		do: [
          'bounce',
          'flash',
          'pulse',
          'rubberBand',
          'shake',
          'swing',
          'tada',
          'wobble',
          'bounceIn',
          'bounceInLeft',
          'bounceInRight',
          'bounceOut',
          'bounceOutDown',
          'bounceOutUp',
          'fadeIn',
          'fadeInLeftBig',
          'fadeInUpBig',
          'fadeOut',
          'fadeOutDown',
          'fadeOutRight',
          'flip',
          'flipInX',
          'flipOutY',
          'lightSpeedIn',
          'rotateIn',
          'rotateInDownRight',
          'rotateInUpRight',
          'rotateOut',
          'rotateOutDownRight',
          'rotateOutUpRight',
          'slideInDown',
          'slideOutRight',
          'slideOutUp',
          'hinge',
          'rollIn',
          'rollOut'

		]
	},
  colorSquares: [
    'blue-bg tada animated infinite',
    'orange-bg',
    'pink-bg',
    'green-bg',
    'pink-bg',
    'green-bg',
    'gray-bg',
    'blue-bg',
    'orange-bg',
    'pink-lgh-bg',
    'blue-lgh-bg',
    'orange-lgh-bg',
    'green-lgh-bg',
    'orange-lgh-bg',
    'green-bg',
    'yellow-lgh-bg',
  ],
  behaviorSquares: [
    { do:'flipInY animated', to:'.container-box' },
    { do:'$toggleClass invisible', to:'.demo-toggle' },
    { do:'rollOut animated', to:'.demo1' },
    { do:'hinge animated', to:'h3' },
    { do:'rotateIn animated', to:'$parent target' },
    { do:'$toggleClass pink-lgh-bg', to:'.demo1' },
    { do:'$toggleClass gray-bg', to:'$ancestors target' },
    { do:'$removeClass gray-bg', to:'$ancestors target' },
    { do:'pulse animated', to:'$children .content-wrapper' },
    { do:'hinge animated', to:'$find body | .fa' },
    { do:'bounceIn animated', to:'$ancestors target | .container-box' },
    { do:'flip animated', to:'$closest target' },
    { do:'rubberBand animated', to:'.container-box' },
    { do:'rollIn animated', to:'$children .container-box' },
    { do:'tada animated', to:'.container-box' },
    { do:'wobble animated', to:'.container-box' },
  ]
};
<commit_msg>Reorganizing examples<commit_after>APP_CONFIG = {
	autoCompleteSources: {
		to: [
			'window',
			'.container-box',
			'.navbar',
			'ul li',
			'p',
			'.content-head',
			'.pure-u-1-2',
			'input',
			'button',
			'pre',
			'#welcome',
			'#tryit',
			'.splash-head',
			'.splash-subhead',
			'l-box-lrg',
			'.content-subhead'
		],
		if: [
			'click',
			'dblclick',
			'mousedown',
			'mouseenter',
			'mouseleave',
			'mouseover',
			'mouseup',
			'scroll',
			'resize',
			'focus',
			'change',
			'contextmenu',
			'keypress',
			'animationend',
			'orientationchange'
		],
		to: [

		],
		do: [
          'bounce',
          'flash',
          'pulse',
          'rubberBand',
          'shake',
          'swing',
          'tada',
          'wobble',
          'bounceIn',
          'bounceInLeft',
          'bounceInRight',
          'bounceOut',
          'bounceOutDown',
          'bounceOutUp',
          'fadeIn',
          'fadeInLeftBig',
          'fadeInUpBig',
          'fadeOut',
          'fadeOutDown',
          'fadeOutRight',
          'flip',
          'flipInX',
          'flipOutY',
          'lightSpeedIn',
          'rotateIn',
          'rotateInDownRight',
          'rotateInUpRight',
          'rotateOut',
          'rotateOutDownRight',
          'rotateOutUpRight',
          'slideInDown',
          'slideOutRight',
          'slideOutUp',
          'hinge',
          'rollIn',
          'rollOut'

		]
	},
  colorSquares: [
    'blue-bg tada animated infinite',
    'orange-bg',
    'pink-bg',
    'green-bg',
    'pink-bg',
    'green-bg',
    'gray-bg',
    'blue-bg',
    'orange-bg',
    'pink-lgh-bg',
    'blue-lgh-bg',
    'orange-lgh-bg',
    'green-lgh-bg',
    'orange-lgh-bg',
    'green-bg',
    'yellow-lgh-bg',
  ],
  behaviorSquares: [
    { do:'flipInY animated', to:'.container-box' },
    { do:'rollIn animated', to:'$children .container-box' },
    { do:'tada animated', to:'.container-box' },
    { do:'pulse animated', to:'$children .content-wrapper' },
    { do:'hinge animated', to:'$find body | .fa' },
    { do:'bounceIn animated', to:'$ancestors target | .container-box' },
    { do:'flip animated', to:'$closest target' },
    { do:'rubberBand animated', to:'.container-box' },
    { do:'wobble animated', to:'.container-box' },
    { do:'rollOut animated', to:'.demo1' },
    { do:'hinge animated', to:'h3' },
    { do:'rotateIn animated', to:'$parent target' },
    { do:'$toggleClass invisible', to:'.demo-toggle' },
    { do:'$toggleClass pink-lgh-bg', to:'.demo1' },
    { do:'$toggleClass gray-bg', to:'$ancestors target' },
    { do:'$removeClass gray-bg', to:'$ancestors target' },
  ]
};
<|endoftext|>


<commit_before>app.factory('AuthFactory', ['$http', '$firebaseAuth', '$location', function($http, $firebaseAuth, $location){
  console.log('AuthFactory running');

  var auth = $firebaseAuth();
  var currentUser = {};
  var loggedIn = false;
  var userStatus = {};

  function logIn(userType) {
    return auth.$signInWithPopup("google").then(function(firebaseUser) {
      currentUser = firebaseUser.user;
      console.log('USER TYPE:', userType);
      if(currentUser) {
        getUser(currentUser, userType);
      }
    });
  }

  function getUser(currentUser, userType){
    currentUser.getToken().then(function(idToken){
      // console.log('ID TOKEN:', idToken);
      $http({
        method: 'GET',
        url: '/users.route',
        headers: {
          id_token: idToken,
          type: userType
        }
      })
      .then(function(response) {
        console.log(response.data);
        userStatus.userType = response.data.userStatus.userType;
        userStatus.userId = response.data.userStatus.userId;

        // if they are a new mentor go straight to profile

        if(userStatus.userType === 'mentor'){
          // BioFactory.setMentorId(userStatus.userId);
          var newUser = response.data.userStatus.newUser;
          if(newUser === true){
              userStatus.newUser = true;
              $location.path("profile");
          }
          console.log("USER STATUS:", userStatus);
        }

      });
      userStatus.isLoggedIn = true;
    });

  }

  auth.$onAuthStateChanged(function(firebaseUser){

    // firebaseUser will be null if not logged in
    currentUser = firebaseUser;
    if(currentUser) {
      getUser(currentUser);
    } else {
      userStatus.isLoggedIn = false;
    }

  });

  function logOut() {
    return auth.$signOut().then(function() {
      currentUser = undefined;
      userStatus.isLoggedIn = false;
      userStatus.userType = "None";
      $location.path("home");
      userStatus.newUser = false;
      console.log('logged out');
      console.log('currentUser: ', currentUser);
    });
  }

  // TODO: Have Oliver explain what these variables are
  var publicApi = {
    auth: auth,
    userStatus: userStatus,
    currentUser: currentUser.user,
    logIn: function(userType) {
      return logIn(userType);
    },
    logOut: function() {
      return logOut();
    }
  };

  return publicApi;

}]);
<commit_msg>Comment out unnecessary line in auth factory<commit_after>app.factory('AuthFactory', ['$http', '$firebaseAuth', '$location', function($http, $firebaseAuth, $location){
  console.log('AuthFactory running');

  var auth = $firebaseAuth();
  var currentUser = {};
  var loggedIn = false;
  var userStatus = {};

  function logIn(userType) {
    return auth.$signInWithPopup("google").then(function(firebaseUser) {
      currentUser = firebaseUser.user;
      console.log('USER TYPE:', userType);
      if(currentUser) {
        getUser(currentUser, userType);
      }
    });
  }

  function getUser(currentUser, userType){
    currentUser.getToken().then(function(idToken){
      // console.log('ID TOKEN:', idToken);
      $http({
        method: 'GET',
        url: '/users.route',
        headers: {
          id_token: idToken,
          type: userType
        }
      })
      .then(function(response) {
        console.log(response.data);
        userStatus.userType = response.data.userStatus.userType;
        userStatus.userId = response.data.userStatus.userId;

        // if they are a new mentor go straight to profile

        if(userStatus.userType === 'mentor'){
          // BioFactory.setMentorId(userStatus.userId);
          var newUser = response.data.userStatus.newUser;
          if(newUser === true){
              userStatus.newUser = true;
              // $location.path("profile");
          }
          console.log("USER STATUS:", userStatus);
        }

      });
      userStatus.isLoggedIn = true;
    });

  }

  auth.$onAuthStateChanged(function(firebaseUser){

    // firebaseUser will be null if not logged in
    currentUser = firebaseUser;
    if(currentUser) {
      getUser(currentUser);
    } else {
      userStatus.isLoggedIn = false;
    }

  });

  function logOut() {
    return auth.$signOut().then(function() {
      currentUser = undefined;
      userStatus.isLoggedIn = false;
      userStatus.userType = "None";
      $location.path("home");
      userStatus.newUser = false;
      console.log('logged out');
      console.log('currentUser: ', currentUser);
    });
  }

  // TODO: Have Oliver explain what these variables are
  var publicApi = {
    auth: auth,
    userStatus: userStatus,
    currentUser: currentUser.user,
    logIn: function(userType) {
      return logIn(userType);
    },
    logOut: function() {
      return logOut();
    }
  };

  return publicApi;

}]);
<|endoftext|>


<commit_before>
erpnext.SerialNoBatchSelector = Class.extend({
	init: function(opts, show_dialog) {
		$.extend(this, opts);
		this.show_dialog = show_dialog;
		// frm, item, warehouse_details, has_batch, oldest
		let d = this.item;
		if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) {
			this.has_batch = 1;
			this.setup();
		// !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined
		} else if(d && d.has_serial_no && !(this.show_dialog == false)) {
			this.has_batch = 0;
			this.setup();
		}
	},

	setup: function() {
		this.item_code = this.item.item_code;
		this.qty = this.item.qty;
		this.make_dialog();
		this.on_close_dialog();
	},

	make_dialog: function() {
		var me = this;

		this.data = this.oldest ? this.oldest : [];
		let title = "";
		let fields = [
			{
				fieldname: 'item_code',
				read_only: 1,
				fieldtype:'Link',
				options: 'Item',
				label: __('Item Code'),
				default: me.item_code
			},
			{fieldtype:'Column Break'},
			{
				fieldname: 'warehouse',
				fieldtype:'Link',
				options: 'Warehouse',
				label: __(me.warehouse_details.type),
				default: me.warehouse_details.name,
				onchange: function(e) {
					me.warehouse_details.name = this.get_value();
					var batches = this.layout.fields_dict.batches;
					if(batches) {
						batches.grid.df.data = [];
						batches.grid.refresh();
						batches.grid.add_new_row(null, null, null);
					}
				},
				get_query: function() {
					return {
						query: "erpnext.controllers.queries.warehouse_query",
						filters: [
							["Bin", "item_code", "=", me.item_code],
							["Warehouse", "is_group", "=", 0],
							["Warehouse", "company", "=", me.frm.doc.company]
						]
					}
				}
			},
			{fieldtype:'Column Break'},
			{
				fieldname: 'qty',
				fieldtype:'Float',
				read_only: me.has_batch,
				label: __(me.has_batch ? 'Total Qty' : 'Qty'),
				default: 0
			},
			{
				fieldname: 'auto_fetch_button',
				fieldtype:'Button',
				hidden: me.has_batch,
				label: __('Fetch based on FIFO'),
				click: () => {
					let qty = this.dialog.fields_dict.qty.get_value();
					let numbers = frappe.call({
						method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number",
						args: {
							qty: qty,
							item_code: me.item_code,
							warehouse: me.warehouse_details.name
						}
					});

					numbers.then((data) => {
						let auto_fetched_serial_numbers = data.message;
						let records_length = auto_fetched_serial_numbers.length;
						if (records_length < qty) {
							frappe.msgprint(`Fetched only ${records_length} serial numbers.`);
						}
						let serial_no_list_field = this.dialog.fields_dict.serial_no;
						numbers = auto_fetched_serial_numbers.join('\n');
						serial_no_list_field.set_value(numbers);
					});
				}
			}
		];

		if (this.has_batch) {
			title = __("Select Batch Numbers");
			fields = fields.concat(this.get_batch_fields());
		} else {
			title = __("Select Serial Numbers");
			fields = fields.concat(this.get_serial_no_fields());
		}

		this.dialog = new frappe.ui.Dialog({
			title: title,
			fields: fields
		});

		if (this.item.serial_no) {
			this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
		}

		this.dialog.set_primary_action(__('Insert'), function() {
			me.values = me.dialog.get_values();
			if(me.validate()) {
				me.set_items();
				me.dialog.hide();
			}
		});

		if(this.show_dialog) {
			let d = this.item;
			if (d.has_serial_no && d.serial_no) {
				this.dialog.set_value('serial_no', d.serial_no);
			}

			if (d.batch_no) {
				this.frm.doc.items.forEach(data => {
					if(data.item_code == d.item_code) {
						this.dialog.fields_dict.batches.df.data.push({
							'batch_no': data.batch_no,
							'actual_qty': data.actual_qty,
							'selected_qty': data.qty,
							'available_qty': data.actual_batch_qty
						});
					}
				});
				this.dialog.fields_dict.batches.grid.refresh();
			}
		}

		if (this.has_batch) {
			this.update_total_qty();
		}

		this.dialog.show();
	},

	on_close_dialog: function() {
		this.dialog.get_close_btn().on('click', () => {
			this.on_close && this.on_close(this.item);
		});
	},

	validate: function() {
		let values = this.values;
		if(!values.warehouse) {
			frappe.throw(__("Please select a warehouse"));
			return false;
		}
		if(this.has_batch) {
			if(values.batches.length === 0 || !values.batches) {
				frappe.throw(__("Please select batches for batched item "
					+ values.item_code));
				return false;
			}
			values.batches.map((batch, i) => {
				if(!batch.selected_qty || batch.selected_qty === 0 ) {
					if (!this.show_dialog) {
						frappe.throw(__("Please select quantity on row " + (i+1)));
						return false;
					}
				}
			});
			return true;

		} else {
			let serial_nos = values.serial_no || '';
			if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
				if (!this.show_dialog) {
					frappe.throw(__("Please enter serial numbers for serialized item "
						+ values.item_code));
					return false;
				}
			}
			return true;
		}
	},

	set_items: function() {
		var me = this;
		if(this.has_batch) {
			this.values.batches.map((batch, i) => {
				let batch_no = batch.batch_no;
				let row = '';

				if (i !== 0 && !this.batch_exists(batch_no)) {
					row = this.frm.add_child("items", {
						'item_code': this.item.item_code,
						'item_name': this.item.item_name,
						'price_list_rate': this.item.price_list_rate,
						'rate': this.item.rate,
						'qty': batch.selected_qty,
						'batch_no': batch_no,
						'actual_qty': this.item.actual_qty,
						'discount_percentage': this.item.discount_percentage
					});
				} else {
					row = this.frm.doc.items.find(i => i.batch_no === batch_no);
				}

				if (!row) {
					row = this.item;
				}

				this.map_row_values(row, batch, 'batch_no',
					'selected_qty', this.values.warehouse);
			});
		} else {
			this.map_row_values(this.item, this.values, 'serial_no', 'qty');
		}

		refresh_field("items");
		this.callback && this.callback(this.item);
	},

	batch_exists: function(batch) {
		const batches = this.frm.doc.items.map(data => data.batch_no);
		return (batches && in_list(batches, batch)) ? true : false;
	},

	map_row_values: function(row, values, number, qty_field, warehouse) {
		row.qty = values[qty_field];
		row.transfer_qty = flt(values[qty_field]) * flt(row.conversion_factor);
		row[number] = values[number];
		if(this.warehouse_details.type === 'Source Warehouse') {
			row.s_warehouse = values.warehouse || warehouse;
		} else if(this.warehouse_details.type === 'Target Warehouse') {
			row.t_warehouse = values.warehouse || warehouse;
		} else {
			row.warehouse = values.warehouse || warehouse;
		}
	},

	update_total_qty: function() {
		let qty_field = this.dialog.fields_dict.qty;
		let total_qty = 0;

		this.dialog.fields_dict.batches.df.data.forEach(data => {
			total_qty += flt(data.selected_qty);
		});

		qty_field.set_input(total_qty);
	},

	get_batch_fields: function() {
		var me = this;
		return [
			{fieldtype:'Section Break', label: __('Batches')},
			{fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'),
				fields: [
					{
						'fieldtype': 'Link',
						'read_only': 0,
						'fieldname': 'batch_no',
						'options': 'Batch',
						'label': __('Select Batch'),
						'in_list_view': 1,
						get_query: function () {
							return {
								filters: { item: me.item_code },
								query: 'erpnext.controllers.queries.get_batch_numbers'
							};
						},
						change: function (e) {
							let val = this.get_value();
							if (val.length === 0) {
								this.grid_row.on_grid_fields_dict
									.available_qty.set_value(0);
								return;
							}
							let selected_batches = this.grid.grid_rows.map((row) => {
								if (row === this.grid_row) {
									return "";
								}

								if (row.on_grid_fields_dict.batch_no) {
									return row.on_grid_fields_dict.batch_no.get_value();
								}
							});
							if (selected_batches.includes(val)) {
								this.set_value("");
								frappe.throw(__(`Batch ${val} already selected.`));
								return;
							}
							if (me.warehouse_details.name) {
								frappe.call({
									method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
									args: {
										batch_no: this.doc.batch_no,
										warehouse: me.warehouse_details.name,
										item_code: me.item_code
									},
									callback: (r) => {
										this.grid_row.on_grid_fields_dict
											.available_qty.set_value(r.message || 0);
									}
								});

							} else {
								this.set_value("");
								frappe.throw(__(`Please select a warehouse to get available
									quantities`));
							}
							// e.stopImmediatePropagation();
						}
					},
					{
						'fieldtype': 'Float',
						'read_only': 1,
						'fieldname': 'available_qty',
						'label': __('Available'),
						'in_list_view': 1,
						'default': 0,
						change: function () {
							this.grid_row.on_grid_fields_dict.selected_qty.set_value('0');
						}
					},
					{
						'fieldtype': 'Float',
						'read_only': 0,
						'fieldname': 'selected_qty',
						'label': __('Qty'),
						'in_list_view': 1,
						'default': 0,
						change: function (e) {
							var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value();
							var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value();
							var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value();

							if (batch_no.length === 0 && parseInt(selected_qty) !== 0) {
								frappe.throw(__("Please select a batch"));
							}
							if (me.warehouse_details.type === 'Source Warehouse' &&
								parseFloat(available_qty) < parseFloat(selected_qty)) {

								this.set_value('0');
								frappe.throw(__(`For transfer from source, selected quantity cannot be
									greater than available quantity`));
							} else {
								this.grid.refresh();
							}

							me.update_total_qty();
						}
					},
				],
				in_place_edit: true,
				data: this.data,
				get_data: function () {
					return this.data;
				},
			}
		];
	},

	get_serial_no_fields: function() {
		var me = this;
		this.serial_list = [];

		let serial_no_filters = {
			item_code: me.item_code,
			delivery_document_no: ""
		}

		if (me.warehouse_details.name) {
			serial_no_filters['warehouse'] = me.warehouse_details.name;
		}
		return [
			{fieldtype: 'Section Break', label: __('Serial Numbers')},
			{
				fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No',
				label: __('Select to add Serial Number.'),
				get_query: function() {
					return {
						filters: serial_no_filters
					};
				},
				onchange: function(e) {
					if(this.in_local_change) return;
					this.in_local_change = 1;

					let serial_no_list_field = this.layout.fields_dict.serial_no;
					let qty_field = this.layout.fields_dict.qty;

					let new_number = this.get_value();
					let list_value = serial_no_list_field.get_value();
					let new_line = '\n';
					if(!list_value) {
						new_line = '';
					} else {
						me.serial_list = list_value.replace(/\n/g, ' ').match(/\S+/g) || [];
					}

					if(!me.serial_list.includes(new_number)) {
						this.set_new_description('');
						serial_no_list_field.set_value(me.serial_list.join('\n') + new_line + new_number);
						me.serial_list = serial_no_list_field.get_value().replace(/\n/g, ' ').match(/\S+/g) || [];
					} else {
						this.set_new_description(new_number + ' is already selected.');
					}

					qty_field.set_input(me.serial_list.length);
					this.$input.val("");
					this.in_local_change = 0;
				}
			},
			{fieldtype: 'Column Break'},
			{
				fieldname: 'serial_no',
				fieldtype: 'Small Text',
				label: __(me.has_batch ? 'Selected Batch Numbers' : 'Selected Serial Numbers'),
				onchange: function() {
					me.serial_list = this.get_value()
						.replace(/\n/g, ' ').match(/\S+/g) || [];
					this.layout.fields_dict.qty.set_input(me.serial_list.length);
				}
			}
		];
	}
});
<commit_msg>fix: Codacy<commit_after>
erpnext.SerialNoBatchSelector = Class.extend({
	init: function(opts, show_dialog) {
		$.extend(this, opts);
		this.show_dialog = show_dialog;
		// frm, item, warehouse_details, has_batch, oldest
		let d = this.item;
		if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) {
			this.has_batch = 1;
			this.setup();
		// !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined
		} else if(d && d.has_serial_no && !(this.show_dialog == false)) {
			this.has_batch = 0;
			this.setup();
		}
	},

	setup: function() {
		this.item_code = this.item.item_code;
		this.qty = this.item.qty;
		this.make_dialog();
		this.on_close_dialog();
	},

	make_dialog: function() {
		var me = this;

		this.data = this.oldest ? this.oldest : [];
		let title = "";
		let fields = [
			{
				fieldname: 'item_code',
				read_only: 1,
				fieldtype:'Link',
				options: 'Item',
				label: __('Item Code'),
				default: me.item_code
			},
			{fieldtype:'Column Break'},
			{
				fieldname: 'warehouse',
				fieldtype:'Link',
				options: 'Warehouse',
				label: __(me.warehouse_details.type),
				default: me.warehouse_details.name,
				onchange: function(e) {
					me.warehouse_details.name = this.get_value();
					var batches = this.layout.fields_dict.batches;
					if(batches) {
						batches.grid.df.data = [];
						batches.grid.refresh();
						batches.grid.add_new_row(null, null, null);
					}
				},
				get_query: function() {
					return {
						query: "erpnext.controllers.queries.warehouse_query",
						filters: [
							["Bin", "item_code", "=", me.item_code],
							["Warehouse", "is_group", "=", 0],
							["Warehouse", "company", "=", me.frm.doc.company]
						]
					}
				}
			},
			{fieldtype:'Column Break'},
			{
				fieldname: 'qty',
				fieldtype:'Float',
				read_only: me.has_batch,
				label: __(me.has_batch ? 'Total Qty' : 'Qty'),
				default: 0
			},
			{
				fieldname: 'auto_fetch_button',
				fieldtype:'Button',
				hidden: me.has_batch,
				label: __('Fetch based on FIFO'),
				click: () => {
					let qty = this.dialog.fields_dict.qty.get_value();
					let numbers = frappe.call({
						method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number",
						args: {
							qty: qty,
							item_code: me.item_code,
							warehouse: me.warehouse_details.name
						}
					});

					numbers.then((data) => {
						let auto_fetched_serial_numbers = data.message;
						let records_length = auto_fetched_serial_numbers.length;
						if (records_length < qty) {
							frappe.msgprint(`Fetched only ${records_length} serial numbers.`);
						}
						let serial_no_list_field = this.dialog.fields_dict.serial_no;
						numbers = auto_fetched_serial_numbers.join('\n');
						serial_no_list_field.set_value(numbers);
					});
				}
			}
		];

		if (this.has_batch) {
			title = __("Select Batch Numbers");
			fields = fields.concat(this.get_batch_fields());
		} else {
			title = __("Select Serial Numbers");
			fields = fields.concat(this.get_serial_no_fields());
		}

		this.dialog = new frappe.ui.Dialog({
			title: title,
			fields: fields
		});

		if (this.item.serial_no) {
			this.dialog.fields_dict.serial_no.set_value(this.item.serial_no);
		}

		this.dialog.set_primary_action(__('Insert'), function() {
			me.values = me.dialog.get_values();
			if(me.validate()) {
				me.set_items();
				me.dialog.hide();
			}
		});

		if(this.show_dialog) {
			let d = this.item;
			if (d.has_serial_no && d.serial_no) {
				this.dialog.set_value('serial_no', d.serial_no);
			}

			if (d.batch_no) {
				this.frm.doc.items.forEach(data => {
					if(data.item_code == d.item_code) {
						this.dialog.fields_dict.batches.df.data.push({
							'batch_no': data.batch_no,
							'actual_qty': data.actual_qty,
							'selected_qty': data.qty,
							'available_qty': data.actual_batch_qty
						});
					}
				});
				this.dialog.fields_dict.batches.grid.refresh();
			}
		}

		if (this.has_batch) {
			this.update_total_qty();
		}

		this.dialog.show();
	},

	on_close_dialog: function() {
		this.dialog.get_close_btn().on('click', () => {
			this.on_close && this.on_close(this.item);
		});
	},

	validate: function() {
		let values = this.values;
		if(!values.warehouse) {
			frappe.throw(__("Please select a warehouse"));
			return false;
		}
		if(this.has_batch) {
			if(values.batches.length === 0 || !values.batches) {
				frappe.throw(__("Please select batches for batched item "
					+ values.item_code));
				return false;
			}
			values.batches.map((batch, i) => {
				if(!batch.selected_qty || batch.selected_qty === 0 ) {
					if (!this.show_dialog) {
						frappe.throw(__("Please select quantity on row " + (i+1)));
						return false;
					}
				}
			});
			return true;

		} else {
			let serial_nos = values.serial_no || '';
			if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
				if (!this.show_dialog) {
					frappe.throw(__("Please enter serial numbers for serialized item "
						+ values.item_code));
					return false;
				}
			}
			return true;
		}
	},

	set_items: function() {
		var me = this;
		if(this.has_batch) {
			this.values.batches.map((batch, i) => {
				let batch_no = batch.batch_no;
				let row = '';

				if (i !== 0 && !this.batch_exists(batch_no)) {
					row = this.frm.add_child("items", {
						'item_code': this.item.item_code,
						'item_name': this.item.item_name,
						'price_list_rate': this.item.price_list_rate,
						'rate': this.item.rate,
						'qty': batch.selected_qty,
						'batch_no': batch_no,
						'actual_qty': this.item.actual_qty,
						'discount_percentage': this.item.discount_percentage
					});
				} else {
					row = this.frm.doc.items.find(i => i.batch_no === batch_no);
				}

				if (!row) {
					row = this.item;
				}

				this.map_row_values(row, batch, 'batch_no',
					'selected_qty', this.values.warehouse);
			});
		} else {
			this.map_row_values(this.item, this.values, 'serial_no', 'qty');
		}

		refresh_field("items");
		this.callback && this.callback(this.item);
	},

	batch_exists: function(batch) {
		const batches = this.frm.doc.items.map(data => data.batch_no);
		return (batches && in_list(batches, batch)) ? true : false;
	},

	map_row_values: function(row, values, number, qty_field, warehouse) {
		row.qty = values[qty_field];
		row.transfer_qty = flt(values[qty_field]) * flt(row.conversion_factor);
		row[number] = values[number];
		if(this.warehouse_details.type === 'Source Warehouse') {
			row.s_warehouse = values.warehouse || warehouse;
		} else if(this.warehouse_details.type === 'Target Warehouse') {
			row.t_warehouse = values.warehouse || warehouse;
		} else {
			row.warehouse = values.warehouse || warehouse;
		}
	},

	update_total_qty: function() {
		let qty_field = this.dialog.fields_dict.qty;
		let total_qty = 0;

		this.dialog.fields_dict.batches.df.data.forEach(data => {
			total_qty += flt(data.selected_qty);
		});

		qty_field.set_input(total_qty);
	},

	get_batch_fields: function() {
		var me = this;
		return [
			{fieldtype:'Section Break', label: __('Batches')},
			{fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'),
				fields: [
					{
						'fieldtype': 'Link',
						'read_only': 0,
						'fieldname': 'batch_no',
						'options': 'Batch',
						'label': __('Select Batch'),
						'in_list_view': 1,
						get_query: function () {
							return {
								filters: { item: me.item_code },
								query: 'erpnext.controllers.queries.get_batch_numbers'
							};
						},
						change: function () {
							let val = this.get_value();
							if (val.length === 0) {
								this.grid_row.on_grid_fields_dict
									.available_qty.set_value(0);
								return;
							}
							let selected_batches = this.grid.grid_rows.map((row) => {
								if (row === this.grid_row) {
									return "";
								}

								if (row.on_grid_fields_dict.batch_no) {
									return row.on_grid_fields_dict.batch_no.get_value();
								}
							});
							if (selected_batches.includes(val)) {
								this.set_value("");
								frappe.throw(__(`Batch ${val} already selected.`));
								return;
							}
							if (me.warehouse_details.name) {
								frappe.call({
									method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
									args: {
										batch_no: this.doc.batch_no,
										warehouse: me.warehouse_details.name,
										item_code: me.item_code
									},
									callback: (r) => {
										this.grid_row.on_grid_fields_dict
											.available_qty.set_value(r.message || 0);
									}
								});

							} else {
								this.set_value("");
								frappe.throw(__(`Please select a warehouse to get available
									quantities`));
							}
							// e.stopImmediatePropagation();
						}
					},
					{
						'fieldtype': 'Float',
						'read_only': 1,
						'fieldname': 'available_qty',
						'label': __('Available'),
						'in_list_view': 1,
						'default': 0,
						change: function () {
							this.grid_row.on_grid_fields_dict.selected_qty.set_value('0');
						}
					},
					{
						'fieldtype': 'Float',
						'read_only': 0,
						'fieldname': 'selected_qty',
						'label': __('Qty'),
						'in_list_view': 1,
						'default': 0,
						change: function () {
							var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value();
							var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value();
							var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value();

							if (batch_no.length === 0 && parseInt(selected_qty) !== 0) {
								frappe.throw(__("Please select a batch"));
							}
							if (me.warehouse_details.type === 'Source Warehouse' &&
								parseFloat(available_qty) < parseFloat(selected_qty)) {

								this.set_value('0');
								frappe.throw(__(`For transfer from source, selected quantity cannot be
									greater than available quantity`));
							} else {
								this.grid.refresh();
							}

							me.update_total_qty();
						}
					},
				],
				in_place_edit: true,
				data: this.data,
				get_data: function () {
					return this.data;
				},
			}
		];
	},

	get_serial_no_fields: function() {
		var me = this;
		this.serial_list = [];

		let serial_no_filters = {
			item_code: me.item_code,
			delivery_document_no: ""
		}

		if (me.warehouse_details.name) {
			serial_no_filters['warehouse'] = me.warehouse_details.name;
		}
		return [
			{fieldtype: 'Section Break', label: __('Serial Numbers')},
			{
				fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No',
				label: __('Select to add Serial Number.'),
				get_query: function() {
					return {
						filters: serial_no_filters
					};
				},
				onchange: function(e) {
					if(this.in_local_change) return;
					this.in_local_change = 1;

					let serial_no_list_field = this.layout.fields_dict.serial_no;
					let qty_field = this.layout.fields_dict.qty;

					let new_number = this.get_value();
					let list_value = serial_no_list_field.get_value();
					let new_line = '\n';
					if(!list_value) {
						new_line = '';
					} else {
						me.serial_list = list_value.replace(/\n/g, ' ').match(/\S+/g) || [];
					}

					if(!me.serial_list.includes(new_number)) {
						this.set_new_description('');
						serial_no_list_field.set_value(me.serial_list.join('\n') + new_line + new_number);
						me.serial_list = serial_no_list_field.get_value().replace(/\n/g, ' ').match(/\S+/g) || [];
					} else {
						this.set_new_description(new_number + ' is already selected.');
					}

					qty_field.set_input(me.serial_list.length);
					this.$input.val("");
					this.in_local_change = 0;
				}
			},
			{fieldtype: 'Column Break'},
			{
				fieldname: 'serial_no',
				fieldtype: 'Small Text',
				label: __(me.has_batch ? 'Selected Batch Numbers' : 'Selected Serial Numbers'),
				onchange: function() {
					me.serial_list = this.get_value()
						.replace(/\n/g, ' ').match(/\S+/g) || [];
					this.layout.fields_dict.qty.set_input(me.serial_list.length);
				}
			}
		];
	}
});
<|endoftext|>


<commit_before>'use strict';

angular.module('app')
    .controller('rgiDocumentTableCtrl', ['_', '$scope', '$rootScope', 'rgiDialogFactory', 'rgiAssessmentSrvc', 'rgiDocumentSrvc', 'rgiHttpResponseProcessorSrvc', 'rgiNotifier', function (
        _,
        $scope,
        $rootScope,
        rgiDialogFactory,
        rgiAssessmentSrvc,
        rgiDocumentSrvc,
        rgiHttpResponseProcessorSrvc,
        rgiNotifier
    ) {
        var currentPage, totalPages, limit = 100;

        $scope.busy = false;
        $scope.assessment_filter_options = [];
        $scope.assessment_filter = '';

        rgiAssessmentSrvc.query({}, function (assessments) {
            if(assessments.reason) {
                rgiNotifier.error('No assessments');
            } else {
                assessments.forEach(function(assessment) {
                    $scope.assessment_filter_options.push({
                        value: assessment.assessment_ID,
                        text: assessment.country + ' ' + assessment.year + ' ' + assessment.version
                    });
                });
            }
        });

        $scope.$watch('assessment_filter', function(assessment) {
            currentPage = 0;
            totalPages = 0;
            var searchOptions = {skip: currentPage, limit: limit};

            if(assessment) {
                searchOptions.assessments = assessment;
            }

            rgiDocumentSrvc.queryCached(searchOptions, function (response) {
                if(response.reason) {
                    rgiNotifier.error('Load document data failure');
                } else {
                    $scope.count = response.count;
                    $scope.documents = response.data;

                    if($scope.documents.length === 0) {
                        rgiNotifier.error('No documents uploaded');
                    }

                    totalPages = Math.ceil(response.count / limit);
                    currentPage = 1;
                }
            }, rgiHttpResponseProcessorSrvc.getDefaultHandler('Load document data failure'));
        });

        $scope.loadMoreDocs = function() {
            if ($scope.busy) {
                return;
            }

            $scope.busy = true;

            if(currentPage < totalPages) {
                var searchOptions = {skip: currentPage, limit: limit};

                if($scope.assessment_filter) {
                    searchOptions.assessments = $scope.assessment_filter;
                }

                rgiDocumentSrvc.query(searchOptions, function (response) {
                    if(response.reason) {
                        rgiNotifier.error('Documents loading failure');
                    } else {
                        $scope.documents = _.union($scope.documents, response.data);
                        currentPage = currentPage + 1;
                        $scope.busy = false;
                    }
                }, rgiHttpResponseProcessorSrvc.getDefaultHandler('Load document data failure'));
            }
        };

        $scope.deleteDocument = function(doc) {
            rgiDialogFactory.deleteDocument($scope, doc);
        };

        $scope.unlink = function(doc) {
            rgiDialogFactory.unlinkDocument($scope, doc);
        };
    }]);
<commit_msg>SAVEPOINT<commit_after>'use strict';

angular.module('app')
    .controller('rgiDocumentTableCtrl', function (
        _,
        $scope,
        $rootScope,
        rgiDialogFactory,
        rgiAssessmentSrvc,
        rgiDocumentSrvc,
        rgiHttpResponseProcessorSrvc,
        rgiIdentitySrvc,
        rgiNotifier
    ) {
        var limit = 100,
            currentPage = 0,
            totalPages = 0;

        $scope.current_user = rgiIdentitySrvc.currentUser;
        $scope.busy = false;

        $scope.assessment_filter_options = [
            {value: 'all', text: 'Show all documents'}
            // {value: 'type', text: 'Sort by document type'},
            // {value: 'assessments', text: 'Sort by attached assessments'}
        ];

        $scope.assessment_filter = $scope.assessment_filter_options[0].value;

        rgiAssessmentSrvc.query({}, function (assessments) {
            if(assessments.reason) {
                rgiNotifier.error('No assessments');
            } else {
                assessments.forEach(function(assessment) {
                    console.log(assessment);
                    $scope.assessment_filter_options.push({
                        value: assessment.assessment_ID,
                        text: assessment.country + ' ' + assessment.year + ' ' + assessment.version
                    });
                });
            }
        });

        rgiDocumentSrvc.query({skip: currentPage, limit: limit}, function (response) {
            if(response.reason) {
                rgiNotifier.error('Load document data failure');
            } else {
                $scope.count = response.count;
                $scope.documents = response.data;
                totalPages = Math.ceil(response.count / limit);
                currentPage = currentPage + 1;
            }
        }, rgiHttpResponseProcessorSrvc.getDefaultHandler('Load document data failure'));

        $scope.loadMoreDocs = function() {
            if ($scope.busy) {
                return;
            }

            $scope.busy = true;

            if(currentPage < totalPages) {
                rgiDocumentSrvc.query({skip: currentPage, limit: limit}, function (response) {
                    if(response.reason) {
                        rgiNotifier.error('Documents loading failure');
                    } else {
                        $scope.documents = _.union($scope.documents, response.data);
                        currentPage = currentPage + 1;
                        $scope.busy = false;
                    }
                }, rgiHttpResponseProcessorSrvc.getDefaultHandler('Load document data failure'));
            }
        };

        $scope.deleteDocument = function(doc) {
            rgiDialogFactory.deleteDocument($scope, doc);
        };
    });
<|endoftext|>


<commit_before>$(function () {
  $('[data-toggle="tooltip"]').tooltip()
})
$('#logs').on('show.bs.modal', function (event) {
  var link = $(event.relatedTarget);
  var stage = link.data('stage');

  $.ajax('/api/stages/' + stage + '/logs', {
        dataType: "json",
        success: function (array) {
            var dest = $('#logs').find('.modal-body');
            console.log('success ' + array);
            $.each(array, function(index, element) {
                dest.append($('' + element + '
'));
            });
        }
    });
})

// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0
hashCode = function(s) {
  var h = 0, l = s.length, i = 0;
  if ( l > 0 )
    while (i < l)
      h = (h << 5) - h + s.charCodeAt(i++) | 0;
  return h;
};

var dashboard = dashboard || {};

dashboard = {
    init: function () {
        this.bootstrap.init();
        this.stages.init();
        this.feedback.init();
    },

    bootstrap: {
        init: function () {
            $('#search').on('keyup', dashboard.stages.filter);
            $('#category').on('change', dashboard.stages.filter);
        }
    },

    stages: {
        init: function () {
            dashboard.stages.reload();
            $('[data-action]').on('click', dashboard.stages.action);
            $('#loading').remove();
        },

        reload: function () {
            $.ajax('/api/stages?select=apps,comment,expire,last-modified-by,running,urls', {
                dataType: "json",
                success: function (data) {
                    var allStages = $('#all-stages');
                    var done = [];
                    $.each(data, function (name, status) {
                        var name;
                        var oldTr;
                        var up;
                        var htmlSt;
                        var htmlName;
                        var htmlUrls;
                        var htmlRestart;
                        var htmlMenu;
                        var newTr;
                        var newHash;
                        var actions;

                        done.push(name);
                        oldTr = allStages.find('[data-name="' + name + '"]');
                        up = status.running !== "";
                        htmlSt = "\n" +
                                 "
\n" +
                                 "    " + (up ? "up" : "down") + "\n"
                                 "
\n" +
                                 "	";
                        htmlName = "\n" +
                                   "  " + name + "	";
                        htmlUrls = "";
                        $.each(status.urls, function (app, url) {
                            htmlUrls = htmlUrls + "" + app + "
\n"
                        })
                        htmlUrls = "" + htmlUrls + "	\n"
                        htmlRestart = " \n" +
                                      "   <button class='btn btn-light btn-sm' type='button' data-action='restart' data-stage='" + name + "'>\n" +
                                      "      Restart\n" +
                                      "   </button>\n"
                                      " 	"
                        htmlMenu = "\n" +
                                   "
\n" +
                                   "    <button type='button' class='btn btn-light btn-sm dropdown-toggle' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>More</button>\n" +
                                   "
\n" +
                                   "      Start\n" +
                                   "      Stop\n" +
                                   "      Expire in 1 week\n" +
                                   "      \n" +
                                   "         Share\n"
                                   "      Log files ...\n" +
                                   "      Remove\n"
                                   "
\n" +
                                   "
\n" +
                                   "	\n"

                        newTr = "
\n" +
                                   htmlSt +
                                   htmlName +
                                   htmlUrls +
                                   "" + status.apps + "	\n" +
                                   "" + status.expire + "	\n" +
                                   "" + status["last-modified-by"] + "	\n" +
                                   htmlRestart +
                                   htmlMenu
                                   "
";
                        newHash = String(hashCode(newTr));
                        if (oldTr.length === 0) {
                            // new stage
                            console.log("append " + name)
                            var appended = allStages.append(newTr);
                            appended = appended.find('[data-name="' + name + '"]');
                            appended.attr("data-content-hash", newHash);
                            appended.find('[data-action]').on('click', dashboard.stages.action);
                        } else if ($(oldTr).attr("data-content-hash") !== newHash) {
                            // updated stage
                            console.log("replace " + name)
                            oldTr.replaceWith(newTr); // replaceWith returns the removed elements
                            replaced = allStages.find('[data-name="' + name + '"]');
                            replaced.attr("data-content-hash", newHash);
                            replaced.find('[data-action]').on('click', dashboard.stages.action);
                        } else {
                            console.log("same " + name)
                            // no changes
                        }
                    });
                    $(allStages).children("tr").each(function (i, tr) {
                        var name;

                        name = $(tr).attr("data-name");
                        if (!done.includes(name)) {
                            $(tr).find('[data-action]').off('click', dashboard.stages.action);
                            tr.remove();
                        }
                    });
                    $('[data-toggle="popover"]').popover();
                    dashboard.stages.filter();
                }
            });
        },

        action: function () {
            var stage, action, arguments, i, url, box;
            if ($(this).parent().hasClass('disabled') && $(this).attr('data-stage') === null) {
                return false;
            }

            stage = $(this).attr('data-stage');
            action = $(this).attr('data-action');
            arguments = $(this).attr('data-arguments');

            $('#' + stage + ' a.action').addClass('disabled');
            box = $('#progress');
            box.modal('show');
            box.find('.modal-header').html("
" + action + " " + stage + "
");

            if (action == "restart") {
                $.post("/api/stages/" + stage + "/stop").fail(function (r) {
                    box.find('.modal-body').html('
failed: ' + r + '
');
                    dashboard.stages.reload();
                }).done(function (r) {
                    $.post("/api/stages/" + stage + "/start").fail(function (r) {
                        box.find('.modal-body').html('
failed: ' + r + '
');
                        dashboard.stages.reload();
                    }).done(function (r) {
                        // TODO: doesn't work if the browser is extremely slow (or busy)
                        // from https://stackoverflow.com/questions/51637199/bootstrap-4-open-modal-a-close-modal-a-open-modal-b-a-not-closing
                        setTimeout( function() { box.modal("hide"); }, 500 );
                        dashboard.stages.reload();
                    });
                });
            } else {
                url = "/api/stages/" + stage + "/" + action;
                if (arguments != null) {
                    url = url + "?" + arguments;
                }
                $.post(url).fail(function (r) {
                    box.find('.modal-body').html('
failed: ' + r + '
');
                    dashboard.stages.reload();
                }).done(function (r) {
                    // TODO: doesn't work if the browser is extremely slow (or busy)
                    // from https://stackoverflow.com/questions/51637199/bootstrap-4-open-modal-a-close-modal-a-open-modal-b-a-not-closing
                    setTimeout( function() { box.modal("hide"); }, 500 );
                    dashboard.stages.reload();
                });
            }
            return false;
        },

        filter: function () {
            var search = $('#search').val().toLowerCase();
            var category = $('#category').val().toLowerCase();

            found = 0;
            $('#all-stages').find('tr.stage').each(function (idx, tr) {
                $(this).toggle(true);
                found++;
            });

            $('#empty').toggle(found == 0);
        }
    },

    feedback: {
        init: function () {
            $('#feedback-submit').on('click', function (e) {
                e.preventDefault();
                var text = $('#feedback-text');
                if (text.val() !== "") {
                    $.post("/ui/feedback", { message: text.val()})
                        .done(function () {
                            text.parent().parent().prepend("
Thanks.
");
                            text.val("");
                        })
                        .fail(function () {
                            text.parent().parent()
                                .prepend("
Opps, something went wrong =(.
");
                        });
                }
            });
        }
    }
};
dashboard.init();
<commit_msg>status fix<commit_after>$(function () {
  $('[data-toggle="tooltip"]').tooltip()
})
$('#logs').on('show.bs.modal', function (event) {
  var link = $(event.relatedTarget);
  var stage = link.data('stage');

  $.ajax('/api/stages/' + stage + '/logs', {
        dataType: "json",
        success: function (array) {
            var dest = $('#logs').find('.modal-body');
            console.log('success ' + array);
            $.each(array, function(index, element) {
                dest.append($('' + element + '
'));
            });
        }
    });
})

// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0
hashCode = function(s) {
  var h = 0, l = s.length, i = 0;
  if ( l > 0 )
    while (i < l)
      h = (h << 5) - h + s.charCodeAt(i++) | 0;
  return h;
};

var dashboard = dashboard || {};

dashboard = {
    init: function () {
        this.bootstrap.init();
        this.stages.init();
        this.feedback.init();
    },

    bootstrap: {
        init: function () {
            $('#search').on('keyup', dashboard.stages.filter);
            $('#category').on('change', dashboard.stages.filter);
        }
    },

    stages: {
        init: function () {
            dashboard.stages.reload();
            $('[data-action]').on('click', dashboard.stages.action);
            $('#loading').remove();
        },

        reload: function () {
            $.ajax('/api/stages?select=apps,comment,expire,last-modified-by,running,urls', {
                dataType: "json",
                success: function (data) {
                    var allStages = $('#all-stages');
                    var done = [];
                    $.each(data, function (name, status) {
                        var name;
                        var oldTr;
                        var up;
                        var htmlSt;
                        var htmlName;
                        var htmlUrls;
                        var htmlRestart;
                        var htmlMenu;
                        var newTr;
                        var newHash;
                        var actions;

                        done.push(name);
                        oldTr = allStages.find('[data-name="' + name + '"]');
                        up = status.running.length > 0;
                        htmlSt = "\n" +
                                 "
\n" +
                                 "    " + (up ? "up" : "down") + "\n"
                                 "
\n" +
                                 "	";
                        htmlName = "\n" +
                                   "  " + name + "	";
                        htmlUrls = "";
                        $.each(status.urls, function (app, url) {
                            htmlUrls = htmlUrls + "" + app + "
\n"
                        })
                        htmlUrls = "" + htmlUrls + "	\n"
                        htmlRestart = " \n" +
                                      "   <button class='btn btn-light btn-sm' type='button' data-action='restart' data-stage='" + name + "'>\n" +
                                      "      Restart\n" +
                                      "   </button>\n"
                                      " 	"
                        htmlMenu = "\n" +
                                   "
\n" +
                                   "    <button type='button' class='btn btn-light btn-sm dropdown-toggle' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>More</button>\n" +
                                   "
\n" +
                                   "      Start\n" +
                                   "      Stop\n" +
                                   "      Expire in 1 week\n" +
                                   "      \n" +
                                   "         Share\n"
                                   "      Log files ...\n" +
                                   "      Remove\n"
                                   "
\n" +
                                   "
\n" +
                                   "	\n"

                        newTr = "
\n" +
                                   htmlSt +
                                   htmlName +
                                   htmlUrls +
                                   "" + status.apps + "	\n" +
                                   "" + status.expire + "	\n" +
                                   "" + status["last-modified-by"] + "	\n" +
                                   htmlRestart +
                                   htmlMenu
                                   "
";
                        newHash = String(hashCode(newTr));
                        if (oldTr.length === 0) {
                            // new stage
                            var appended = allStages.append(newTr);
                            appended = appended.find('[data-name="' + name + '"]');
                            appended.attr("data-content-hash", newHash);
                            appended.find('[data-action]').on('click', dashboard.stages.action);
                        } else if ($(oldTr).attr("data-content-hash") !== newHash) {
                            // updated stage
                            oldTr.replaceWith(newTr); // replaceWith returns the removed elements
                            replaced = allStages.find('[data-name="' + name + '"]');
                            replaced.attr("data-content-hash", newHash);
                            replaced.find('[data-action]').on('click', dashboard.stages.action);
                        } else {
                            // no changes
                        }
                    });
                    $(allStages).children("tr").each(function (i, tr) {
                        var name;

                        name = $(tr).attr("data-name");
                        if (!done.includes(name)) {
                            $(tr).find('[data-action]').off('click', dashboard.stages.action);
                            tr.remove();
                        }
                    });
                    $('[data-toggle="popover"]').popover();
                    dashboard.stages.filter();
                }
            });
        },

        action: function () {
            var stage, action, arguments, i, url, box;
            if ($(this).parent().hasClass('disabled') && $(this).attr('data-stage') === null) {
                return false;
            }

            stage = $(this).attr('data-stage');
            action = $(this).attr('data-action');
            arguments = $(this).attr('data-arguments');

            $('#' + stage + ' a.action').addClass('disabled');
            box = $('#progress');
            box.modal('show');
            box.find('.modal-header').html("
" + action + " " + stage + "
");

            if (action == "restart") {
                $.post("/api/stages/" + stage + "/stop").fail(function (r) {
                    box.find('.modal-body').html('
failed: ' + r + '
');
                    dashboard.stages.reload();
                }).done(function (r) {
                    $.post("/api/stages/" + stage + "/start").fail(function (r) {
                        box.find('.modal-body').html('
failed: ' + r + '
');
                        dashboard.stages.reload();
                    }).done(function (r) {
                        // TODO: doesn't work if the browser is extremely slow (or busy)
                        // from https://stackoverflow.com/questions/51637199/bootstrap-4-open-modal-a-close-modal-a-open-modal-b-a-not-closing
                        setTimeout( function() { box.modal("hide"); }, 500 );
                        dashboard.stages.reload();
                    });
                });
            } else {
                url = "/api/stages/" + stage + "/" + action;
                if (arguments != null) {
                    url = url + "?" + arguments;
                }
                $.post(url).fail(function (r) {
                    box.find('.modal-body').html('
failed: ' + r + '
');
                    dashboard.stages.reload();
                }).done(function (r) {
                    // TODO: doesn't work if the browser is extremely slow (or busy)
                    // from https://stackoverflow.com/questions/51637199/bootstrap-4-open-modal-a-close-modal-a-open-modal-b-a-not-closing
                    setTimeout( function() { box.modal("hide"); }, 500 );
                    dashboard.stages.reload();
                });
            }
            return false;
        },

        filter: function () {
            var search = $('#search').val().toLowerCase();
            var category = $('#category').val().toLowerCase();

            found = 0;
            $('#all-stages').find('tr.stage').each(function (idx, tr) {
                $(this).toggle(true);
                found++;
            });

            $('#empty').toggle(found == 0);
        }
    },

    feedback: {
        init: function () {
            $('#feedback-submit').on('click', function (e) {
                e.preventDefault();
                var text = $('#feedback-text');
                if (text.val() !== "") {
                    $.post("/ui/feedback", { message: text.val()})
                        .done(function () {
                            text.parent().parent().prepend("
Thanks.
");
                            text.val("");
                        })
                        .fail(function () {
                            text.parent().parent()
                                .prepend("
Opps, something went wrong =(.
");
                        });
                }
            });
        }
    }
};
dashboard.init();
<|endoftext|>