<commit_before>import React, { Component } from 'react';
import Highlight from 'react-highlight';
import Markdown from 'react-remarkable';

import { Layer, LayerContext } from 'react-layer-stack';

import FixedLayer from './components/FixedLayer';
import Window from './components/Window';

const styles = {
  header: {
    height: '36px',
    background: '#F6F6F6',
    borderRadius: '5px 5px 0 0',
    borderWidth: '1px',
    padding: '20px 20px 0 20px'
  },
  body: {
    height: 'auto',
    minHeight: '450px',
    background: '#FFFFFF',
    borderRadius: '0 0 5px 5px',
    padding: '20px'
  },
  footer: {
    height: '34px',
    background: '#F6F6F6',
    borderRadius: '0 0 5px 5px',
    padding: '20px 0 20px 0'
  }
};

class Demo extends Component {

  componentWillMount() {
    this.setState({counter: 1})
    setInterval(() => this.setState({counter: this.state.counter + 1}), 1000)
  }

  render() {
    return (

        <Layer id="layer_state_infobox" showInitially>{({views, displaying}) =>
          <FixedLayer>

              <Markdown>
                #### Layers displaying:
                <Highlight className="js">
                { JSON.stringify(displaying, null, '  ') }
                </Highlight>
                #### Layers registered:
                <Highlight className="js">
                { JSON.stringify(views, null, '  ') }
                </Highlight>
              </Markdown>

          </FixedLayer>}
        </Layer>
        { this.renderMovableWindow() }
        { this.renderSimpleWindow() }
        { this.renderZoom() }
        <Markdown>
          #### DEMO component data
              { JSON.stringify(this.state, null, '\t') }
          #### LAYER STATE TOGGLE
          <LayerContext id="layer_state_infobox">{({ showMe, hideMe, isActive }) => (
            <button onClick={ () => isActive ? hideMe() : showMe() }>{ isActive ? 'HIDE LAYER STATE' : 'SHOW LAYER STATE' }</button> )}
          </LayerContext>
          #### SIMPLE MODALS
          <LayerContext id="simple_window">{({ showMe }) => (
            <button onClick={ () => showMe() }>OPEN SIMPLE MODAL</button> )}
          </LayerContext>
          #### MOVABLE WINDOWS
          <LayerContext id="movable_window">{({ showMe }) => (
            <button onClick={ () => showMe() }>OPEN MOVABLE WINDOW</button> )}
          </LayerContext>

          #### LIGHTBOX
          <LayerContext id="lightbox">{({ showMe, hideMe }) => (
            <button onMouseLeave={ hideMe } onMouseEnter={ (e) => showMe({ content: `
            “Bill Clinton’s 1992 campaign was a classic example of sticky ideas at work in a difficult environment. Not only did the campaign have the normal set of complexities, Clinton himself added a few new wrinkles.”
            `, rect: e.nativeEvent.relatedTarget.getClientRects()[0] }) }>MOVE IT TO MEE</button> )}
          </LayerContext>
        </Markdown>

    );
  }

  renderZoom() {
    return (
      <Layer id="lightbox">{ ({ index, hideMe }, { content, rect: { top, left, width, height } }) =>
        <FixedLayer style={ { marginRight: '15px', marginBottom: '15px' } }>

{ content }

        </FixedLayer>
      }</Layer>
    )
  }

  renderSimpleWindow() {
    return (
      <Layer
        id="simple_window">{({index, hideMe, showMe}) => (
        <FixedLayer
          style={ { background: 'rgba(0,0,0,0.3)' } }
          onClick={ hideMe }
          zIndex={ index * 100 }>
          <Window style={{margin: 'auto'}}>

              SIPMLE MODAL

          </Window>
        </FixedLayer> )}
      </Layer>
    )
  }

  renderMovableWindow() {
    return (
      <Layer
        use={[this.state.counter]}  // data from the context
        id="movable_window">{({index, hideMe, showMe}, {
          ...rest,
          pinned = false,
          mouseDown = false,
          mouseLastPositionX = 0,
          mouseLastPositionY = 0,
          windowLeft = 400,
          windowTop = 100} = {}) => (
        <FixedLayer
          onMouseDown={() => showMe({...rest, mouseDown: true})}
          onMouseUp={() => showMe({...rest, mouseDown: false})}
          onMouseMove={({ screenX, screenY}) => {
            const newArgs = {
              mouseLastPositionX: screenX, mouseLastPositionY: screenY
            };
            if (pinned && mouseDown) {
              newArgs.windowLeft =  windowLeft + (screenX - mouseLastPositionX);
              newArgs.windowTop =  windowTop + (screenY - mouseLastPositionY);
            }
            showMe({...rest, ...newArgs})
          }}
          onClick={ hideMe }
          zIndex={ index * 100 }>
            <Window style={{ top: windowTop, left: windowLeft }}>

 mouseDown || showMe({...rest, pinned: true})}
                onMouseLeave={() => mouseDown || showMe({...rest, pinned: false})}>
                PIN TO MOVE

                <Markdown>
                  ##### Arguments:
                  <Highlight className="js">
                    { JSON.stringify(rest, null, '\t') }
                  </Highlight>
                  ##### Data from outer component (closure/context):
                  <Highlight className="js">
                    { JSON.stringify(this.state, null, '\t') }
                  </Highlight>
                </Markdown>

            </Window>
        </FixedLayer> )}
      </Layer>
    )
  }
}

export default Demo;
<commit_msg>update demo<commit_after>import React, { Component } from 'react';
import Highlight from 'react-highlight';
import Markdown from 'react-remarkable';

import { Layer, LayerContext } from 'react-layer-stack';

import FixedLayer from './components/FixedLayer';
import Window from './components/Window';

const styles = {
  header: {
    height: '36px',
    background: '#F6F6F6',
    borderRadius: '5px 5px 0 0',
    borderWidth: '1px',
    padding: '20px 20px 0 20px'
  },
  body: {
    height: 'auto',
    minHeight: '450px',
    background: '#FFFFFF',
    borderRadius: '0 0 5px 5px',
    padding: '20px'
  },
  footer: {
    height: '34px',
    background: '#F6F6F6',
    borderRadius: '0 0 5px 5px',
    padding: '20px 0 20px 0'
  }
};

class Demo extends Component {

  componentWillMount() {
    this.setState({counter: 1})
    setInterval(() => this.setState({counter: this.state.counter + 1}), 1000)
  }

  render() {
    return (

        <Layer id="layer_state_infobox" showInitially>{({views, displaying}) =>
          <FixedLayer>

              <Markdown>
                #### Layers displaying:
                <Highlight className="js">
                { JSON.stringify(displaying, null, '  ') }
                </Highlight>
                #### Layers registered:
                <Highlight className="js">
                { JSON.stringify(views, null, '  ') }
                </Highlight>
              </Markdown>

          </FixedLayer>}
        </Layer>
        { this.renderMovableWindow() }
        { this.renderSimpleWindow() }
        { this.renderZoom() }
        <Markdown>
          #### DEMO component data
              { JSON.stringify(this.state, null, '\t') }
          #### LAYER STATE TOGGLE
          <LayerContext id="layer_state_infobox">{({ showMe, hideMe, isActive }) => (
            <button onClick={ () => isActive ? hideMe() : showMe() }>{ isActive ? 'HIDE LAYER STATE' : 'SHOW LAYER STATE' }</button> )}
          </LayerContext>
          #### SIMPLE MODALS
          <LayerContext id="simple_window">{({ showMe }) => (
            <button onClick={ () => showMe() }>OPEN SIMPLE MODAL</button> )}
          </LayerContext>
          #### MOVABLE WINDOWS
          <LayerContext id="movable_window">{({ showMe }) => (
            <button onClick={ () => showMe() }>OPEN MOVABLE WINDOW</button> )}
          </LayerContext>

          #### LIGHTBOX
          <LayerContext id="lightbox">{({ showMe, hideMe }) => (
            <button onMouseLeave={ hideMe } onMouseEnter={ (e) => showMe({ content: `
            “Bill Clinton’s 1992 campaign was a classic example of sticky ideas at work in a difficult environment. Not only did the campaign have the normal set of complexities, Clinton himself added a few new wrinkles.”
            `, rect: e.nativeEvent.relatedTarget.getClientRects()[0] }) }>MOVE IT TO MEE</button> )}
          </LayerContext>
        </Markdown>

    );
  }

  renderZoom() {
    return (
      <Layer use={ [this.state.counter] } id="lightbox">{ ({ index, hideMe }, { content, rect: { top, left, width, height } }) =>
        <FixedLayer style={ { marginRight: '15px', marginBottom: '15px' } }>

{ content } But following is more important: { JSON.stringify(this.state) }

        </FixedLayer>
      }</Layer>
    )
  }

  renderSimpleWindow() {
    return (
      <Layer
        id="simple_window">{({index, hideMe, showMe}) => (
        <FixedLayer
          style={ { background: 'rgba(0,0,0,0.3)' } }
          onClick={ hideMe }
          zIndex={ index * 100 }>
          <Window style={{margin: 'auto'}}>

              SIPMLE MODAL

          </Window>
        </FixedLayer> )}
      </Layer>
    )
  }

  renderMovableWindow() {
    return (
      <Layer
        use={[this.state.counter]}  // data from the context
        id="movable_window">{({index, hideMe, showMe}, {
          ...rest,
          pinned = false,
          mouseDown = false,
          mouseLastPositionX = 0,
          mouseLastPositionY = 0,
          windowLeft = 400,
          windowTop = 100} = {}) => (
        <FixedLayer
          onMouseDown={() => showMe({...rest, mouseDown: true})}
          onMouseUp={() => showMe({...rest, mouseDown: false})}
          onMouseMove={({ screenX, screenY}) => {
            const newArgs = {
              mouseLastPositionX: screenX, mouseLastPositionY: screenY
            };
            if (pinned && mouseDown) {
              newArgs.windowLeft =  windowLeft + (screenX - mouseLastPositionX);
              newArgs.windowTop =  windowTop + (screenY - mouseLastPositionY);
            }
            showMe({...rest, ...newArgs})
          }}
          onClick={ hideMe }
          zIndex={ index * 100 }>
            <Window style={{ top: windowTop, left: windowLeft }}>

 mouseDown || showMe({...rest, pinned: true})}
                onMouseLeave={() => mouseDown || showMe({...rest, pinned: false})}>
                PIN TO MOVE

                <Markdown>
                  ##### Arguments:
                  <Highlight className="js">
                    { JSON.stringify(rest, null, '\t') }
                  </Highlight>
                  ##### Data from outer component (closure/context):
                  <Highlight className="js">
                    { JSON.stringify(this.state, null, '\t') }
                  </Highlight>
                </Markdown>

            </Window>
        </FixedLayer> )}
      </Layer>
    )
  }
}

export default Demo;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var express = require('express'),
    http = require('http'),
	morgan = require('morgan'),
	bodyParser = require('body-parser'),
	session = require('express-session'),
	compress = require('compression'),
	methodOverride = require('method-override'),
	cookieParser = require('cookie-parser'),
	helmet = require('helmet'),
	passport = require('passport'),
	mongoStore = require('connect-mongo')({
		session: session
	}),
	flash = require('connect-flash'),
	config = require('./config'),
	consolidate = require('consolidate'),
	path = require('path');

module.exports = function(db) {
	// Initialize express app
	var app = express();
    var server = http.createServer(app);
    var io = require('socket.io').listen(server);

	// Globbing model files
	config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
		require(path.resolve(modelPath));
	});

	// Setting application local variables
	app.locals.title = config.app.title;
	app.locals.description = config.app.description;
	app.locals.keywords = config.app.keywords;
	app.locals.facebookAppId = config.facebook.clientID;
	app.locals.jsFiles = config.getJavaScriptAssets();
	app.locals.cssFiles = config.getCSSAssets();

	// Passing the request url to environment locals
	app.use(function(req, res, next) {
		res.locals.url = req.protocol + ':// ' + req.headers.host + req.url;
		next();
	});

	// Should be placed before express.static
	app.use(compress({
		filter: function(req, res) {
			return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
		},
		level: 9
	}));

	// Showing stack errors
	app.set('showStackError', true);

	// Set swig as the template engine
	app.engine('server.view.html', consolidate[config.templateEngine]);

	// Set views path and view engine
	app.set('view engine', 'server.view.html');
	app.set('views', './app/views');

	// Environment dependent middleware
	if (process.env.NODE_ENV === 'development') {
		// Enable logger (morgan)
		app.use(morgan('dev'));

		// Disable views cache
		app.set('view cache', false);
	} else if (process.env.NODE_ENV === 'production') {
		app.locals.cache = 'memory';
	}

	// Request body parsing middleware should be above methodOverride
	app.use(bodyParser.urlencoded());
	app.use(bodyParser.json());
	app.use(methodOverride());

	// Enable jsonp
	app.enable('jsonp callback');

	// CookieParser should be above session
	app.use(cookieParser());

	// Express MongoDB session storage
	app.use(session({
		secret: config.sessionSecret,
		store: new mongoStore({
			db: db.connection.db,
			collection: config.sessionCollection
		})
	}));

	// use passport session
	app.use(passport.initialize());
	app.use(passport.session());

	// connect flash for flash messages
	app.use(flash());

	// Use helmet to secure Express headers
	app.use(helmet.xframe());
	app.use(helmet.iexss());
	app.use(helmet.contentTypeOptions());
	app.use(helmet.ienoopen());
	app.disable('x-powered-by');

	// Setting the app router and static folder
	app.use(express.static(path.resolve('./public')));

	// Globbing routing files
	config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
		require(path.resolve(routePath))(app);
	});

	// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
	app.use(function(err, req, res, next) {
		// If the error object doesn't exists
		if (!err) return next();

		// Log it
		console.error(err.stack);

		// Error page
		res.status(500).render('500', {
			error: err.stack
		});
	});

	// Assume 404 since no middleware responded
	app.use(function(req, res) {
		res.status(404).render('404', {
			url: req.originalUrl,
			error: 'Not Found'
		});
	});
// Add headers
app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Pass to next layer of middleware
    next();
});

//    io.on('connection', function(socket){
//        socket.broadcast.emit('user connected');
//
//         socket.on('send msg', function(msg){
//            console.log(msg);
//         });
//    });

	return app;
};<commit_msg>test return server for socket io<commit_after>'use strict';

/**
 * Module dependencies.
 */
var express = require('express'),
    http = require('http'),
	morgan = require('morgan'),
	bodyParser = require('body-parser'),
	session = require('express-session'),
	compress = require('compression'),
	methodOverride = require('method-override'),
	cookieParser = require('cookie-parser'),
	helmet = require('helmet'),
	passport = require('passport'),
	mongoStore = require('connect-mongo')({
		session: session
	}),
	flash = require('connect-flash'),
	config = require('./config'),
	consolidate = require('consolidate'),
	path = require('path');

module.exports = function(db) {
	// Initialize express app
	var app = express();
    var server = http.createServer(app);
    var io = require('socket.io').listen(server);

	// Globbing model files
	config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
		require(path.resolve(modelPath));
	});

	// Setting application local variables
	app.locals.title = config.app.title;
	app.locals.description = config.app.description;
	app.locals.keywords = config.app.keywords;
	app.locals.facebookAppId = config.facebook.clientID;
	app.locals.jsFiles = config.getJavaScriptAssets();
	app.locals.cssFiles = config.getCSSAssets();

	// Passing the request url to environment locals
	app.use(function(req, res, next) {
		res.locals.url = req.protocol + ':// ' + req.headers.host + req.url;
		next();
	});

	// Should be placed before express.static
	app.use(compress({
		filter: function(req, res) {
			return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
		},
		level: 9
	}));

	// Showing stack errors
	app.set('showStackError', true);

	// Set swig as the template engine
	app.engine('server.view.html', consolidate[config.templateEngine]);

	// Set views path and view engine
	app.set('view engine', 'server.view.html');
	app.set('views', './app/views');

	// Environment dependent middleware
	if (process.env.NODE_ENV === 'development') {
		// Enable logger (morgan)
		app.use(morgan('dev'));

		// Disable views cache
		app.set('view cache', false);
	} else if (process.env.NODE_ENV === 'production') {
		app.locals.cache = 'memory';
	}

	// Request body parsing middleware should be above methodOverride
	app.use(bodyParser.urlencoded());
	app.use(bodyParser.json());
	app.use(methodOverride());

	// Enable jsonp
	app.enable('jsonp callback');

	// CookieParser should be above session
	app.use(cookieParser());

	// Express MongoDB session storage
	app.use(session({
		secret: config.sessionSecret,
		store: new mongoStore({
			db: db.connection.db,
			collection: config.sessionCollection
		})
	}));

	// use passport session
	app.use(passport.initialize());
	app.use(passport.session());

	// connect flash for flash messages
	app.use(flash());

	// Use helmet to secure Express headers
	app.use(helmet.xframe());
	app.use(helmet.iexss());
	app.use(helmet.contentTypeOptions());
	app.use(helmet.ienoopen());
	app.disable('x-powered-by');

	// Setting the app router and static folder
	app.use(express.static(path.resolve('./public')));

	// Globbing routing files
	config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
		require(path.resolve(routePath))(app);
	});

	// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
	app.use(function(err, req, res, next) {
		// If the error object doesn't exists
		if (!err) return next();

		// Log it
		console.error(err.stack);

		// Error page
		res.status(500).render('500', {
			error: err.stack
		});
	});

	// Assume 404 since no middleware responded
	app.use(function(req, res) {
		res.status(404).render('404', {
			url: req.originalUrl,
			error: 'Not Found'
		});
	});
// Add headers
app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Pass to next layer of middleware
    next();
});

//    io.on('connection', function(socket){
//        socket.broadcast.emit('user connected');
//
//         socket.on('send msg', function(msg){
//            console.log(msg);
//         });
//    });

	return server;
};<|endoftext|>


<commit_before>/* globals Prism */
(function ($, Prism) {
    'use strict';

    // My plugin default options
    var defaultOptions = {};

    function highlightIt(text, language) {
        return [
            '
',
            '' + Prism.highlight(text, Prism.languages[language]) + '',
            '
',
        ].join('');
    }

    // If my plugin is a button
    function buildButtonDef(trumbowyg) {
        return {
            fn: function () {
                var $modal = trumbowyg.openModal('Code', [
                    '
',
                    '   <select class="' + trumbowyg.o.prefix + 'highlight-form-control language">',
                    (function () {
                        var options = '';

                        for (var lang in Prism.languages) {
                            if (Prism.languages.hasOwnProperty(lang)) {
                                options += '<option value="' + lang + '">' + lang + '</option>';
                            }
                        }

                        return options;
                    })(),
                    '   </select>',
                    '
',
                    '
',
                    '   <textarea class="' + trumbowyg.o.prefix + 'highlight-form-control code"></textarea>',
                    '
',
                ].join('\n')),
                $language = $modal.find('.language'),
                $code = $modal.find('.code');

                // Listen clicks on modal box buttons
                $modal.on('tbwconfirm', function () {
                    trumbowyg.restoreRange();
                    trumbowyg.execCmd('insertHTML', highlightIt($code.val(), $language.val()));
                    trumbowyg.execCmd('insertHTML', '

');

                    trumbowyg.closeModal();
                });

                $modal.on('tbwcancel', function () {
                    trumbowyg.closeModal();
                });
            }
        };
    }

    $.extend(true, $.trumbowyg, {
        // Add some translations
        langs: {
            // jshint camelcase:false
            en: {
                highlight: 'Code syntax highlight'
            },
            pt_br: {
                highlight: 'Realçar sintaxe de código'
            },
            ko: {
                highlight: '코드 문법 하이라이트'
            },
            // jshint camelcase:true
        },
        // Add our plugin to Trumbowyg registered plugins
        plugins: {
            highlight: {
                init: function (trumbowyg) {
                    // Fill current Trumbowyg instance with my plugin default options
                    trumbowyg.o.plugins.highlight = $.extend(true, {},
                        defaultOptions,
                        trumbowyg.o.plugins.highlight || {}
                    );

                    // If my plugin is a button
                    trumbowyg.addBtnDef('highlight', buildButtonDef(trumbowyg));
                }
            }
        }
    });
})(jQuery, Prism);
<commit_msg>[Highlight Plugin] Add line highlight via Prism Line Highlight plugin<commit_after>/* globals Prism */
(function ($, Prism) {
    'use strict';

    // My plugin default options
    var defaultOptions = {};

    function highlightIt(text, language, lineHighlight) {
        return [
            '
',
            '' + Prism.highlight(text, Prism.languages[language]) + '',
            '
',
        ].join('');
    }

    // If my plugin is a button
    function buildButtonDef(trumbowyg) {
        return {
            fn: function () {
                var $modal = trumbowyg.openModal('Code', [
                    '
',
                    '   <select class="' + trumbowyg.o.prefix + 'highlight-form-control language">',
                    (function () {
                        var options = '';

                        for (var lang in Prism.languages) {
                            if (Prism.languages.hasOwnProperty(lang)) {
                                options += '<option value="' + lang + '">' + lang + '</option>';
                            }
                        }

                        return options;
                    })(),
                    '   </select>',
                    '
',
                    '
',
                    '   <textarea class="' + trumbowyg.o.prefix + 'highlight-form-control code"></textarea>',
                    '
',
                    '
',
                    '   <input title="'+ trumbowyg.lang.prism_highlight_plugin_alert +
                            '" placeholder="' + trumbowyg.lang.highlight_line +
                            '" class="' + trumbowyg.o.prefix + 'highlight-form-control trumbowyg-line-highlight"/>',
                    '
'
                ].join('\n')),
                $language = $modal.find('.language'),
                $code = $modal.find('.code'),
                $lineHighlight = $modal.find('.trumbowyg-line-highlight');

                // Listen clicks on modal box buttons
                $modal.on('tbwconfirm', function () {
                    trumbowyg.restoreRange();
                    trumbowyg.execCmd('insertHTML', highlightIt($code.val(), $language.val(), $lineHighlight.val()));
                    trumbowyg.execCmd('insertHTML', '

');

                    trumbowyg.closeModal();
                });

                $modal.on('tbwcancel', function () {
                    trumbowyg.closeModal();
                });
            }
        };
    }

    $.extend(true, $.trumbowyg, {
        // Add some translations
        langs: {
            // jshint camelcase:false
            en: {
                highlight: 'Code syntax highlight',
                highlight_line: 'Highlight lines, e.g.: 1,3-5',
                prism_highlight_plugin_alert: 'You must have Prism Line Highlight plugin installed'
            },
            es: {
                highlight: 'Resaltado de sintaxis de código',
                highlight_line: 'Resaltar lineas, ej: 1,3-5',
                prism_highlight_plugin_alert: 'Debes de tener el plugin Prism Line Highlight instalado'
            },
            pt_br: {
                highlight: 'Realçar sintaxe de código'
            },
            ko: {
                highlight: '코드 문법 하이라이트'
            },
            // jshint camelcase:true
        },
        // Add our plugin to Trumbowyg registered plugins
        plugins: {
            highlight: {
                init: function (trumbowyg) {
                    // Fill current Trumbowyg instance with my plugin default options
                    trumbowyg.o.plugins.highlight = $.extend(true, {},
                        defaultOptions,
                        trumbowyg.o.plugins.highlight || {}
                    );

                    // If my plugin is a button
                    trumbowyg.addBtnDef('highlight', buildButtonDef(trumbowyg));
                }
            }
        }
    });
})(jQuery, Prism);
<|endoftext|>


<commit_before>require("underscore");
var utils = require("../common/utils");
var loading = require("./loading");
var Iframe = require("./iframe");
var tenantList = {};
var DEFAULT_CONFIG;

// get parameters from easemob.js
var baseConfig = getScriptConfig();
var _config = {};
var iframe;
var bind;

window.easemobim = window.easemobim || {};
window.easemobim.config = window.easemobim.config || {};
window.easemobim.version = "__WEBIM_PLUGIN_VERSION__";

if(
	/MSIE 7\.0/.test(navigator.userAgent)
	&& !window.localStorage
	&& !document.querySelector
){
	easemobim.bind = function(){
		alert("您使用的IE浏览器版本过低，请使用IE8以上版本的IE浏览器或Chrome浏览器"); // eslint-disable-line no-alert
	};
	throw new Error("unsupported browser.");
}

require("../../plugin-scss/easemob.scss");

DEFAULT_CONFIG = {
	tenantId: "",
	to: "",
	agentName: "",
	appKey: "",
	domain: "",
	path: "",
	ticket: true,
	staticPath: "",
	buttonText: __("common.contact_agent"),
	dialogWidth: "360px",
	dialogHeight: "550px",
	dragenable: true,
	minimum: true,
	soundReminder: true,
	titleSlide: true,
	dialogPosition: { x: "10px", y: "10px" },
	user: {
		username: "",
		password: "",
		token: ""
	},
	fromUrl: window.location.href,
	referer: document.referrer
};

// init _config & concat config and global easemobim.config
function reset(config){
	var hide;
	var resources;
	var sat;
	// growing io user id
	// 由于存在 cookie 跨域问题，所以从配置传过去
	var configData = _.extend({}, DEFAULT_CONFIG, { grUserId: utils.get("gr_user_id") });
	config = config || {};
	configData = _.extend({}, configData, easemobim.config, config);

	hide = utils.convertFalse(configData.hide) !== "" ? configData.hide : baseConfig.json.hide;
	resources = utils.convertFalse(_config.resources) !== "" ? configData.resources : baseConfig.json.resources;
	sat = utils.convertFalse(configData.satisfaction) !== "" ? configData.satisfaction : baseConfig.json.sat;

	configData = _.extend({}, configData, {
		tenantId: configData.tenantId || baseConfig.json.tenantId,
		configId: configData.configId || baseConfig.json.configId,
		hide: utils.convertFalse(hide),
		resources: utils.convertFalse(resources),
		satisfaction: utils.convertFalse(sat),
		domain: configData.domain || baseConfig.domain,
		path: configData.path || (baseConfig.domain + "__WEBIM_SLASH_KEY_PATH__/webim"),
		staticPath: configData.staticPath || (baseConfig.domain + "__WEBIM_SLASH_KEY_PATH__/webim/static"),
		guestId: utils.getStore("guestId") // 这个是别人种的cookie
	});
	// demo 页面点击联系客服带着 tenantId, 就删除 config 中的 configId, 否则 configId 存在就会用 configId 去渲染页面
	if(config.tenantId){
		configData.configId = "";
	}
	setConfig(configData);
}

function setConfig(configExt){
	_config = _.extend({}, _config, configExt);
}

// get config from current script
function getScriptConfig(){
	var src;
	var obj = {};
	var scripts = document.scripts;
	var s, l, i, len;

	for(s = 0, l = scripts.length; s < l; s++){
		if(~scripts[s].src.indexOf("easemob.js")){
			// src 会被强制加上域名
			src = scripts[s].src;
			break;
		}
	}

	if(!src){
		return { json: obj, domain: "" };
	}

	var tmp;
	var idx = src.indexOf("?");
	var sIdx = ~src.indexOf("//") ? src.indexOf("//") : 0;
	var domain = src.slice(0, src.indexOf("/", sIdx + 2));
	var arr = src.slice(idx + 1).split("&");

	for(i = 0, len = arr.length; i < len; i++){
		tmp = arr[i].split("=");
		obj[tmp[0]] = tmp.length > 1 ? decodeURIComponent(tmp[1]) : "";
	}
	return { json: obj, domain: domain };
}

/*
 * @param: {String} 技能组名称，选填
 * 兼容旧版接口，建议使用easemobim.bind方法
 */
window.easemobIM = function(group){
	easemobim.bind({ emgroup: group });
};
window.easemobIMS = function(tenantId, group){
	easemobim.bind({ tenantId: tenantId, emgroup: group });
};

/*
 * @param: {Object} config
 */

bind = function(config, autoLoad){
	var cacheKeyName;
	var i;
	reset(config);
	// 自动加载的
	// 后续把此 if else 消除掉
	if(autoLoad){
		if(
			(!_config.hide || _config.autoConnect || _config.eventCollector)
			&& (_config.tenantId || _config.configId)
			// CLOUD-15297 【dev47.35】网页插件：手机端不弹出自动邀请弹窗
			// && !utils.isMobile
		){
			cacheKeyName = _config.configId || (_config.tenantId + (_config.emgroup || ""));
			if(!_config.user.username){
				// 从cookie里取用户名
				// keyName = [to + ] tenantId [ + emgroup]
				_config.isUsernameFromCookie = true;
				_config.user = {
					username: utils.get(
						_config.configId || ((_config.to || "") + _config.tenantId + (_config.emgroup || ""))
					)
				};
			}
			iframe = tenantList[cacheKeyName] || new Iframe(_config);
			tenantList[cacheKeyName] = iframe;
			iframe.set(_config, iframe.close);
			// 访客上报用后失效
			easemobim.config.eventCollector = false;
		}
	}
	// 用户点击的
	else{
		// 防止空参数调用异常
		config = config || {};
		config.emgroup = config.emgroup || easemobim.config.emgroup || "";

		cacheKeyName = config.configId || (config.tenantId + config.emgroup);

		for(i in tenantList){
			if(Object.prototype.hasOwnProperty.call(tenantList, i)){
				// if(tenantList[i].show){
				// 	return false
				// }
				tenantList[i].close();
			}
		}

		iframe = tenantList[cacheKeyName];

		if(iframe){
			iframe.open();
		}
		else{
			utils.isMobile && loading.show();
			if(!_config.user.username){
				// 从cookie里取用户名
				// keyName = [to + ] tenantId [ + emgroup]
				config.isUsernameFromCookie = true;
				config.user = {
					username: utils.get(
						_config.configId || ((_config.to || "") + _config.tenantId + (_config.emgroup || ""))
					)
				};
			}
			setConfig(config);

			if(!_config.tenantId && !_config.configId){
				console.error("No tenantId is specified.");
				return;
			}

			iframe = new Iframe(_config);
			tenantList[cacheKeyName] = iframe;

			iframe.set(_config, iframe.open);
		}
	}

};

// open api1: send custom extend message
easemobim.sendExt = function(ext){
	if(iframe){
		iframe.send({
			ext: ext
		});
	}
	else{
		console.error("The chat window is not initialized.");
	}
};

// open api2: send text message
/*
 * @param: {object} 消息体
 * {
 *		data: "text msg",
 *		ext: {}
 * }
 */

easemobim.sendText = function(msg){
	if(iframe){
		iframe.sendText(msg);
	}
	else{
		console.error("The chat window is not initialized.");
	}
};

easemobim.minimize = function(){
	if(iframe){
		iframe.close();
	}
};
easemobim.restore = function(){
	if(iframe){
		iframe.open();
	}
};

// user click
window.easemobim.bind = function(config){
	bind(config, false);
};

// auto load
bind({}, true);

// support cmd & amd
if(typeof module === "object" && typeof module.exports === "object"){
	module.exports = easemobim;
}
else if(typeof define === "function" && define.amd){
	define("easemob-kefu-webim-plugin", [], function(){
		return easemobim;
	});
}
<commit_msg>fromUrl 测试<commit_after>require("underscore");
var utils = require("../common/utils");
var loading = require("./loading");
var Iframe = require("./iframe");
var tenantList = {};
var DEFAULT_CONFIG;

// get parameters from easemob.js
var baseConfig = getScriptConfig();
var _config = {};
var iframe;
var bind;

window.easemobim = window.easemobim || {};
window.easemobim.config = window.easemobim.config || {};
window.easemobim.version = "__WEBIM_PLUGIN_VERSION__";

if(
	/MSIE 7\.0/.test(navigator.userAgent)
	&& !window.localStorage
	&& !document.querySelector
){
	easemobim.bind = function(){
		alert("您使用的IE浏览器版本过低，请使用IE8以上版本的IE浏览器或Chrome浏览器"); // eslint-disable-line no-alert
	};
	throw new Error("unsupported browser.");
}

require("../../plugin-scss/easemob.scss");

DEFAULT_CONFIG = {
	tenantId: "",
	to: "",
	agentName: "",
	appKey: "",
	domain: "",
	path: "",
	ticket: true,
	staticPath: "",
	buttonText: __("common.contact_agent"),
	dialogWidth: "360px",
	dialogHeight: "550px",
	dragenable: true,
	minimum: true,
	soundReminder: true,
	titleSlide: true,
	dialogPosition: { x: "10px", y: "10px" },
	user: {
		username: "",
		password: "",
		token: ""
	},
	fromUrl: window.location.href,
	referer: document.referrer
};
console.log(window.location.href)

// init _config & concat config and global easemobim.config
function reset(config){
	var hide;
	var resources;
	var sat;
	// growing io user id
	// 由于存在 cookie 跨域问题，所以从配置传过去
	var configData = _.extend({}, DEFAULT_CONFIG, { grUserId: utils.get("gr_user_id") });
	config = config || {};
	configData = _.extend({}, configData, easemobim.config, config);

	hide = utils.convertFalse(configData.hide) !== "" ? configData.hide : baseConfig.json.hide;
	resources = utils.convertFalse(_config.resources) !== "" ? configData.resources : baseConfig.json.resources;
	sat = utils.convertFalse(configData.satisfaction) !== "" ? configData.satisfaction : baseConfig.json.sat;

	configData = _.extend({}, configData, {
		tenantId: configData.tenantId || baseConfig.json.tenantId,
		configId: configData.configId || baseConfig.json.configId,
		hide: utils.convertFalse(hide),
		resources: utils.convertFalse(resources),
		satisfaction: utils.convertFalse(sat),
		domain: configData.domain || baseConfig.domain,
		path: configData.path || (baseConfig.domain + "__WEBIM_SLASH_KEY_PATH__/webim"),
		staticPath: configData.staticPath || (baseConfig.domain + "__WEBIM_SLASH_KEY_PATH__/webim/static"),
		guestId: utils.getStore("guestId") // 这个是别人种的cookie
	});
	// demo 页面点击联系客服带着 tenantId, 就删除 config 中的 configId, 否则 configId 存在就会用 configId 去渲染页面
	if(config.tenantId){
		configData.configId = "";
	}
	setConfig(configData);
}

function setConfig(configExt){
	_config = _.extend({}, _config, configExt);
}

// get config from current script
function getScriptConfig(){
	var src;
	var obj = {};
	var scripts = document.scripts;
	var s, l, i, len;

	for(s = 0, l = scripts.length; s < l; s++){
		if(~scripts[s].src.indexOf("easemob.js")){
			// src 会被强制加上域名
			src = scripts[s].src;
			break;
		}
	}

	if(!src){
		return { json: obj, domain: "" };
	}

	var tmp;
	var idx = src.indexOf("?");
	var sIdx = ~src.indexOf("//") ? src.indexOf("//") : 0;
	var domain = src.slice(0, src.indexOf("/", sIdx + 2));
	var arr = src.slice(idx + 1).split("&");

	for(i = 0, len = arr.length; i < len; i++){
		tmp = arr[i].split("=");
		obj[tmp[0]] = tmp.length > 1 ? decodeURIComponent(tmp[1]) : "";
	}
	return { json: obj, domain: domain };
}

/*
 * @param: {String} 技能组名称，选填
 * 兼容旧版接口，建议使用easemobim.bind方法
 */
window.easemobIM = function(group){
	easemobim.bind({ emgroup: group });
};
window.easemobIMS = function(tenantId, group){
	easemobim.bind({ tenantId: tenantId, emgroup: group });
};

/*
 * @param: {Object} config
 */

bind = function(config, autoLoad){
	var cacheKeyName;
	var i;
	reset(config);
	// 自动加载的
	// 后续把此 if else 消除掉
	if(autoLoad){
		if(
			(!_config.hide || _config.autoConnect || _config.eventCollector)
			&& (_config.tenantId || _config.configId)
			// CLOUD-15297 【dev47.35】网页插件：手机端不弹出自动邀请弹窗
			// && !utils.isMobile
		){
			cacheKeyName = _config.configId || (_config.tenantId + (_config.emgroup || ""));
			if(!_config.user.username){
				// 从cookie里取用户名
				// keyName = [to + ] tenantId [ + emgroup]
				_config.isUsernameFromCookie = true;
				_config.user = {
					username: utils.get(
						_config.configId || ((_config.to || "") + _config.tenantId + (_config.emgroup || ""))
					)
				};
			}
			iframe = tenantList[cacheKeyName] || new Iframe(_config);
			tenantList[cacheKeyName] = iframe;
			iframe.set(_config, iframe.close);
			// 访客上报用后失效
			easemobim.config.eventCollector = false;
		}
	}
	// 用户点击的
	else{
		// 防止空参数调用异常
		config = config || {};
		config.emgroup = config.emgroup || easemobim.config.emgroup || "";

		cacheKeyName = config.configId || (config.tenantId + config.emgroup);

		for(i in tenantList){
			if(Object.prototype.hasOwnProperty.call(tenantList, i)){
				// if(tenantList[i].show){
				// 	return false
				// }
				tenantList[i].close();
			}
		}

		iframe = tenantList[cacheKeyName];

		if(iframe){
			iframe.open();
		}
		else{
			utils.isMobile && loading.show();
			if(!_config.user.username){
				// 从cookie里取用户名
				// keyName = [to + ] tenantId [ + emgroup]
				config.isUsernameFromCookie = true;
				config.user = {
					username: utils.get(
						_config.configId || ((_config.to || "") + _config.tenantId + (_config.emgroup || ""))
					)
				};
			}
			setConfig(config);

			if(!_config.tenantId && !_config.configId){
				console.error("No tenantId is specified.");
				return;
			}

			iframe = new Iframe(_config);
			tenantList[cacheKeyName] = iframe;

			iframe.set(_config, iframe.open);
		}
	}

};

// open api1: send custom extend message
easemobim.sendExt = function(ext){
	if(iframe){
		iframe.send({
			ext: ext
		});
	}
	else{
		console.error("The chat window is not initialized.");
	}
};

// open api2: send text message
/*
 * @param: {object} 消息体
 * {
 *		data: "text msg",
 *		ext: {}
 * }
 */

easemobim.sendText = function(msg){
	if(iframe){
		iframe.sendText(msg);
	}
	else{
		console.error("The chat window is not initialized.");
	}
};

easemobim.minimize = function(){
	if(iframe){
		iframe.close();
	}
};
easemobim.restore = function(){
	if(iframe){
		iframe.open();
	}
};

// user click
window.easemobim.bind = function(config){
	bind(config, false);
};

// auto load
bind({}, true);

// support cmd & amd
if(typeof module === "object" && typeof module.exports === "object"){
	module.exports = easemobim;
}
else if(typeof define === "function" && define.amd){
	define("easemob-kefu-webim-plugin", [], function(){
		return easemobim;
	});
}
<|endoftext|>


<commit_before>var Element = require( '../../lib/element' )

describe( 'Element', function () {
	var elem = new Element()
	var elemInstance
	//create element
	describe( 'var elem = new Element() //create new element', function(){
		it( 'elem is instance of Element', function () {
			expect(elem).to.be.instanceOf(Element);
		})

		it( 'elem has no key by default', function () {
			expect(elem._$key).to.equal(void 0);
		})

		it( 'elem has no path', function () {
			expect(elem.$path.length).to.equal(0);
		})

		it( 'elem has no context', function () {
			expect(elem._$context).to.not.be.ok;
		})
	})

	//set key
	describe( 'elem.set({ $key:\'elemKey\' }) //set key', function(){

		it( 'elem now has key \'elemKey\'', function () {
			elem.set({$key:'elemKey'})
			expect(elem._$key).to.equal('elemKey');
		})

		it( 'elem now has a path [\'elemKey\']', function () {
			expect(elem.$path).to.deep.equal(['elemKey']);
		})

		it( 'elem still has no context', function () {
			expect(elem._$context).to.not.be.ok;
		})

	})

	//add child
	describe( 'elem.set({ elemChild:{}, elemChild2:{} }) //add children', function(){

		it( 'elem.elemChild is instance of Element', function () {
			elem.set({ elemChild:{}, elemChild2:{} })
			expect(elem.elemChild instanceof Element).to.equal(true);
		})

		it( 'elem.elemChild has key \'elemChild\'', function () {
			expect(elem.elemChild._$key).to.equal('elemChild');
		})

		it( "elem.elemChild has path ['elemKey','elemChild']", function () {
			expect(elem.elemChild.$path).to.deep.equal(['elemKey','elemChild']);
		})

		it( 'elem.elemChild has no context', function () {
			expect(elem.elemChild._$context).to.not.be.ok;
		})

	})

	//create instance of elem
	describe( 'var elemInstance = new elem.$Constructor() //create instance', function(){
		elemInstance = new elem.$Constructor()

		it( 'elemInstance is instance of elem._$Constructor', function () {
			expect(elemInstance).to.be.instanceOf(elem._$Constructor);
		})

		it( 'elemInstance.elemChild is instance of elem.elemChild._$Constructor', function () {
			expect(elemInstance.elemChild).to.be.instanceOf(elem.elemChild._$Constructor);
		})

		it( 'elemInstance has the same key as elem: \'elemKey\'', function () {
			expect(elem._$key).to.equal(elemInstance._$key).to.equal('elemKey');
		})

		it( 'elemInstance has no context', function () {
			expect(elemInstance._$context).to.not.be.ok;
		})

		it( 'elemInstance.elemChild has context elemInstance', function () {
			expect(elemInstance.elemChild._$context).to.equal(elemInstance);
		})

		it( 'elem.elemChild still has no context', function () {
			expect(elem.elemChild._$context).to.not.be.ok;
		})
	})

	//set key
	describe( 'elemInstance.set({ $key:\'elemInstanceKey\' }) //change instance key', function(){

		it( 'elemInstance now has key \'elemInstanceKey\'', function () {
			elemInstance.set({$key:'elemInstanceKey'})
			expect(elemInstance._$key).to.equal('elemInstanceKey');
		})

		it( 'elemInstance now has a path [\'elemInstanceKey\']', function () {
			expect(elemInstance.$path).to.deep.equal(['elemInstanceKey']);
		})

	})

	//add child to elemInstance
	describe( 'elemInstance.set({ elemInstanceChild:{} }) //add child to instance', function(){
		it( 'elem does not have child elemInstanceChild', function () {
			elemInstance.set({ elemInstanceChild:{} })
			expect(elem.elemInstanceChild).to.not.be.ok
		})

		it( 'elemInstance.elemInstanceChild is instance of Element', function () {
			expect(elemInstance.elemInstanceChild).to.be.instanceOf(Element);
		})

		it( 'elemInstance.elemInstanceChild has path [\'elemInstanceKey\',\'elemInstanceChild\']', function () {
			expect(elemInstance.elemInstanceChild.$path).to.deep.equal(['elemInstanceKey','elemInstanceChild']);
		})

	})

	//remove child from original
	describe( 'elem.elemChild.remove() //remove child from original', function(){

		it( 'elem.elemChild is removed', function () {
			elem.elemChild.remove()
			expect(elem.elemChild).to.not.be.ok
		})

		it( 'elemInstance.elemChild also is removed', function () {
			expect(elemInstance.elemChild).to.not.be.ok
		})

	})

	//remove child from instance
	describe( 'elemInstance.elemChild2.remove() //remove child from instance', function(){

		it( 'elemInstance.elemChild2 is removed', function () {
			elemInstance.elemChild2.remove()
			expect(elemInstance.elemChild2).to.not.be.ok
		})

		it( 'elem.elemChild2 is not removed', function () {
			expect(elem.elemChild2).to.be.ok
		})

	})

	describe( 'changes should be happen in the correct element',function(){

		elemInstance.set({
			$on:{
				$change:function(){
				}
			}
		})

		it( 'changes in elemInstance should NOT change elem',function () {
			var spy = sinon.spy(elem.$on.change)
			elemInstance.$val = "new"
			expect(spy.calledOnce).to.not.be.true
		})

		it( 'elem should keep the same val',function () {
			expect(elem.$val).to.not.be.equal("new")
		})
		it( 'change elem $val should not update elemInstance $val', function(){
			elem.$val="Another value"
			expect(elemInstance.$val).to.be.equal("new")
		})

	})

})<commit_msg>Refactoring on element tests<commit_after>var Element = require( '../../lib/element' )

describe( 'Element', function () {
	var elem = new Element()
	var elemInstance
	//create element
	describe( 'var elem = new Element() //create new element', function(){
		it( 'elem is instance of Element', function () {
			expect(elem).to.be.instanceOf(Element);
		})

		it( 'elem has no key by default', function () {
			expect(elem._$key).to.equal(void 0);
		})

		it( 'elem has no path', function () {
			expect(elem.$path.length).to.equal(0);
		})

		it( 'elem has no context', function () {
			expect(elem._$context).to.not.be.ok;
		})
	})

	//set key
	describe( 'elem.set({ $key:\'elemKey\' }) //set key', function(){

		it( 'elem now has key \'elemKey\'', function () {
			elem.set({$key:'elemKey'})
			expect(elem._$key).to.equal('elemKey');
		})

		it( 'elem now has a path [\'elemKey\']', function () {
			expect(elem.$path).to.deep.equal(['elemKey']);
		})

		it( 'elem still has no context', function () {
			expect(elem._$context).to.not.be.ok;
		})

	})

	//add child
	describe( 'elem.set({ elemChild:{}, elemChild2:{} }) //add children', function(){

		it( 'elem.elemChild is instance of Element', function () {
			elem.set({ elemChild:{}, elemChild2:{} })
			expect(elem.elemChild instanceof Element).to.equal(true);
		})

		it( 'elem.elemChild has key \'elemChild\'', function () {
			expect(elem.elemChild._$key).to.equal('elemChild');
		})

		it( "elem.elemChild has path ['elemKey','elemChild']", function () {
			expect(elem.elemChild.$path).to.deep.equal(['elemKey','elemChild']);
		})

		it( 'elem.elemChild has no context', function () {
			expect(elem.elemChild._$context).to.not.be.ok;
		})

	})

	//create instance of elem
	describe( 'var elemInstance = new elem.$Constructor() //create instance', function(){
		elemInstance = new elem.$Constructor()

		it( 'elemInstance is instance of elem._$Constructor', function () {
			expect(elemInstance).to.be.instanceOf(elem._$Constructor);
		})

		it( 'elemInstance.elemChild is instance of elem.elemChild._$Constructor', function () {
			expect(elemInstance.elemChild).to.be.instanceOf(elem.elemChild._$Constructor);
		})

		it( 'elemInstance has the same key as elem: \'elemKey\'', function () {
			expect(elem._$key).to.equal(elemInstance._$key).to.equal('elemKey');
		})

		it( 'elemInstance has no context', function () {
			expect(elemInstance._$context).to.not.be.ok;
		})

		it( 'elemInstance.elemChild has context elemInstance', function () {
			expect(elemInstance.elemChild._$context).to.equal(elemInstance);
		})

		it( 'elem.elemChild still has no context', function () {
			expect(elem.elemChild._$context).to.not.be.ok;
		})
	})

	//set key
	describe( 'elemInstance.set({ $key:\'elemInstanceKey\' }) //change instance key', function(){

		it( 'elemInstance now has key \'elemInstanceKey\'', function () {
			elemInstance.set({$key:'elemInstanceKey'})
			expect(elemInstance._$key).to.equal('elemInstanceKey');
		})

		it( 'elemInstance now has a path [\'elemInstanceKey\']', function () {
			expect(elemInstance.$path).to.deep.equal(['elemInstanceKey']);
		})

	})

	//add child to elemInstance
	describe( 'elemInstance.set({ elemInstanceChild:{} }) //add child to instance', function(){
		it( 'elem does not have child elemInstanceChild', function () {
			elemInstance.set({ elemInstanceChild:{} })
			expect(elem.elemInstanceChild).to.not.be.ok
		})

		it( 'elemInstance.elemInstanceChild is instance of Element', function () {
			expect(elemInstance.elemInstanceChild).to.be.instanceOf(Element);
		})

		it( 'elemInstance.elemInstanceChild has path [\'elemInstanceKey\',\'elemInstanceChild\']', function () {
			expect(elemInstance.elemInstanceChild.$path).to.deep.equal(['elemInstanceKey','elemInstanceChild']);
		})

	})

	//remove child from original
	describe( 'elem.elemChild.remove() //remove child from original', function(){

		it( 'elem.elemChild is removed', function () {
			elem.elemChild.remove()
			expect(elem.elemChild).to.not.be.ok
		})

		it( 'elemInstance.elemChild also is removed', function () {
			expect(elemInstance.elemChild).to.not.be.ok
		})

	})

	//remove child from instance
	describe( 'elemInstance.elemChild2.remove() //remove child from instance', function(){

		it( 'elemInstance.elemChild2 is removed', function () {
			elemInstance.elemChild2.remove()
			expect(elemInstance.elemChild2).to.not.be.ok
		})

		it( 'elem.elemChild2 is not removed', function () {
			expect(elem.elemChild2).to.be.ok
		})

	})

	describe( 'changes should be happen in the correct element',function(){

		elemInstance.set({
			$on:{
				$change:function(){
				}
			}
		})

		it( 'changes in elemInstance should NOT change elem',function () {
			var spy = sinon.spy(elem.$on.change)
			elemInstance.$val = "new"
			expect(spy.calledOnce).to.not.be.true
		})

		it( 'elem should keep the same val',function () {
			expect(elem.$val).to.not.be.equal("new")
		})
		it( 'change elem $val should not update elemInstance $val', function(){
			elem.$val="Another value"
			expect(elemInstance.$val).to.be.equal("new")
			expect(elem.$val).to.be.equal("Another value")
		})

	})

})<|endoftext|>


<commit_before><!--//
//Start plugin
//wrap in namespace to be safe.
(function($){

    //Start jQ plugin
    $.fn.rotator = function(options){

        var settings = $.extend({
            starting: 0,
            ending: 100,
            lineWidth: 7,
            timer: 10,
            radius: 125,
            percentage: true,
            show_text: true,
            color: 'green',
            fontStyle: 'Calibri',
            fontSize: '20pt',
            fontColor: 'darkblue',
            backgroundColor: 'lightgray',
            callback: function () {}
        }, options);

        this.empty().append("<canvas height ="+this.height()+" width="+this.width()+" id='rotator-canvas'/ ></canvas>");

        //Get the canvas tag and assign to a local var
        var canvas = document.getElementById('rotator-canvas');
        var context = canvas.getContext("2d");
        var step = settings.starting;
        var steps = settings.ending - settings.starting;
        var x = canvas.width / 2;
        var y = canvas.height / 2;
        var z = setInterval(function() {
            //Canvas math(s)
            //TODO Needs work. Need 'min','start_point','end_point','max'.
            context.beginPath();
            context.arc(
                x, y, settings.radius,
                (1.5 + (step/(steps*0.5)))*Math.PI,
                (1.5 + (++step/(steps*0.5)))*Math.PI,
                false
            );
            context.lineWidth = settings.lineWidth;
            context.strokeStyle = settings.color;
            context.stroke();
            context.font = settings.fontSize + ' ' + settings.fontStyle;
            context.fillStyle = settings.fontColor;
            context.clearRect(
                x - parseInt(settings.fontSize)*1.5,
                y - parseInt(settings.fontSize)/2,
                parseInt(settings.fontSize)*3,
                parseInt(settings.fontSize)
            );

            //show text?
            if (settings.show_text) {
                //Now to handle the text changes
                var text;
                //Do we use the % with the text?
                if (settings.percentage) { text = step + "%" } else { text = step };
                context.textAlign = 'center';
                context.textBaseline = 'middle';
                context.fillText(text, x , y );
            }

            //Incriment a fill step
            if (step >= steps) {
                window.clearInterval(z);

                if(settings.percentage) { text = step + "%" } else { text = step };

                context.clearRect(
                    x - parseInt(settings.fontSize)*1.5,
                    y - parseInt(settings.fontSize)/2,
                    parseInt(settings.fontSize)*3,
                    parseInt(settings.fontSize)
                );
                context.fillText(text, x , y );

                if (typeof(settings.callback) == 'function') {
                    settings.callback.call(this);
                }
            }
        }, settings.timer)

        //Set the radial background pre filled (also be blank for non)
        if (settings.backgroundColor) {
            context.arc(x, y, settings.radius, 0, (2*Math.PI), false);
            context.strokeStyle = settings.backgroundColor;
            context.lineWidth   = settings.lineWidth;
            context.stroke();
        };
    }
}(jQuery));

//Example function call to run the fill animation
function setpb(){
    $('#rotator').rotator({
        starting: $("#startval").val(),
        ending: $("#endval").val(),
        lineWidth: 10
    })
}

//Example of executing the animation on DOM ready
$(document).ready(function(){

    $('#rotator').rotator({
        starting: 0,
        ending: 100,
        lineWidth: 50,
        color: 'yellow'
    });
});
--><commit_msg>Removed text updating from the fill animation logic block, wht was it there? no idea. But its gone now.<commit_after><!--//
//Start plugin
//wrap in namespace to be safe.
(function($){

    //Start jQ plugin
    $.fn.rotator = function(options){

        var settings = $.extend({
            starting: 0,
            ending: 100,
            lineWidth: 7,
            timer: 10,
            radius: 125,
            percentage: true,
            show_text: true,
            color: 'green',
            fontStyle: 'Calibri',
            fontSize: '20pt',
            fontColor: 'darkblue',
            backgroundColor: 'lightgray',
            callback: function () {}
        }, options);

        this.empty().append("<canvas height ="+this.height()+" width="+this.width()+" id='rotator-canvas'/ ></canvas>");

        //Get the canvas tag and assign to a local var
        var canvas = document.getElementById('rotator-canvas');
        var context = canvas.getContext("2d");
        var step = settings.starting;
        var steps = settings.ending - settings.starting;
        var x = canvas.width / 2;
        var y = canvas.height / 2;
        var z = setInterval(function() {
            //Canvas math(s)
            //TODO Needs work. Need 'min','start_point','end_point','max'.
            context.beginPath();
            context.arc(
                x, y, settings.radius,
                (1.5 + (step/(steps*0.5)))*Math.PI,
                (1.5 + (++step/(steps*0.5)))*Math.PI,
                false
            );
            context.lineWidth = settings.lineWidth;
            context.strokeStyle = settings.color;
            context.stroke();
            context.font = settings.fontSize + ' ' + settings.fontStyle;
            context.fillStyle = settings.fontColor;
            context.clearRect(
                x - parseInt(settings.fontSize)*1.5,
                y - parseInt(settings.fontSize)/2,
                parseInt(settings.fontSize)*3,
                parseInt(settings.fontSize)
            );

            //show text?
            if (settings.show_text) {
                //Now to handle the text changes
                var text;
                //Do we use the % with the text?
                if (settings.percentage) { text = step + "%" } else { text = step };
                context.textAlign = 'center';
                context.textBaseline = 'middle';
                context.fillText(text, x , y );
            }

            //Incriment a fill step
            if (step >= steps) {
                window.clearInterval(z);
                context.clearRect(
                    x - parseInt(settings.fontSize)*1.5,
                    y - parseInt(settings.fontSize)/2,
                    parseInt(settings.fontSize)*3,
                    parseInt(settings.fontSize)
                );
                context.fillText(text, x , y );

                if (typeof(settings.callback) == 'function') {
                    settings.callback.call(this);
                }
            }
        }, settings.timer)

        //Set the radial background pre filled (also be blank for non)
        if (settings.backgroundColor) {
            context.arc(x, y, settings.radius, 0, (2*Math.PI), false);
            context.strokeStyle = settings.backgroundColor;
            context.lineWidth   = settings.lineWidth;
            context.stroke();
        };
    }
}(jQuery));

//Example function call to run the fill animation
function setpb(){
    $('#rotator').rotator({
        starting: $("#startval").val(),
        ending: $("#endval").val(),
        lineWidth: 10
    })
}

//Example of executing the animation on DOM ready
$(document).ready(function(){

    $('#rotator').rotator({
        starting: 0,
        ending: 100,
        lineWidth: 50,
        color: 'yellow'
    });
});
--><|endoftext|>


<commit_before>
import { DOMImplementation, XMLSerializer } from 'xmldom';

export default class OsmEdit{
    /**
     * @author Guillaume AMAT
     * @access public
     * @param {object} osmAuth - Instance of osm-auth.
     */
    constructor (osmAuth) {
        this._auth = osmAuth;
        this._changesetCreatedBy = null;
        this._changesetComment = null;
        this._lat = null;
        this._lng = null;
        this._tags = [];
        this._uid = null;
        this._version = 0;
        this._id = null;
        this._type = 'node';
        this._timestamp = new Date().toISOString();
        this._displayName = null;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} changesetCreatedBy - Application used to send datas to OSM (in the changeset).
     */
    setChangesetCreatedBy(changesetCreatedBy) {
        this._changesetCreatedBy = changesetCreatedBy;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getChangesetCreatedBy() {
        return this._changesetCreatedBy;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} changesetComment - Comment used in the changeset.
     */
    setChangesetComment(changesetComment) {
        this._changesetComment = changesetComment;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getChangesetComment() {
        return this._changesetComment;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {number} lat - Node's latitude.
     */
    setLatitude(lat) {
        this._lat = lat;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {number}
     */
    getLatitude() {
        return this._lat;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {number} lon - Node's longitude.
     */
    setLongitude(lon) {
        this._lon = lon;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {number}
     */
    getLongitude() {
        return this._lon;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {array} tags - Node's tags.
     */
    setTags(tags) {
        this._tags = tags;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {array}
     */
    getTags() {
        return this._tags;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} uid - UID of the node's editor.
     */
    setUid(uid) {
        this._uid = uid;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getUid() {
        return this._uid;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} type - Element's type.
     */
    setType(type) {
        this._type = type;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getType() {
        return this._type;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} id - Element's ID.
     */
    setId(id) {
        this._id = id;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getId() {
        return this._id;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} timestamp - Timestamp of the node creation.
     */
    setTimestamp(timestamp) {
        this._timestamp = timestamp;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getTimestamp() {
        return this._timestamp;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} version - Element's version.
     */
    setVersion(version) {
        this._version = version;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getVersion() {
        return this._version;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} displayName - Display name of the node's editor.
     */
    setDisplayName(displayName) {
        this._displayName = displayName;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getDisplayName() {
        return this._displayName;
    }

    /**
     * Fetch an element from OSM.
     *
     * @author Guillaume AMAT
     * @access public
     * @param {string} type - Element's type.
     * @param {string|number} id - Element's ID.
     * @return {promise}
     */
    fetch(type, id) {
        return new Promise((resolve, reject) => {
            this._auth.xhr({
                'method': 'GET',
                'path': `/api/0.6/${type}/${id}`,
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                }
            },
            (err, xml) => {
                if (err) {
                    console.error('ERROR on fetch element: ' + err.response);
                    return reject(err);
                }

                let key, value,
                tags = {},
                parentElement = xml.getElementsByTagName(type)[0],
                tagElements = xml.documentElement.getElementsByTagName('tag'),
                version = parseInt( parentElement.getAttribute('version') );

                for (let tag of tagElements) {
                    if ( tag.getAttribute ) {
                        key = tag.getAttribute('k');
                        value = tag.getAttribute('v');
                        tags[ key ] = value;
                    }
                }

                this.setVersion(version);
                this.setTags(tags);

                resolve(this);
            });
        });
    }

    /**
     * Sends the node to OSM.
     *
     * @author Guillaume AMAT
     * @access public
     * @return {promise}
     */
    send() {

        return new Promise((resolve, reject) => {

            this._getChangesetId()
            .then(
                changesetId => {

                    return this._sendXml(changesetId);
                },
                err => {

                    reject(err);
                }
            )
            .then(
                nodeId => {

                    resolve(nodeId);
                },
                err => {

                    reject(err);
                }
            );
        });
    }

    /**
     * Builds a changeset XML.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {string} - The changeset XML.
     */
    _buildChangesetXml() {

        var xml = new DOMImplementation().createDocument('', '', null),
        osmElement = xml.createElement('osm'),
        changesetElement = xml.createElement('changeset'),
        createdByElement = xml.createElement('tag'),
        commentElement = xml.createElement('tag');

        createdByElement.setAttribute('k', 'created_by');
        createdByElement.setAttribute('v', this._changesetCreatedBy);
        changesetElement.appendChild(createdByElement);

        commentElement.setAttribute('k', 'comment');
        commentElement.setAttribute('v', this._changesetComment);
        changesetElement.appendChild(commentElement);

        osmElement.appendChild(changesetElement);
        xml.appendChild(osmElement);

        return new XMLSerializer().serializeToString(xml);
    }

    /**
     * Builds a node XML.
     *
     * @author Guillaume AMAT
     * @access private
     * @param {number} changesetId - The changeset ID used during the call to OSM.
     * @return {string} - The node XML.
     */
    _buildXml(changesetId) {

        var xml = new DOMImplementation().createDocument('', '', null),
        osmElement = xml.createElement('osm'),
        nodeElement = xml.createElement('node');

        nodeElement.setAttribute('changeset', changesetId);
        nodeElement.setAttribute('version', this._version);
        nodeElement.setAttribute('timestamp', this._timestamp);
        nodeElement.setAttribute('uid', this._uid);
        nodeElement.setAttribute('display_name', this._displayName);
        nodeElement.setAttribute('lat', this._lat);
        nodeElement.setAttribute('lon', this._lon);

        this._tags.forEach(tag => {

            if (!tag.key || !tag.value) {
                return false;
            }

            let tagElement = xml.createElement('tag');

            tagElement.setAttribute('k', tag.key);
            tagElement.setAttribute('v', tag.value);
            nodeElement.appendChild(tagElement);
        });

        osmElement.appendChild(nodeElement);
        xml.appendChild(osmElement);

        return new XMLSerializer().serializeToString(xml);
    }

    /**
     * Asks OSM to create a changeset and return its ID.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {promise}
     */
    _createChangeset() {

        var changesetXml = this._buildChangesetXml(this._changesetCreatedBy, this._changesetComment);

        return new Promise((resolve, reject) => {

            this._auth.xhr({

                'method': 'PUT',
                'path': '/api/0.6/changeset/create',
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
                'content': changesetXml
            },
            (err, changesetId) => {

                if (err) {

                    console.error('ERROR on put changeset: ' + err.response);
                    return reject(err);
                }

                resolve( parseInt(changesetId) );
            });
        });
    }

    /**
     * Checks if a given changeset is still opened at OSM.
     *
     * @author Guillaume AMAT
     * @access private
     * @param {number} changesetId - The changeset ID used during the call to OSM.
     * @return {promise}
     */
    _isChangesetStillOpen(changesetId) {

        return new Promise((resolve, reject) => {

            this._auth.xhr({

                'method': 'GET',
                'path': '/api/0.6/changeset/'+ changesetId.toString(),
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
            },
            (err, xml) => {

                if (err) {

                    return reject(err);
                }

                var isOpened = xml.getElementsByTagName('changeset')[0].getAttribute('open');

                if (isOpened === 'false') {

                    return reject(err);
                }

                resolve(changesetId);
            });
        });
    }

    /**
     * Gets a changeset ID from an old one or a creation.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {promise}
     */
    _getChangesetId() {

        var changesetId = parseInt( sessionStorage.getItem('osmEdit-changesetId') );

        if ( changesetId ) {

            return this._isChangesetStillOpen(changesetId)
            .then(
                changesetId => {

                    return changesetId;
                },
                err => {

                    sessionStorage.removeItem('osmEdit-changesetId');
                    return this._getChangesetId();
                }
            );
        }
        else {

            return this._createChangeset()
            .then(
                changesetId => {

                    sessionStorage.setItem('osmEdit-changesetId', changesetId);
                    return changesetId;
                },
                err => {

                    sessionStorage.removeItem('osmEdit-changesetId');
                    return this._getChangesetId();
                }
            );
        }
    }

     /**
      * Sends the node xml to OSM.
      *
      * @author Guillaume AMAT
      * @access private
      * @param {number} changesetId - The changeset ID to use during the sending.
      * @return {promise}
      */
    _sendXml(changesetId) {

        var data,
        method = 'PUT',
        path = `/api/0.6/${this._type}/create`,
        xml = this._buildXml(changesetId);

        if (this._id) {
            path = `/api/0.6/${this._type}/${this._id}`;
        }

        return new Promise((resolve, reject) => {
            this._auth.xhr({
                'method': method,
                'path': path,
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
                'content': xml,
            },
            (err, nodeId) => {
                if (err) {
                    reject(err);
                    return;
                }

                resolve(nodeId);
            });
        });
    }
}
<commit_msg>Fixes the contribution on Chrome based browsers<commit_after>
import { DOMImplementation, XMLSerializer } from 'xmldom';

export default class OsmEdit{
    /**
     * @author Guillaume AMAT
     * @access public
     * @param {object} osmAuth - Instance of osm-auth.
     */
    constructor (osmAuth) {
        this._auth = osmAuth;
        this._changesetCreatedBy = null;
        this._changesetComment = null;
        this._lat = null;
        this._lng = null;
        this._tags = [];
        this._uid = null;
        this._version = 0;
        this._id = null;
        this._type = 'node';
        this._timestamp = new Date().toISOString();
        this._displayName = null;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} changesetCreatedBy - Application used to send datas to OSM (in the changeset).
     */
    setChangesetCreatedBy(changesetCreatedBy) {
        this._changesetCreatedBy = changesetCreatedBy;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getChangesetCreatedBy() {
        return this._changesetCreatedBy;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} changesetComment - Comment used in the changeset.
     */
    setChangesetComment(changesetComment) {
        this._changesetComment = changesetComment;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getChangesetComment() {
        return this._changesetComment;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {number} lat - Node's latitude.
     */
    setLatitude(lat) {
        this._lat = lat;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {number}
     */
    getLatitude() {
        return this._lat;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {number} lon - Node's longitude.
     */
    setLongitude(lon) {
        this._lon = lon;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {number}
     */
    getLongitude() {
        return this._lon;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {array} tags - Node's tags.
     */
    setTags(tags) {
        this._tags = tags;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {array}
     */
    getTags() {
        return this._tags;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} uid - UID of the node's editor.
     */
    setUid(uid) {
        this._uid = uid;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getUid() {
        return this._uid;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} type - Element's type.
     */
    setType(type) {
        this._type = type;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getType() {
        return this._type;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} id - Element's ID.
     */
    setId(id) {
        this._id = id;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getId() {
        return this._id;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} timestamp - Timestamp of the node creation.
     */
    setTimestamp(timestamp) {
        this._timestamp = timestamp;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getTimestamp() {
        return this._timestamp;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string|number} version - Element's version.
     */
    setVersion(version) {
        this._version = version;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string|number}
     */
    getVersion() {
        return this._version;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @param {string} displayName - Display name of the node's editor.
     */
    setDisplayName(displayName) {
        this._displayName = displayName;
    }

    /**
     * @author Guillaume AMAT
     * @access public
     * @return {string}
     */
    getDisplayName() {
        return this._displayName;
    }

    /**
     * Fetch an element from OSM.
     *
     * @author Guillaume AMAT
     * @access public
     * @param {string} type - Element's type.
     * @param {string|number} id - Element's ID.
     * @return {promise}
     */
    fetch(type, id) {
        return new Promise((resolve, reject) => {
            this._auth.xhr({
                'method': 'GET',
                'path': `/api/0.6/${type}/${id}`,
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                }
            },
            (err, xml) => {
                if (err) {
                    console.error('ERROR on fetch element: ' + err.response);
                    return reject(err);
                }

                let key, value,
                tags = {},
                parentElement = xml.getElementsByTagName(type)[0],
                tagElements = xml.documentElement.getElementsByTagName('tag'),
                version = parseInt( parentElement.getAttribute('version') );

                for (let index in tagElements) {
                    let tag = tagElements[index];

                    if ( tag.getAttribute ) {
                        key = tag.getAttribute('k');
                        value = tag.getAttribute('v');
                        tags[ key ] = value;
                    }
                }

                this.setVersion(version);
                this.setTags(tags);

                resolve(this);
            });
        });
    }

    /**
     * Sends the node to OSM.
     *
     * @author Guillaume AMAT
     * @access public
     * @return {promise}
     */
    send() {

        return new Promise((resolve, reject) => {

            this._getChangesetId()
            .then(
                changesetId => {

                    return this._sendXml(changesetId);
                },
                err => {

                    reject(err);
                }
            )
            .then(
                nodeId => {

                    resolve(nodeId);
                },
                err => {

                    reject(err);
                }
            );
        });
    }

    /**
     * Builds a changeset XML.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {string} - The changeset XML.
     */
    _buildChangesetXml() {

        var xml = new DOMImplementation().createDocument('', '', null),
        osmElement = xml.createElement('osm'),
        changesetElement = xml.createElement('changeset'),
        createdByElement = xml.createElement('tag'),
        commentElement = xml.createElement('tag');

        createdByElement.setAttribute('k', 'created_by');
        createdByElement.setAttribute('v', this._changesetCreatedBy);
        changesetElement.appendChild(createdByElement);

        commentElement.setAttribute('k', 'comment');
        commentElement.setAttribute('v', this._changesetComment);
        changesetElement.appendChild(commentElement);

        osmElement.appendChild(changesetElement);
        xml.appendChild(osmElement);

        return new XMLSerializer().serializeToString(xml);
    }

    /**
     * Builds a node XML.
     *
     * @author Guillaume AMAT
     * @access private
     * @param {number} changesetId - The changeset ID used during the call to OSM.
     * @return {string} - The node XML.
     */
    _buildXml(changesetId) {

        var xml = new DOMImplementation().createDocument('', '', null),
        osmElement = xml.createElement('osm'),
        nodeElement = xml.createElement('node');

        nodeElement.setAttribute('changeset', changesetId);
        nodeElement.setAttribute('version', this._version);
        nodeElement.setAttribute('timestamp', this._timestamp);
        nodeElement.setAttribute('uid', this._uid);
        nodeElement.setAttribute('display_name', this._displayName);
        nodeElement.setAttribute('lat', this._lat);
        nodeElement.setAttribute('lon', this._lon);

        this._tags.forEach(tag => {

            if (!tag.key || !tag.value) {
                return false;
            }

            let tagElement = xml.createElement('tag');

            tagElement.setAttribute('k', tag.key);
            tagElement.setAttribute('v', tag.value);
            nodeElement.appendChild(tagElement);
        });

        osmElement.appendChild(nodeElement);
        xml.appendChild(osmElement);

        return new XMLSerializer().serializeToString(xml);
    }

    /**
     * Asks OSM to create a changeset and return its ID.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {promise}
     */
    _createChangeset() {

        var changesetXml = this._buildChangesetXml(this._changesetCreatedBy, this._changesetComment);

        return new Promise((resolve, reject) => {

            this._auth.xhr({

                'method': 'PUT',
                'path': '/api/0.6/changeset/create',
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
                'content': changesetXml
            },
            (err, changesetId) => {

                if (err) {

                    console.error('ERROR on put changeset: ' + err.response);
                    return reject(err);
                }

                resolve( parseInt(changesetId) );
            });
        });
    }

    /**
     * Checks if a given changeset is still opened at OSM.
     *
     * @author Guillaume AMAT
     * @access private
     * @param {number} changesetId - The changeset ID used during the call to OSM.
     * @return {promise}
     */
    _isChangesetStillOpen(changesetId) {

        return new Promise((resolve, reject) => {

            this._auth.xhr({

                'method': 'GET',
                'path': '/api/0.6/changeset/'+ changesetId.toString(),
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
            },
            (err, xml) => {

                if (err) {

                    return reject(err);
                }

                var isOpened = xml.getElementsByTagName('changeset')[0].getAttribute('open');

                if (isOpened === 'false') {

                    return reject(err);
                }

                resolve(changesetId);
            });
        });
    }

    /**
     * Gets a changeset ID from an old one or a creation.
     *
     * @author Guillaume AMAT
     * @access private
     * @return {promise}
     */
    _getChangesetId() {

        var changesetId = parseInt( sessionStorage.getItem('osmEdit-changesetId') );

        if ( changesetId ) {

            return this._isChangesetStillOpen(changesetId)
            .then(
                changesetId => {

                    return changesetId;
                },
                err => {

                    sessionStorage.removeItem('osmEdit-changesetId');
                    return this._getChangesetId();
                }
            );
        }
        else {

            return this._createChangeset()
            .then(
                changesetId => {

                    sessionStorage.setItem('osmEdit-changesetId', changesetId);
                    return changesetId;
                },
                err => {

                    sessionStorage.removeItem('osmEdit-changesetId');
                    return this._getChangesetId();
                }
            );
        }
    }

     /**
      * Sends the node xml to OSM.
      *
      * @author Guillaume AMAT
      * @access private
      * @param {number} changesetId - The changeset ID to use during the sending.
      * @return {promise}
      */
    _sendXml(changesetId) {

        var data,
        method = 'PUT',
        path = `/api/0.6/${this._type}/create`,
        xml = this._buildXml(changesetId);

        if (this._id) {
            path = `/api/0.6/${this._type}/${this._id}`;
        }

        return new Promise((resolve, reject) => {
            this._auth.xhr({
                'method': method,
                'path': path,
                'options': {
                    'header': {
                        'Content-Type': 'text/xml'
                    }
                },
                'content': xml,
            },
            (err, nodeId) => {
                if (err) {
                    reject(err);
                    return;
                }

                resolve(nodeId);
            });
        });
    }
}
<|endoftext|>