<commit_before>'use strict';

/**
 * @ngdoc service
 * @name flowsimUiApp.ports
 * @description
 * # ports
 * Service in the flowsimUiApp.
 */
angular.module('flowsimUiApp')
  .factory('Ports', function(fgConstraints, ETHERNET) {

var defaultPorts  = 24;
var defaultMAC    = '00:00:00:00:00:01';
var defaultName   = 'eth';
var defaultSpeed  = '1_gbps';
var defaultMedium = 'Fiber';
var defaultMode   = 'full_duplex';
var defaultSpeed  = '10_gbps';

var Port = {};

Port.TIPS = {
  port_id: 'Ethernet port identifier',
  mac:     'MAC address used by port in spanning tree',
  name:    'display name of the port',
  speed:   'physical layer speed of port',
  mode:    'physical layer transmission mode',
  medium:  'physical layer medium of port'
};

Port.TESTS = {
  mac: ETHERNET.isMAC,
  name: function(v) { return /[a-zA-Z_][a-zA-Z_0-9]*/.test(v) }
}

Port.Capabilities = function(port) {
  if(typeof port === 'number') {
    // default construction
    this.port_id = port;
    this.mac     = defaultMAC;
    this.name    = defaultName + this.port_id;
    this.speed   = defaultSpeed;
    this.mode    = defaultMode;
    this.medium  = defaultMedium;
  } else {
    // copy construction
    if(!port instanceof Port.Capabilities) {
      _.extend(this, port);
    }
    this.port_id = port.port_id;
    this.mac     = port.mac;
    this.name    = port.name;
    this.speed   = port.speed;
    this.mode    = port.mode;
    this.medium  = port.medium;
  }
}

Port.Capabilities.prototype.openflow_1_0 = function(speeds, mediums, modes) {
  // ensure speed/medium/mode are in param sets
};

Port.Configuration = function(port) {
  if(port) {
    if(port instanceof Port.Capabilities) {
      // Capability constructor
      this.capabilities = port;
    } else if(port instanceof Port.Configuration) {
      // Copy constructor
    } else {
      // JSON constructor
      _.extend(this, port);
    }
  } else {
    // default constructor
  }
}

Port.Speeds = [{
  label: '10 Mbps',
  value: '10_mbps'
}, {
  label: '100 Mbps',
  value: '100_mbps'
}, {
  label: '1 Gbps',
  value: '1_gbps'
}, {
  label: '10 Gbps',
  value: '10_gbps'
}, {
  label: '40 Gbps',
  value: '40_gbps'
}, {
  label: '100 Gbps',
  value: '100_gbps'
}, {
  label: '1 Tbps',
  value: '1_tbps'
}];

Port.Modes = [{
  label: 'Half Duplex',
  value: 'half_duplex'
}, {
  label: 'Full Duplex',
  value: 'full_duplex'
}];

Port.Mediums = [
  'Copper',
  'Fiber'
];

function Capabilities(ports) {
  if(ports) {
    // copy constructor
    if(!ports instanceof Ports) {
      _.extend(this, ports);
    }
    this.n_ports = ports.n_ports;
    this.ports = _.map(ports.ports, function(port) {
      return new Port.Capabilities(port);
    });
    this.speeds = _.clone(ports.speeds);
    this.modes = _.clone(ports.modes);
    this.mediums = _.clone(ports.mediums);
    this.vports = _.clone(ports.vports);
  } else {
    // default constructor
    this.n_ports = defaultPorts;
    this.ports = _.map(_.range(this.n_ports), function(id) {
      return new Port.Capabilities(id);
    });
    this.vports = {
      port_stats: true,
      stp:        false,
      in_port:    true,
      table:      true,
      normal:     true,
      flood:      true,
      all:        true,
      controller: true,
      local:      true,
      any:        true,
      none:       true
    };
    this.speeds = {
      '10_mbps': true,
      '100_mbps': true,
      '1_gbps': true,
      '10_gbps': true,
      '40_gbps': true,
      '100_bgps': true,
      '1_tbps': true
    };
    this.mediums = {
      'Copper': true,
      'Fiber': true
    };
    this.modes = {
      'half_duplex': true,
      'full_duplex': true
    };
  }
}

Capabilities.prototype.rebuild = function() {
  var i;
  if(this.n_ports === this.ports.length) {
    return;
  } else if(this.n_ports < this.ports.length) {
    this.ports.splice(this.n_ports, this.ports.length-this.n_ports);
  } else {
    for(i=this.ports.length; i<this.n_ports; ++i) {
      this.ports.push(new Port.Capabilities(i));
    }
  }
}

Capabilities.prototype.openflow_1_0 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        true, // optional
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true,
    any:        false,
    none:       true
  };
  _.each(this.ports, function(port) {
    port.openflow_1_0(this.speeds, this.mediums, this.modes);
  });

  // set number of ports?
  // construct ports?
};

Capabilities.prototype.openflow_1_1 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_2 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_3 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_4 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

function Configuration(ports) {
  if(ports) {
    if(ports instanceof Capabilities) {
      // capability constructor
    } else if(ports instanceof Configuration) {
      // copy constructor
    } else {
      // JSON constructor
      _.extend(this, ports);
    }
  } else {
    // default constructor
  }
}

var TIPS = {
  Port: Port.TIPS,
};

var TESTS = {
  Port: Port.TESTS,
};

return {
  Capabilities: Capabilities,
  Configuration: Configuration,
  TIPS: TIPS,
  TESTS: TESTS,
  MEDIUMS: Port.MEDIUMS,
  MODES: Port.MODES,
  SPEEDS: Port.SPEEDS
};

});
<commit_msg>updated port profile<commit_after>'use strict';

/**
 * @ngdoc service
 * @name flowsimUiApp.ports
 * @description
 * # ports
 * Service in the flowsimUiApp.
 */
angular.module('flowsimUiApp')
  .factory('Ports', function(fgConstraints, ETHERNET) {

var defaultPorts  = 24;
var defaultMAC    = '00:00:00:00:00:01';
var defaultName   = 'eth';
var defaultSpeed  = '1_gbps';
var defaultMedium = 'Fiber';
var defaultMode   = 'full_duplex';
var defaultSpeed  = '10_gbps';

var Port = {};

Port.TIPS = {
  port_id: 'Ethernet port identifier',
  mac:     'MAC address used by port in spanning tree',
  name:    'display name of the port',
  speed:   'physical layer speed of port',
  mode:    'physical layer transmission mode',
  medium:  'physical layer medium of port'
};

Port.TESTS = {
  mac: ETHERNET.isMAC,
  name: function(v) { return /[a-zA-Z_][a-zA-Z_0-9]*/.test(v) }
}

Port.Capabilities = function(port) {
  if(typeof port === 'number') {
    // default construction
    this.port_id = port;
    this.mac     = defaultMAC;
    this.name    = defaultName + this.port_id;
    this.speed   = defaultSpeed;
    this.mode    = defaultMode;
    this.medium  = defaultMedium;
  } else {
    // copy construction
    if(!port instanceof Port.Capabilities) {
      _.extend(this, port);
    }
    this.port_id = port.port_id;
    this.mac     = port.mac;
    this.name    = port.name;
    this.speed   = port.speed;
    this.mode    = port.mode;
    this.medium  = port.medium;
  }
}

Port.Capabilities.prototype.openflow_1_0 = function(speeds, mediums, modes) {
  this.speeds = {
      '10_mbps': true,
      '100_mbps': true,
      '1_gbps': true,
      '10_gbps': true,
      '40_gbps': false,
      '100_gbps': false,
      '1_tbps': false
    };

    // ensure speed/medium/mode are in param sets
    if (this.speeds[this.speed] === false) {
      this.speeds = '10_gbps';
    }
};

Port.Configuration = function(port) {
  if(port) {
    if(port instanceof Port.Capabilities) {
      // Capability constructor
      this.capabilities = port;
    } else if(port instanceof Port.Configuration) {
      // Copy constructor
    } else {
      // JSON constructor
      _.extend(this, port);
    }
  } else {
    // default constructor
  }
}

Port.Speeds = [{
  label: '10 Mbps',
  value: '10_mbps'
}, {
  label: '100 Mbps',
  value: '100_mbps'
}, {
  label: '1 Gbps',
  value: '1_gbps'
}, {
  label: '10 Gbps',
  value: '10_gbps'
}, {
  label: '40 Gbps',
  value: '40_gbps'
}, {
  label: '100 Gbps',
  value: '100_gbps'
}, {
  label: '1 Tbps',
  value: '1_tbps'
}];

Port.Modes = [{
  label: 'Half Duplex',
  value: 'half_duplex'
}, {
  label: 'Full Duplex',
  value: 'full_duplex'
}];

Port.Mediums = [
  'Copper',
  'Fiber'
];

function Capabilities(ports) {
  if(ports) {
    // copy constructor
    if(!ports instanceof Ports) {
      _.extend(this, ports);
    }
    this.n_ports = ports.n_ports;
    this.ports = _.map(ports.ports, function(port) {
      return new Port.Capabilities(port);
    });
    this.speeds = _.clone(ports.speeds);
    this.modes = _.clone(ports.modes);
    this.mediums = _.clone(ports.mediums);
    this.vports = _.clone(ports.vports);
  } else {
    // default constructor
    this.n_ports = defaultPorts;
    this.ports = _.map(_.range(this.n_ports), function(id) {
      return new Port.Capabilities(id);
    });
    this.vports = {
      port_stats: true,
      stp:        false,
      in_port:    true,
      table:      true,
      normal:     true,
      flood:      true,
      all:        true,
      controller: true,
      local:      true,
      any:        true,
      none:       true
    };
    this.speeds = {
      '10_mbps': true,
      '100_mbps': true,
      '1_gbps': true,
      '10_gbps': true,
      '40_gbps': true,
      '100_gbps': true,
      '1_tbps': true
    };
    this.mediums = {
      'Copper': true,
      'Fiber': true
    };
    this.modes = {
      'half_duplex': true,
      'full_duplex': true
    };
  }
}

Capabilities.prototype.rebuild = function() {
  var i;
  if(this.n_ports === this.ports.length) {
    return;
  } else if(this.n_ports < this.ports.length) {
    this.ports.splice(this.n_ports, this.ports.length-this.n_ports);
  } else {
    for(i=this.ports.length; i<this.n_ports; ++i) {
      this.ports.push(new Port.Capabilities(i));
    }
  }
}

Capabilities.prototype.openflow_1_0 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        true, // optional
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true,
    any:        false,
    none:       true
  };
  _.each(this.ports, function(port) {
    port.openflow_1_0(this.speeds, this.mediums, this.modes);
  });

  // set number of ports?
  // construct ports?
};

Capabilities.prototype.openflow_1_1 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_2 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_3 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

Capabilities.prototype.openflow_1_4 = function() {
  // Ports
  this.vports = {
    port_stats: true,
    stp:        false,
    in_port:    true,
    table:      true,
    normal:     true, // optional
    flood:      true, // optional
    all:        true,
    controller: true,
    local:      true, // optional
    any:        true,
    none:       false
  };
};

function Configuration(ports) {
  if(ports) {
    if(ports instanceof Capabilities) {
      // capability constructor
    } else if(ports instanceof Configuration) {
      // copy constructor
    } else {
      // JSON constructor
      _.extend(this, ports);
    }
  } else {
    // default constructor
  }
}

var TIPS = {
  Port: Port.TIPS,
};

var TESTS = {
  Port: Port.TESTS,
};

return {
  Capabilities: Capabilities,
  Configuration: Configuration,
  TIPS: TIPS,
  TESTS: TESTS,
  MEDIUMS: Port.MEDIUMS,
  MODES: Port.MODES,
  SPEEDS: Port.SPEEDS
};

});
<|endoftext|>


<commit_before>import React from 'react';

// Redux
import withRedux from 'next-redux-wrapper';
import { initStore } from 'store';

// Components
import Page from 'layout/page';
import Layout from 'layout/layout/layout-app';
import Banner from 'components/app/common/Banner';
import Breadcrumbs from 'components/ui/Breadcrumbs';
import ContactUsForm from 'components/app/common/ContactUs/ContactUsForm';
import { Link } from 'routes';

class ContactUs extends Page {
  render() {
    return (
      <Layout
        title="Contact us"
        description="Contact us description"
        url={this.props.url}
        user={this.props.user}
        pageHeader
      >

                  <Breadcrumbs
                    items={[{ name: 'About', route: 'about' }]}
                  />

Contact us

                  Questions, comments, or feedback?
                  Help us strengthen Resource Watch.

                <ContactUsForm />

Media inquiries

Please contact Rose Gilroy at rose.gilroy@wri.org or Lauren Zelin at lzelin@wri.org for media inquiries.

                <Banner className="-text-center" bgImage={'/static/images/backgrounds/partners-02@2x.jpg'}>

                    Let’s build a more sustainable
 world together.

                  <Link to="about_partners">
                    Partners
                  </Link>
                </Banner>

      </Layout>
    );
  }
}

export default withRedux(initStore)(ContactUs);
<commit_msg>Update ContactUs.js<commit_after>import React from 'react';

// Redux
import withRedux from 'next-redux-wrapper';
import { initStore } from 'store';

// Components
import Page from 'layout/page';
import Layout from 'layout/layout/layout-app';
import Banner from 'components/app/common/Banner';
import Breadcrumbs from 'components/ui/Breadcrumbs';
import ContactUsForm from 'components/app/common/ContactUs/ContactUsForm';
import { Link } from 'routes';

class ContactUs extends Page {
  render() {
    return (
      <Layout
        title="Contact us"
        description="Contact us description"
        url={this.props.url}
        user={this.props.user}
        pageHeader
      >

                  <Breadcrumbs
                    items={[{ name: 'About', route: 'about' }]}
                  />

Contact us

                  Questions, comments, or feedback?
                  Help us strengthen Resource Watch.

                <ContactUsForm />

Media inquiries

Please contact Corey Filiault at corey.filiault@wri.org or Lauren Zelin at lzelin@wri.org for media inquiries.

                <Banner className="-text-center" bgImage={'/static/images/backgrounds/partners-02@2x.jpg'}>

                    Let’s build a more sustainable
 world together.

                  <Link to="about_partners">
                    Partners
                  </Link>
                </Banner>

      </Layout>
    );
  }
}

export default withRedux(initStore)(ContactUs);
<|endoftext|>


<commit_before>var querystring = require('querystring');
var https = require('https');
var V = require('argument-validator');

module.exports = function (email, token, options) {
    options = options || { };
    options.email = email;
    options.token = token;

    return new PagseguroCheckout(options);
};

var paymentUrl = "https://pagseguro.uol.com.br/v2/checkout/payment.html";

// I'm aware of what can happen if we try to parse the XML/HTML with regex
// (you should be aware too :)) http://stackoverflow.com/a/1732454/1617888
// BUT, we just need to get some simple data from the response XML
// we just need to get the checkout code OR the error messages, so these
// regex can help us to do a good job here.
var regex = {
    checkoutCode: /<checkout><code>([\w\W]+)<\/code>/,
    errors: /<errors>([\w\W]+)<\/errors>/,
    codes: /<code>([\d]+)<\/code>/g,
    messages: /<message>([\s\S]*?)<\/message>/g
};

var defaultOptions = {
    maxUses: 1,
    maxAge: 1800,
    currency: 'BRL',
    extraAmount:  null,
    redirectUrl: null,
    notificationUrl: null,
    reference: null,
    sender: null,
    shipping: null,
    receiver: null
};

function PagseguroCheckout (opts) {
    V.keysWithString(opts, [ 'email', 'token' ], 'options');

    this.checkout = {};

    this.email = opts.email;
    this.token = opts.token;
    this.items = opts.items || [ ];

    // here we build the acessors, for every option we have a cool chaining
    // method that can be used to retrieve the value or set it.
    // e.g:
    // var maxUses = this.maxUses();
    // this.maxUses(5).maxAge(100);
    for (var key in defaultOptions) {
        (function(key) {
            this[key] = function(v) {
                return this._getOrSetCheckoutProperty(key, v);
            }
            this.checkout[key] = opts[key] || defaultOptions[key];
        }).call(this, key);
    }
};

PagseguroCheckout.prototype = {
    constructor: PagseguroCheckout.constructor,

    add: function (item) {
        V.keysWithString(item, [ 'description' ], 'item');
        V.keysWithNumber(item, [ 'quantity', 'weight' ], 'item');

        // ensure always two decimal points for amount
        item.amount = Number(item.amount).toFixed(2)

        this.items.push(item);
        return this;
    },

    xml: function () {
        var xml = [ '<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>' ];
        var checkout = [];

        for (var key in this.checkout) {
            var value = this.checkout[key];

            if (value == null) continue;
            if (V.isObject(value)) value = objectToXml(value);

            checkout.push(keyValueToXml(key, value));
        }

        var items = [ ];
        for (var i = 0; i < this.items.length; i++) {
            items.push(keyValueToXml('item', objectToXml(this.items[i])));
        }

        checkout.push(keyValueToXml("items", items.join("")))
        xml.push(keyValueToXml("checkout", checkout.join("")))

        return xml.join("");
    },

    // if found any errors it will return
    // callback(errorMessages|rawResponse, responseObject)
    // otherwise: callback(null, { code, url })
    request: function (callback) {
        V.instanceOf(Function, callback, 'callback');
        if (this.items.length == 0) {
            return callback("Items must have at least one item.");
        }

        var qs = querystring.stringify({ email: this.email, token: this.token });
        var body = this.xml();
        var options = {
            host: "ws.pagseguro.uol.com.br",
            path: "/v2/checkout?" + qs,
            port: 443,
            method: "POST",
            headers: {
                'Cookie': "cookie",
                'Content-Type': 'application/xml; charset=UTF-8',
                'Content-Length': Buffer.byteLength(body)
            }
        };

        var req = https.request(options, function (res) {
            var xml = '';
            res.on('data', function (chunk) { xml += chunk; });
            res.on("end", function() { onRequestDone(xml, res, callback); });
        });

        req.on('error', callback).write(body);
        req.end();
    },

    _getOrSetCheckoutProperty: function (attr, value) {
        V.string(attr, 'attr');

        if (value !== undefined) {
            this.checkout[attr] = value;
            return this;
        }

        return this.checkout[attr];
    }
};

var keyValueToXml = function (key, value) {
    V.string(key, 'key');
    return "<" + key + ">" + value + "</" + key + ">";
};

var objectToXml = function (obj) {
    V.object(obj, 'obj');

    var xml = [ ];
    for (var key in obj) {
        var value = obj[key];
        if (V.isObject(value)) value = objectToXml(value);

        xml.push(keyValueToXml(key, value));
    }

    return xml.join("");
};

var onRequestDone = function (xml, res, callback) {
    V.string(xml, 'xml');
    V.object(res, 'response object');
    V.instanceOf(Function, callback, 'callback');

    var code = xml.match(regex.checkoutCode);
    if (code && code[1]) {
        var url = paymentUrl + "?" + querystring.stringify({ code: code[1] });
        return callback(null, { code: code[1], url: url });
    }

    var errors = xml.match(regex.errors);

    // not found any <errors>*</errors> and not found checkout code,
    // in that case we return the original xml and the response object
    if (!errors) return callback(xml, res);

    errors = errors[1];

    // We need this for regex with global flag
    // and we need /g to get all error messages/codes
    // http://stackoverflow.com/a/1520853/1617888
    regex.codes.lastIndex = 0;
    regex.messages.lastIndex = 0;

    var codes = errors.match(regex.codes);
    var messages = errors.match(regex.messages);

    // unable to find an error message/code OR
    // we found more/less code than messages
    // In this (odd)case we return to the caller
    // the full xml and response object
    // although, thats a very weird case, and so far I have not saw that
    if (codes.length == 0 || codes.length !== messages.length) {
        return callback(xml, res);
    }

    errors = [];
    for (var i = 0; i < codes.length; i++) {
        errors.push({
            code: codes[i].replace(/<\/?code>/g, ''),
            message: messages[i].replace(/<\/?message>/g, '')
        });
    }

    callback(errors, res);
};
<commit_msg>add validate for id, amount<commit_after>var querystring = require('querystring');
var https = require('https');
var V = require('argument-validator');

module.exports = function (email, token, options) {
    options = options || { };
    options.email = email;
    options.token = token;

    return new PagseguroCheckout(options);
};

var paymentUrl = "https://pagseguro.uol.com.br/v2/checkout/payment.html";

var defaultOptions = {
    maxUses: 1,
    maxAge: 1800,
    currency: 'BRL',
    extraAmount:  null,
    redirectUrl: null,
    notificationUrl: null,
    reference: null,
    sender: null,
    shipping: null,
    receiver: null
};

function PagseguroCheckout (opts) {
    V.keysWithString(opts, [ 'email', 'token' ], 'options');

    this.checkout = {};

    this.email = opts.email;
    this.token = opts.token;
    this.items = opts.items || [ ];

    // here we build the acessors, for every option we have a cool chaining
    // method that can be used to retrieve the value or set it.
    // e.g:
    // var maxUses = this.maxUses();
    // this.maxUses(5).maxAge(100);
    for (var key in defaultOptions) {
        (function(key) {
            this[key] = function(v) {
                return this._getOrSetCheckoutProperty(key, v);
            }
            this.checkout[key] = opts[key] || defaultOptions[key];
        }).call(this, key);
    }
};

PagseguroCheckout.prototype = {
    constructor: PagseguroCheckout.constructor,

    add: function (item) {
        V.keys(item, [ 'id', 'amount' ], 'item');
        V.keysWithString(item, [ 'description' ], 'item');
        V.keysWithNumber(item, [ 'quantity', 'weight' ], 'item');

        // ensure always two decimal points for amount
        item.amount = Number(item.amount).toFixed(2)

        this.items.push(item);
        return this;
    },

    xml: function () {
        var xml = [ '<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>' ];
        var checkout = [];

        for (var key in this.checkout) {
            var value = this.checkout[key];

            if (value == null) continue;
            if (V.isObject(value)) value = objectToXml(value);

            checkout.push(keyValueToXml(key, value));
        }

        var items = [ ];
        for (var i = 0; i < this.items.length; i++) {
            items.push(keyValueToXml('item', objectToXml(this.items[i])));
        }

        checkout.push(keyValueToXml("items", items.join("")))
        xml.push(keyValueToXml("checkout", checkout.join("")))

        return xml.join("");
    },

    // if found any errors it will return
    // callback(errorMessages|rawResponse, responseObject)
    // otherwise: callback(null, { code, url })
    request: function (callback) {
        V.instanceOf(Function, callback, 'callback');
        if (this.items.length == 0) {
            return callback("Items must have at least one item.");
        }

        var qs = querystring.stringify({ email: this.email, token: this.token });
        var body = this.xml();
        var options = {
            host: "ws.pagseguro.uol.com.br",
            path: "/v2/checkout?" + qs,
            port: 443,
            method: "POST",
            headers: {
                'Cookie': "cookie",
                'Content-Type': 'application/xml; charset=UTF-8',
                'Content-Length': Buffer.byteLength(body)
            }
        };

        var req = https.request(options, function (res) {
            var xml = '';
            res.on('data', function (chunk) { xml += chunk; });
            res.on("end", function() { onRequestDone(xml, res, callback); });
        });

        req.on('error', callback).write(body);
        req.end();
    },

    _getOrSetCheckoutProperty: function (attr, value) {
        V.string(attr, 'attr');

        if (value !== undefined) {
            this.checkout[attr] = value;
            return this;
        }

        return this.checkout[attr];
    }
};

var keyValueToXml = function (key, value) {
    V.string(key, 'key');
    return "<" + key + ">" + value + "</" + key + ">";
};

var objectToXml = function (obj) {
    V.object(obj, 'obj');

    var xml = [ ];
    for (var key in obj) {
        var value = obj[key];
        if (V.isObject(value)) value = objectToXml(value);

        xml.push(keyValueToXml(key, value));
    }

    return xml.join("");
};

// I'm aware of what can happen if we try to parse the XML/HTML with regex
// (you should be aware too :)) http://stackoverflow.com/a/1732454/1617888
// BUT, we just need to get some simple data from the response XML
// we just need to get the checkout code OR the error messages, so these
// regex can help us to do a good job here.
var regex = {
    checkoutCode: /<checkout><code>([\w\W]+)<\/code>/,
    errors: /<errors>([\w\W]+)<\/errors>/,
    codes: /<code>([\d]+)<\/code>/g,
    messages: /<message>([\s\S]*?)<\/message>/g
};

var onRequestDone = function (xml, res, callback) {
    V.string(xml, 'xml');
    V.object(res, 'response object');
    V.instanceOf(Function, callback, 'callback');

    var code = xml.match(regex.checkoutCode);
    if (code && code[1]) {
        var url = paymentUrl + "?" + querystring.stringify({ code: code[1] });
        return callback(null, { code: code[1], url: url });
    }

    var errors = xml.match(regex.errors);

    // not found any <errors>*</errors> and not found checkout code,
    // in that case we return the original xml and the response object
    if (!errors) return callback(xml, res);

    errors = errors[1];

    // We need this for regex with global flag
    // and we need /g to get all error messages/codes
    // http://stackoverflow.com/a/1520853/1617888
    regex.codes.lastIndex = 0;
    regex.messages.lastIndex = 0;

    var codes = errors.match(regex.codes);
    var messages = errors.match(regex.messages);

    // unable to find an error message/code OR
    // we found more/less code than messages
    // In this (odd)case we return to the caller
    // the full xml and response object
    // although, thats a very weird case, and so far I have not saw that
    if (codes.length == 0 || codes.length !== messages.length) {
        return callback(xml, res);
    }

    errors = [];
    for (var i = 0; i < codes.length; i++) {
        errors.push({
            code: codes[i].replace(/<\/?code>/g, ''),
            message: messages[i].replace(/<\/?message>/g, '')
        });
    }

    callback(errors, res);
};
<|endoftext|>


<commit_before>var Drone = {};

Drone.View = Drone.Controller = function (classObject) {
  return function (attributes) {
      var F = function () {};

      for (var key in attributes) {
        if (attributes.hasOwnProperty(key)) {
          classObject[key] = attributes[key];
        }
      }

      F.prototype = classObject;

      var instance = new F();
      if (instance.init) {
        instance.init();
      };

      return instance;
  };
};

Drone.Initializer = function (classObject, autoexec) {
  classObject.exec = function () {
    for (var key in classObject) {
      if (classObject.hasOwnProperty(key) && key != "exec") {
        classObject[key]();
      }
    }
  };

  if (autoexec === true) {
    $().ready(function () {
      classObject.exec();
    });
  }

  return classObject;
};
<commit_msg>Adding version<commit_after>/*!
 * Drone MVCI Framework for Javascript
 * https://github.com/andremendonca/dronejs
 *
 * Depends on jQuery
 *
 * Version 0.0.1-alpha
 */

var Drone = {};

Drone.View = Drone.Controller = function (classObject) {
  return function (attributes) {
      var F = function () {};

      for (var key in attributes) {
        if (attributes.hasOwnProperty(key)) {
          classObject[key] = attributes[key];
        }
      }

      F.prototype = classObject;

      var instance = new F();
      if (instance.init) {
        instance.init();
      };

      return instance;
  };
};

Drone.Initializer = function (classObject, autoexec) {
  classObject.exec = function () {
    for (var key in classObject) {
      if (classObject.hasOwnProperty(key) && key != "exec") {
        classObject[key]();
      }
    }
  };

  if (autoexec === true) {
    $().ready(function () {
      classObject.exec();
    });
  }

  return classObject;
};
<|endoftext|>


<commit_before>var app = angular.module('starter', ['ionic', 'pouchdb']);
app.run(['$ionicPlatform', function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }

    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
}]);

app.factory('PouchDBProvider', ['pouchDB', function (pouchDB) {
  var db = null;
  var changeCallback = function() {};
  var initializeDB = function() {
    db = pouchDB('todos');
    var remoteCouch = 'http://192.168.0.115:5984/todos';
    db.replicate.to(remoteCouch, {live: true});
    db.replicate.from(remoteCouch, {live: true});

    db.changes({
      since: 'now',
      live: true
    }).on('change', function() {
      changeCallback();
    });
  };

  var onChangeCallback = function(callback) {
    changeCallback = callback;
  }

  var save = function(doc, callback) {
    db.put(doc, callback);
  };

  var findAll = function(callback) {
    db.allDocs({include_docs: true, descending: true}, function(err, results) {
      callback(err, results.rows.map(function(row) {return row.doc}));
    });
  };

  initializeDB();
  return {
    onChangeCallback: onChangeCallback,
    save: save,
    findAll: findAll
  };
}])

app.controller('ExampleController', ['$scope', '$ionicPopup', 'PouchDBProvider',
  function ($scope, $ionicPopup, PouchDBProvider) {
    $scope.todos = [];

    $scope.create = function() {
      $ionicPopup.prompt({
        title: 'Enter a new TODO item',
        intputType: 'text'
      }).then(function(result) {
        if (!!result) {
          var todo = {_id: new Date().toISOString(), title: result, completed: false };
          PouchDBProvider.save(todo);
        }
      });
    };

    $scope.init = function() {
      PouchDBProvider.onChangeCallback(fullFillTodos);
      fullFillTodos();
    }

    var fullFillTodos = function() {
      PouchDBProvider.findAll(function(err, results) {
        $scope.todos = results;
        $scope.$apply();
      });
    };

  }]);<commit_msg>parametrizacao da conexao<commit_after>var app = angular.module('starter', ['ionic', 'pouchdb']);
app.run(['$ionicPlatform', function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }

    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
}]);

// #####################################################
// Alterar as constantes abaixo para os devidos valores.
// #####################################################
app.constant('COUCHDB_URL', 'http://localhost:5984');
app.constant('COUCHDB_COLLECTION', 'todos');

app.factory('PouchDBProvider', ['pouchDB', 'COUCHDB_URL', 'COUCHDB_COLLECTION',
  function (pouchDB, COUCHDB_URL, COUCHDB_COLLECTION) {
    var db = null;
    var changeCallback = function() {};
    var initializeDB = function() {
      db = pouchDB(COUCHDB_COLLECTION);
      db.replicate.to(COUCHDB_URL, {live: true});
      db.replicate.from(COUCHDB_URL, {live: true});

      db.changes({
        since: 'now',
        live: true
      }).on('change', function() {
        changeCallback();
      });
    };

    var onChangeCallback = function(callback) {
      changeCallback = callback;
    }

    var save = function(doc, callback) {
      db.put(doc, callback);
    };

    var findAll = function(callback) {
      db.allDocs({include_docs: true, descending: true}, function(err, results) {
        callback(err, results.rows.map(function(row) {return row.doc}));
      });
    };

    initializeDB();
    return {
      onChangeCallback: onChangeCallback,
      save: save,
      findAll: findAll
    };
}])

app.controller('ExampleController', ['$scope', '$ionicPopup', 'PouchDBProvider',
  function ($scope, $ionicPopup, PouchDBProvider) {
    $scope.todos = [];

    $scope.create = function() {
      $ionicPopup.prompt({
        title: 'Enter a new TODO item',
        intputType: 'text'
      }).then(function(result) {
        if (!!result) {
          var todo = {_id: new Date().toISOString(), title: result, completed: false };
          PouchDBProvider.save(todo);
        }
      });
    };

    $scope.init = function() {
      PouchDBProvider.onChangeCallback(fullFillTodos);
      fullFillTodos();
    }

    var fullFillTodos = function() {
      PouchDBProvider.findAll(function(err, results) {
        $scope.todos = results;
        $scope.$apply();
      });
    };

  }]);<|endoftext|>


<commit_before>/**
 * Created with JetBrains PhpStorm.
 * User: taoqili
 * Date: 13-2-21
 * Time: 下午1:31
 * To change this template use File | Settings | File Templates.
 */
function getTable(str) {
    var div = document.getElementById("testTable");
    if(!div){
        div = document.createElement("div");
        div.id = "testTable";
        document.body.appendChild(div);
    }
    div.innerHTML = "
" + str + "
";
    return div.firstChild;
}
UT = UE.UETable;
test("create UETable",function(){
    var table = getTable("
ddd
"),
        ut = new UT(table);
    ok(ut.table === table,"UT对象创建成功");
    ok(ut.colsNum == 1 && ut.rowsNum == 1,"单元格行、列数为1");
});

test("getMaxRows",function(){
    var table = getTable("
1	2	3
" +
                         "
1	2	3
"),
        ut = new UT(table);
    var maxRows = ut.getMaxRows();
    equal(maxRows,2,"最大行数为2");
    table = getTable("
1	2	3
" +
                     "
2
");
    ut = new UT(table);
    maxRows = ut.getMaxRows();
    equal(maxRows,3,"最大行数为3");
});
test("getMaxCols",function(){
    var table = getTable("
1	2	3
" +
                         "
1	2	3
"),
        ut = new UT(table);
    var maxCols = ut.getMaxCols();
    equal(maxCols,3,"最大列数为3");

    table = getTable("
1	2	3
" +
        "
2
");
    ut = new UT(table);
    maxCols = ut.getMaxCols();
    equal(maxCols,6,"最大列数为6");
});

test("getSameEndPosCells",function(){
    var table = getTable("
1	2	3
" +
                         "
2	3
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0],
        cells1 = ut.getSameEndPosCells(cell,"x"),
        cells2 = ut.getSameEndPosCells(cell,"y");
    ok(cells1.length == 1, "获取到同样X轴结尾位置的cell1个");
    ok(cells2.length == 2, "获取到同样Y轴结尾位置的cell2个");
});

test("getHSideCell",function(){
    var table = getTable("
1	2	3
" +
        "
2	3
"),
        ut = new UT(table);
    var rows = table.rows,
        cell = rows[1].cells[1],
        cell1 = ut.getHSideCell(cell),
        cell2 = ut.getHSideCell(cell,true);
    equal(cell1,rows[1].cells[0],"左边单元格");
    equal(cell2,null,"位于右边缘的单元格无右邻居单元格");
    equal(ut.getHSideCell(rows[0][0]),null,"位于左边缘的单元格无左邻居单元格");
});

test("getVSideCell",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var rows = table.rows,
        cell = rows[1].cells[1],
        cell1 = ut.getVSideCell(cell),
        cell2 = ut.getVSideCell(cell,true),
        cell3 = ut.getVSideCell(cell,true,true);
    equal(cell1,rows[0].cells[2],"上边单元格");
    equal(cell2,null,"位于下边缘的单元格无下邻居单元格");
    equal(cell3,null,"位于左边缘的单元格无左邻居单元格");
});
test("setCellContent",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0];
    ut.setCellContent(cell,"这是测试内容");
    equal(cell.innerHTML,"这是测试内容","设置了正确的内容");
    ut.setCellContent(cell);
    equal(cell.innerHTML,browser.ie ? domUtils.fillChar : "
");
});

test("cloneCell",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);

    var cell = ut.cloneCell(table.rows[0].cells[0]);
    equal(cell.rowSpan,2,"clone了一个2行一列的单元格");
    equal(cell.style.borderTopColor,"green","上边框的颜色将会被下边框取代");
    cell = ut.cloneCell(table.rows[0].cells[0],true);
    ok(cell.rowSpan,1,"忽略被合并单元格时将会充值单元格的rowspan和colspan为1")
});

test("getCellsRange、getCells",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var range = ut.getCellsRange(table.rows[0].cells[1],table.rows[1].cells[0]);
    ok(range.beginRowIndex===0 && range.beginColIndex===1 && range.endRowIndex===1 && range.endColIndex===1,"获取到range")

    var cells = ut.getCells(range);
    ok(cells.length ==2,"获取到2个单元格");
    ok(cells[0] == table.rows[0].cells[1],"第一个单元格存在");
});

test("insertRow、deleterRow",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);

    var cellPrototype = document.createElement("td");
    cellPrototype.innerHTML = "aa";
    cellPrototype.setAttribute("vAlign","top");
    ut.insertRow(2,cellPrototype);
    ok(table.rows.length ===3,"行数变成3行");
    ok(table.rows[2].cells[0].getAttribute("vAlign") =="top","新插入的单元格中包含原型单元格中的属性");

});

test("mergeRight,mergeDown",function(){
    var table = getTable("
1	2	3	4	5	6
" +
                         "
2	3	5	6
"+
                         "
2	3	4	5	6
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[1];
    ut.mergeDown(cell);
    ok(cell.rowSpan===2,"向下合并成功");

    ut.mergeDown(cell);
    ok(cell.rowSpan===3,"向下合并成功");

    cell = cell.previousSibling;
    ut.mergeRight(cell);
    ok(cell.rowSpan===3 && cell.colSpan ===2,"向右合并成功");

    equal(cell.parentNode.rowIndex,0,"合并到了正确的位置" )
});
test("split",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0],
        num = table.getElementsByTagName("td").length;
    ut.splitToCells(cell);
    ok(cell.rowSpan==1&&cell.colSpan==1,"单元格被成功拆分");

    var newNum = table.getElementsByTagName("td").length;
    ok(num+2 ==newNum,"单元格数量增加了2个");

    cell = table.rows[0].cells[3];
    ut.splitToCols(cell);
    ok(cell.colSpan===1 && cell.rowSpan==2 ,"被拆分成了2列");

});

test("selectRow",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    ut.selectRow(1);
    equal(ut.selectedTds.length,table.getElementsByTagName("td").length,"选中了所有单元格")
    var cells = table.rows[1].cells,
        flag = false;
    utils.each(cells,function(cell){
        if(cell.className==""){
            flag = true;
        }
    });
    ok(!flag,"所有单元格都被选中");
    ok(ut.cellsRange.beginRowIndex===0,"cellsRange正确");

});
test("selectTable",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    ut.selectTable();
    ok(ut.selectedTds.length === table.getElementsByTagName("td").length,"选中了整个表格")

});

<commit_msg>table.core<commit_after>/**
 * Created with JetBrains PhpStorm.
 * User: taoqili
 * Date: 13-2-21
 * Time: 下午1:31
 * To change this template use File | Settings | File Templates.
 */
function getTable(str) {
    var div = document.getElementById("testTable");
    if(!div){
        div = document.createElement("div");
        div.id = "testTable";
        document.body.appendChild(div);
    }
    div.innerHTML = "
" + str + "
";
    return div.firstChild;
}
UT = UE.UETable;
test("create UETable",function(){
    var table = getTable("
ddd
"),
        ut = new UT(table);
    ok(ut.table === table,"UT对象创建成功");
    ok(ut.colsNum == 1 && ut.rowsNum == 1,"单元格行、列数为1");
});

test("getMaxRows",function(){
    var table = getTable("
1	2	3
" +
                         "
1	2	3
"),
        ut = new UT(table);
    var maxRows = ut.getMaxRows();
    equal(maxRows,2,"最大行数为2");
    table = getTable("
1	2	3
" +
                     "
2
");
    ut = new UT(table);
    maxRows = ut.getMaxRows();
    equal(maxRows,3,"最大行数为3");
});
test("getMaxCols",function(){
    var table = getTable("
1	2	3
" +
                         "
1	2	3
"),
        ut = new UT(table);
    var maxCols = ut.getMaxCols();
    equal(maxCols,3,"最大列数为3");

    table = getTable("
1	2	3
" +
        "
2
");
    ut = new UT(table);
    maxCols = ut.getMaxCols();
    equal(maxCols,6,"最大列数为6");
});

test("getSameEndPosCells",function(){
    var table = getTable("
1	2	3
" +
                         "
2	3
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0],
        cells1 = ut.getSameEndPosCells(cell,"x"),
        cells2 = ut.getSameEndPosCells(cell,"y");
    ok(cells1.length == 1, "获取到同样X轴结尾位置的cell1个");
    ok(cells2.length == 2, "获取到同样Y轴结尾位置的cell2个");
});

test("getHSideCell",function(){
    var table = getTable("
1	2	3
" +
        "
2	3
"),
        ut = new UT(table);
    var rows = table.rows,
        cell = rows[1].cells[1],
        cell1 = ut.getHSideCell(cell),
        cell2 = ut.getHSideCell(cell,true);
    equal(cell1,rows[1].cells[0],"左边单元格");
    equal(cell2,null,"位于右边缘的单元格无右邻居单元格");
    equal(ut.getHSideCell(rows[0][0]),null,"位于左边缘的单元格无左邻居单元格");
});

test("getVSideCell",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var rows = table.rows,
        cell = rows[1].cells[1],
        cell1 = ut.getVSideCell(cell),
        cell2 = ut.getVSideCell(cell,true),
        cell3 = ut.getVSideCell(cell,true,true);
    equal(cell1,rows[0].cells[2],"上边单元格");
    equal(cell2,null,"位于下边缘的单元格无下邻居单元格");
    equal(cell3,null,"位于左边缘的单元格无左邻居单元格");
});
test("setCellContent",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0];
    ut.setCellContent(cell,"这是测试内容");
    equal(cell.innerHTML,"这是测试内容","设置了正确的内容");
    ut.setCellContent(cell);
    equal(cell.innerHTML,browser.ie ? domUtils.fillChar : "
");
});

test("cloneCell",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);

    var cell = ut.cloneCell(table.rows[0].cells[0]);
    equal(cell.rowSpan,2,"clone了一个2行一列的单元格");
    equal(cell.style.borderTopColor,"green","上边框的颜色将会被下边框取代");
    cell = ut.cloneCell(table.rows[0].cells[0],true);
    ok(cell.rowSpan,1,"忽略被合并单元格时将会充值单元格的rowspan和colspan为1")
});

test("getCellsRange、getCells",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);
    var range = ut.getCellsRange(table.rows[0].cells[1],table.rows[1].cells[0]);
    ok(range.beginRowIndex===0 && range.beginColIndex===1 && range.endRowIndex===1 && range.endColIndex===1,"获取到range")

    var cells = ut.getCells(range);
    ok(cells.length ==2,"获取到2个单元格");
    ok(cells[0] == table.rows[0].cells[1],"第一个单元格存在");
});

test("insertRow、deleterRow",function(){
    var table = getTable("
1	2	3
" +
            "
2	3
"),
        ut = new UT(table);

    var cellPrototype = document.createElement("td");
    cellPrototype.innerHTML = "aa";
    cellPrototype.setAttribute("vAlign","top");
    ut.insertRow(2,cellPrototype);
    ok(table.rows.length ===3,"行数变成3行");
    ok(table.rows[2].cells[0].getAttribute("vAlign") =="top","新插入的单元格中包含原型单元格中的属性");

});

test("mergeRight,mergeDown",function(){
    var table = getTable("
1	2	3	4	5	6
" +
                         "
2	3	5	6
"+
                         "
2	3	4	5	6
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[1];
    ut.mergeDown(cell);
    ok(cell.rowSpan===2,"向下合并成功");

    ut.mergeDown(cell);
    ok(cell.rowSpan===3,"向下合并成功");

    cell = cell.previousSibling;
    ut.mergeRight(cell);
    ok(cell.rowSpan===3 && cell.colSpan ===2,"向右合并成功");

    equal(cell.parentNode.rowIndex,0,"合并到了正确的位置" )
});
test("split",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    var cell = table.rows[0].cells[0],
        num = table.getElementsByTagName("td").length;
    ut.splitToCells(cell);
    ok(cell.rowSpan==1&&cell.colSpan==1,"单元格被成功拆分");

    var newNum = table.getElementsByTagName("td").length;
    ok(num+2 ==newNum,"单元格数量增加了2个");

    cell = table.rows[0].cells[3];
    ut.splitToCols(cell);
    ok(cell.colSpan===1 && cell.rowSpan==2 ,"被拆分成了2列");

});

test("selectRow",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    ut.selectRow(1);
    equal(ut.selectedTds.length,table.getElementsByTagName("td").length,"选中了所有单元格")
    var cells = table.rows[1].cells,
        flag = false;
    utils.each(cells,function(cell){
        if(cell.className==""){
            flag = true;
        }
    });
    ok(!flag,"所有单元格都被选中");
    ok(ut.cellsRange.beginRowIndex===0,"cellsRange正确");

});
test("selectTable",function(){
    var table = getTable("
1	2	3	4	6	7
" +
            "
2	3	6	7
"+
            "
2	3	4	5	6	7
"),
        ut = new UT(table);
    ut.selectTable();
    ok(ut.selectedTds.length === table.getElementsByTagName("td").length,"选中了整个表格")

});

test("sortTable",function(){
    var table = getTable("
01	2	3	4	6	7
" +
            "
11	2	3	4	6	7
" +
            "
21	2	3	4	6	7
"),
        ut = new UT(table);
    ut.sortTable(1,function(a,b){
        return 1;//逆序
    });
    var value = table.rows[0].cells[0].innerHTML;
    equal(value,"21","单元格被逆序");

    ut.sortTable(0,function(td1,td2){
        var value1 = parseInt(td1.innerHTML,10),
            value2 = parseInt(td2.innerHTML,10);
        return value2 - value1;
    })
    value = table.rows[0].cells[0].innerHTML;
    equal(value,"21","按数值从大到小排列");
});

test("setBackground",function(){
    var table = getTable("
01	2	3	4	6	7
" +
            "
11	2	3	4	6	7
" +
            "
21	2	3	4	6	7
"),
        ut = new UT(table);
    ut.setBackground(table.getElementsByTagName("td"),"green");
    var cell = table.rows[1].cells[1];
    ok(cell.style.backgroundColor=="green","单种背景颜色设置成功");

    ut.removeBackground(table.getElementsByTagName("td"));
    ok(cell.style.backgroundColor=="","背景颜色被清除");

    ut.setBackground(table.getElementsByTagName("td"),{
        repeat:true,
        colorList:["green","red"]
    })
    ok(table.rows[0].cells[0].style.backgroundColor == "green","第一行的单元格为绿色");
    ok(table.rows[1].cells[0].style.backgroundColor =="red","第二行的单元格为红色");

});
<|endoftext|>


<commit_before>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.addons.mod_forum')

/**
 * Add new discussion controller.
 *
 * @module mm.addons.mod_forum
 * @ngdoc controller
 * @name mmaModForumNewDiscussionCtrl
 */
.controller('mmaModForumNewDiscussionCtrl', function($scope, $stateParams, $mmGroups, $q, $mmaModForum, $mmEvents, $ionicPlatform,
            $mmUtil, $ionicHistory, $translate, mmaModForumNewDiscussionEvent, $mmaModForumOffline, $mmSite, mmaModForumComponent,
            mmaModForumAutomSyncedEvent, $mmSyncBlock, $mmaModForumSync, $mmText, $mmaModForumHelper) {

    var courseId = $stateParams.cid,
        forumId = $stateParams.forumid,
        cmId = $stateParams.cmid,
        timecreated = $stateParams.timecreated,
        syncObserver,
        syncId,
        originalData;

    // Block leaving the view, we want to show a confirm to the user if there's unsaved data.
    $mmUtil.blockLeaveView($scope, leaveView);

    $scope.newDiscussion = {
        subject: '',
        text: '',
        subscribe: true,
        files: []
    };

    $scope.hasOffline = false;
    $scope.component = mmaModForumComponent;
    $scope.canAddAttachments = $mmaModForum.canAddAttachments();

    // Fetch if forum uses groups and the groups it uses.
    function fetchDiscussionData(refresh) {
        return $mmGroups.getActivityGroupMode(cmId).then(function(mode) {
            var promises = [];

            if (mode === $mmGroups.SEPARATEGROUPS || mode === $mmGroups.VISIBLEGROUPS) {
                promises.push($mmGroups.getActivityAllowedGroups(cmId).then(function(forumgroups) {
                    var promise;
                    if (mode === $mmGroups.VISIBLEGROUPS) {
                        // We need to check which of the returned groups the user can post to.
                        promise = validateVisibleGroups(forumgroups, refresh);
                    } else {
                        // WS already filters groups, no need to do it ourselves.
                        promise = $q.when(forumgroups);
                    }

                    return promise.then(function(forumgroups) {
                        if (forumgroups.length > 0) {
                            $scope.groups = forumgroups;
                            // Do not override groupid.
                            $scope.newDiscussion.groupid = $scope.newDiscussion.groupid ?
                                $scope.newDiscussion.groupid : forumgroups[0].id;
                            $scope.showGroups = true;
                        } else {
                            var message = mode === $mmGroups.SEPARATEGROUPS ?
                                                'mma.mod_forum.cannotadddiscussionall' : 'mma.mod_forum.cannotadddiscussion';
                            return $q.reject($translate.instant(message));
                        }
                    });
                }));
            } else {
                $scope.showGroups = false;
            }

            // Get forum.
            promises.push($mmaModForum.getForum(courseId, cmId).then(function(forum) {
                $scope.forum = forum;
            }));

            // If editing a discussion, get offline data.
            if (timecreated && !refresh) {
                syncId = $mmaModForumSync.getForumSyncId(forumId);
                promises.push($mmaModForumSync.waitForSync(syncId).then(function() {
                    // Do not block if the scope is already destroyed.
                    if (!$scope.$$destroyed) {
                        $mmSyncBlock.blockOperation(mmaModForumComponent, syncId);
                    }
                    return $mmaModForumOffline.getNewDiscussion(forumId, timecreated).then(function(discussion) {
                        $scope.hasOffline = true;
                        $scope.newDiscussion.groupid = discussion.groupid ? discussion.groupid : $scope.newDiscussion.groupid;
                        $scope.newDiscussion.subject = discussion.subject;
                        $scope.newDiscussion.text = discussion.message;
                        $scope.newDiscussion.subscribe = discussion.subscribe;

                        // Treat offline attachments if any.
                        if (discussion.attachments && discussion.attachments.offline) {
                            return $mmaModForumHelper.getNewDiscussionStoredFiles(forumId, timecreated).then(function(files) {
                                $scope.newDiscussion.files = files;
                            });
                        }
                    });
                }));
            }

            return $q.all(promises);
        }).then(function() {
            if (!originalData) {
                // Initialize original data.
                originalData = angular.copy($scope.newDiscussion);
            }
            $scope.showForm = true;
        }).catch(function(message) {
            $mmUtil.showErrorModalDefault(message, 'mma.mod_forum.errorgetgroups', true);
            $scope.showForm = false;
            return $q.reject();
        });
    }

    // Validate which of the groups returned by getActivityAllowedGroups in visible groups should be shown to post to.
    function validateVisibleGroups(forumgroups, refresh) {
        if ($mmaModForum.isCanAddDiscussionAvailable()) {
            // Use the canAddDiscussion function to filter the groups.
            // We first check if the user can post to all the groups.
            return $mmaModForum.canAddDiscussionToAll(forumId).catch(function() {
                // The call failed, let's assume he can't.
                return false;
            }).then(function(canAdd) {
                if (canAdd) {
                    // The user can post to all groups, return them all.
                    return forumgroups;
                } else {
                    // The user can't post to all groups, let's check which groups he can post to.
                    var promises = [],
                        filtered = [];

                    angular.forEach(forumgroups, function(group) {
                        promises.push($mmaModForum.canAddDiscussion(forumId, group.id).catch(function() {
                            // The call failed, let's return true so the group is shown. If the user can't post to
                            // it an error will be shown when he tries to add the discussion.
                            return true;
                        }).then(function(canAdd) {
                            if (canAdd) {
                                filtered.push(group);
                            }
                        }));
                    });

                    return $q.all(promises).then(function() {
                        return filtered;
                    });
                }
            });
        } else {
            // We can't check it using WS. We'll get the groups the user belongs to and use them to
            // filter the groups to post.
            return $mmGroups.getUserGroupsInCourse(courseId, refresh).then(function(usergroups) {
                if (usergroups.length === 0) {
                    // User doesn't belong to any group, probably a teacher. Let's return all groups,
                    // if the user can't post to some of them it will be filtered by add discussion WS.
                    return forumgroups;
                }
                return filterGroups(forumgroups, usergroups);
            });
        }
    }

    // Filter forumgroups, returning only those that are inside usergroups.
    function filterGroups(forumgroups, usergroups) {
        var filtered = [],
            usergroupsids = usergroups.map(function(g) {
                return g.id;
            });

        angular.forEach(forumgroups, function(fg) {
            if (usergroupsids.indexOf(fg.id) > -1) {
                filtered.push(fg);
            }
        });

        return filtered;
    }

    fetchDiscussionData().finally(function() {
        $scope.groupsLoaded = true;
    });

    // Pull to refresh.
    $scope.refreshGroups = function() {
        var p1 = $mmGroups.invalidateActivityGroupMode(cmId),
            p2 = $mmGroups.invalidateActivityAllowedGroups(cmId),
            p3 = $mmaModForum.invalidateCanAddDiscussion(forumId);

        $q.all([p1, p2, p3]).finally(function() {
            fetchDiscussionData(true).finally(function() {
                $scope.$broadcast('scroll.refreshComplete');
            });
        });
    };

    // Convenience function to update or return to discussions depending on device.
    function returnToDiscussions(discussionId) {
        var data = {
            forumid: forumId,
            cmid: cmId
        };

        if (discussionId) {
            data.discussionid = discussionId;
        }
        $mmEvents.trigger(mmaModForumNewDiscussionEvent, data);

        // Delete the local files from the tmp folder.
        $mmaModForumHelper.clearTmpFiles($scope.newDiscussion.files);

        if ($ionicPlatform.isTablet()) {
            // Empty form.
            $scope.hasOffline = false;
            $scope.newDiscussion.subject = '';
            $scope.newDiscussion.text = '';
            $scope.newDiscussion.files = [];
            originalData = angular.copy($scope.newDiscussion);
        } else {
            // Go back to discussions list.
            $ionicHistory.goBack();
        }
    }

    // Ask to confirm if there are changes.
    function leaveView() {
        var promise;

        if (!$mmaModForumHelper.hasPostDataChanged($scope.newDiscussion, originalData)) {
            promise = $q.when();
        } else {
            // Show confirmation if some data has been modified.
            promise = $mmUtil.showConfirm($translate('mm.core.confirmcanceledit'));
        }

        return promise.then(function() {
            // Delete the local files from the tmp folder.
            $mmaModForumHelper.clearTmpFiles($scope.newDiscussion.files);
        });
    }

    // Add a new discussion.
    $scope.add = function() {
        var modal,
            forumName = $scope.forum.name,
            subject = $scope.newDiscussion.subject,
            message = $scope.newDiscussion.text,
            subscribe = $scope.newDiscussion.subscribe,
            groupId = $scope.newDiscussion.groupid,
            attachments = $scope.newDiscussion.files,
            discTimecreated = timecreated || Date.now(),
            saveOffline = false;

        if (!subject) {
            $mmUtil.showErrorModal('mma.mod_forum.erroremptysubject', true);
            return;
        }
        if (!message) {
            $mmUtil.showErrorModal('mma.mod_forum.erroremptymessage', true);
            return;
        }

        modal = $mmUtil.showModalLoading('mm.core.sending', true);

        // Check if rich text editor is enabled or not.
        $mmUtil.isRichTextEditorEnabled().then(function(enabled) {
            if (!enabled) {
                // Rich text editor not enabled, add some HTML to the message if needed.
                message = $mmText.formatHtmlLines(message);
            }

            // Upload attachments first if any.
            if (attachments.length) {
                return $mmaModForumHelper.uploadOrStoreNewDiscussionFiles(forumId, discTimecreated, attachments, false)
                        .catch(function() {
                    // Cannot upload them in online, save them in offline.
                    saveOffline = true;
                    return $mmaModForumHelper.uploadOrStoreNewDiscussionFiles(forumId, discTimecreated, attachments, true);
                });
            }
        }).then(function(attach) {
            if (saveOffline) {
                // Save discussion in offline.
                return $mmaModForumOffline.addNewDiscussion(forumId, forumName, courseId, subject,
                        message, subscribe, groupId, attach, discTimecreated).then(function() {
                    // Don't return anything.
                });
            } else {
                // Try to send it to server.
                // Don't allow offline if there are attachments since they were uploaded fine.
                return $mmaModForum.addNewDiscussion(forumId, forumName, courseId, subject, message, subscribe,
                        groupId, attach, undefined, discTimecreated, !attachments.length);
            }
        }).then(function(discussionId) {
            if (discussionId) {
                // Data sent to server, delete stored files (if any).
                $mmaModForumHelper.deleteNewDiscussionStoredFiles(forumId, discTimecreated);
            }

            returnToDiscussions(discussionId);
        }).catch(function(message) {
            $mmUtil.showErrorModalDefault(message, 'mma.mod_forum.cannotcreatediscussion', true);
        }).finally(function() {
            modal.dismiss();
        });
    };

    if (timecreated) {
        // Refresh data if this forum is synchronized automatically. Only if we're editing one.
        syncObserver = $mmEvents.on(mmaModForumAutomSyncedEvent, function(data) {
            if (data && data.siteid == $mmSite.getId() && data.forumid == forumId && data.userid == $mmSite.getUserId()) {
                $mmUtil.showModal('mm.core.notice', 'mm.core.contenteditingsynced');
                returnToDiscussions();
            }
        });
    }

    // Discard an offline saved discussion.
    $scope.discard = function() {
        return $mmUtil.showConfirm($translate('mm.core.areyousure')).then(function() {
            var promises = [];

            promises.push($mmaModForumOffline.deleteNewDiscussion(forumId, timecreated));
            promises.push($mmaModForumHelper.deleteNewDiscussionStoredFiles(forumId, timecreated).catch(function() {
                // Ignore errors, maybe there are no files.
            }));

            return $q.all(promises).then(function() {
                returnToDiscussions();
            });
        });
    };

    // Text changed when rendered.
    $scope.firstRender = function() {
        if (originalData) {
            originalData.text = $scope.newDiscussion.text;
        }
    };

    $scope.$on('$destroy', function(){
        syncObserver && syncObserver.off && syncObserver.off();
        if (syncId) {
            $mmSyncBlock.unblockOperation(mmaModForumComponent, syncId);
        }
    });
});
<commit_msg>MOBILE-1986 forum: Show All Participants option if needed<commit_after>// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

angular.module('mm.addons.mod_forum')

/**
 * Add new discussion controller.
 *
 * @module mm.addons.mod_forum
 * @ngdoc controller
 * @name mmaModForumNewDiscussionCtrl
 */
.controller('mmaModForumNewDiscussionCtrl', function($scope, $stateParams, $mmGroups, $q, $mmaModForum, $mmEvents, $ionicPlatform,
            $mmUtil, $ionicHistory, $translate, mmaModForumNewDiscussionEvent, $mmaModForumOffline, $mmSite, mmaModForumComponent,
            mmaModForumAutomSyncedEvent, $mmSyncBlock, $mmaModForumSync, $mmText, $mmaModForumHelper) {

    var courseId = $stateParams.cid,
        forumId = $stateParams.forumid,
        cmId = $stateParams.cmid,
        timecreated = $stateParams.timecreated,
        syncObserver,
        syncId,
        originalData;

    // Block leaving the view, we want to show a confirm to the user if there's unsaved data.
    $mmUtil.blockLeaveView($scope, leaveView);

    $scope.newDiscussion = {
        subject: '',
        text: '',
        subscribe: true,
        files: []
    };

    $scope.hasOffline = false;
    $scope.component = mmaModForumComponent;
    $scope.canAddAttachments = $mmaModForum.canAddAttachments();

    // Fetch if forum uses groups and the groups it uses.
    function fetchDiscussionData(refresh) {
        return $mmGroups.getActivityGroupMode(cmId).then(function(mode) {
            var promises = [];

            if (mode === $mmGroups.SEPARATEGROUPS || mode === $mmGroups.VISIBLEGROUPS) {
                promises.push($mmGroups.getActivityAllowedGroups(cmId).then(function(forumgroups) {
                    var promise;
                    if (mode === $mmGroups.VISIBLEGROUPS) {
                        // We need to check which of the returned groups the user can post to.
                        promise = validateVisibleGroups(forumgroups, refresh);
                    } else {
                        // WS already filters groups, no need to do it ourselves. Add "All participants" if needed.
                        promise = addAllParticipantsOption(forumgroups, true);
                    }

                    return promise.then(function(forumgroups) {
                        if (forumgroups.length > 0) {
                            $scope.groups = forumgroups;
                            // Do not override groupid.
                            $scope.newDiscussion.groupid = $scope.newDiscussion.groupid ?
                                $scope.newDiscussion.groupid : forumgroups[0].id;
                            $scope.showGroups = true;
                        } else {
                            var message = mode === $mmGroups.SEPARATEGROUPS ?
                                                'mma.mod_forum.cannotadddiscussionall' : 'mma.mod_forum.cannotadddiscussion';
                            return $q.reject($translate.instant(message));
                        }
                    });
                }));
            } else {
                $scope.showGroups = false;
            }

            // Get forum.
            promises.push($mmaModForum.getForum(courseId, cmId).then(function(forum) {
                $scope.forum = forum;
            }));

            // If editing a discussion, get offline data.
            if (timecreated && !refresh) {
                syncId = $mmaModForumSync.getForumSyncId(forumId);
                promises.push($mmaModForumSync.waitForSync(syncId).then(function() {
                    // Do not block if the scope is already destroyed.
                    if (!$scope.$$destroyed) {
                        $mmSyncBlock.blockOperation(mmaModForumComponent, syncId);
                    }
                    return $mmaModForumOffline.getNewDiscussion(forumId, timecreated).then(function(discussion) {
                        $scope.hasOffline = true;
                        $scope.newDiscussion.groupid = discussion.groupid ? discussion.groupid : $scope.newDiscussion.groupid;
                        $scope.newDiscussion.subject = discussion.subject;
                        $scope.newDiscussion.text = discussion.message;
                        $scope.newDiscussion.subscribe = discussion.subscribe;

                        // Treat offline attachments if any.
                        if (discussion.attachments && discussion.attachments.offline) {
                            return $mmaModForumHelper.getNewDiscussionStoredFiles(forumId, timecreated).then(function(files) {
                                $scope.newDiscussion.files = files;
                            });
                        }
                    });
                }));
            }

            return $q.all(promises);
        }).then(function() {
            if (!originalData) {
                // Initialize original data.
                originalData = angular.copy($scope.newDiscussion);
            }
            $scope.showForm = true;
        }).catch(function(message) {
            $mmUtil.showErrorModalDefault(message, 'mma.mod_forum.errorgetgroups', true);
            $scope.showForm = false;
            return $q.reject();
        });
    }

    // Validate which of the groups returned by getActivityAllowedGroups in visible groups should be shown to post to.
    function validateVisibleGroups(forumgroups, refresh) {
        if ($mmaModForum.isCanAddDiscussionAvailable()) {
            // Use the canAddDiscussion function to filter the groups.
            // We first check if the user can post to all the groups.
            return $mmaModForum.canAddDiscussionToAll(forumId).catch(function() {
                // The call failed, let's assume he can't.
                return false;
            }).then(function(canAdd) {
                if (canAdd) {
                    // The user can post to all groups, add the "All participants" option and return them all.
                    return addAllParticipantsOption(forumgroups);
                } else {
                    // The user can't post to all groups, let's check which groups he can post to.
                    var promises = [],
                        filtered = [];

                    angular.forEach(forumgroups, function(group) {
                        promises.push($mmaModForum.canAddDiscussion(forumId, group.id).catch(function() {
                            // The call failed, let's return true so the group is shown. If the user can't post to
                            // it an error will be shown when he tries to add the discussion.
                            return true;
                        }).then(function(canAdd) {
                            if (canAdd) {
                                filtered.push(group);
                            }
                        }));
                    });

                    return $q.all(promises).then(function() {
                        return filtered;
                    });
                }
            });
        } else {
            // We can't check it using WS. We'll get the groups the user belongs to and use them to
            // filter the groups to post.
            return $mmGroups.getUserGroupsInCourse(courseId, refresh).then(function(usergroups) {
                if (usergroups.length === 0) {
                    // User doesn't belong to any group, probably a teacher. Let's return all groups,
                    // if the user can't post to some of them it will be filtered by add discussion WS.
                    return forumgroups;
                }
                return filterGroups(forumgroups, usergroups);
            });
        }
    }

    // Filter forumgroups, returning only those that are inside usergroups.
    function filterGroups(forumgroups, usergroups) {
        var filtered = [],
            usergroupsids = usergroups.map(function(g) {
                return g.id;
            });

        angular.forEach(forumgroups, function(fg) {
            if (usergroupsids.indexOf(fg.id) > -1) {
                filtered.push(fg);
            }
        });

        return filtered;
    }

    // Add the "All participants" option to a list of groups if the user can add a discussion to all participants.
    function addAllParticipantsOption(groups, check) {
        var promise;

        if (check) {
            // We need to check if the user can add a discussion to all participants.
            promise = $mmaModForum.canAddDiscussionToAll(forumId).catch(function() {
                // The call failed, let's assume he can't.
                return false;
            });
        } else {
            // No need to check, assume the user can.
            promise = $q.when(true);
        }

        return promise.then(function(canAdd) {
            if (canAdd) {
                groups.unshift({
                    courseid: courseId,
                    id: -1,
                    name: $translate.instant('mm.core.allparticipants')
                });
            }

            return groups;
        });
    }

    fetchDiscussionData().finally(function() {
        $scope.groupsLoaded = true;
    });

    // Pull to refresh.
    $scope.refreshGroups = function() {
        var p1 = $mmGroups.invalidateActivityGroupMode(cmId),
            p2 = $mmGroups.invalidateActivityAllowedGroups(cmId),
            p3 = $mmaModForum.invalidateCanAddDiscussion(forumId);

        $q.all([p1, p2, p3]).finally(function() {
            fetchDiscussionData(true).finally(function() {
                $scope.$broadcast('scroll.refreshComplete');
            });
        });
    };

    // Convenience function to update or return to discussions depending on device.
    function returnToDiscussions(discussionId) {
        var data = {
            forumid: forumId,
            cmid: cmId
        };

        if (discussionId) {
            data.discussionid = discussionId;
        }
        $mmEvents.trigger(mmaModForumNewDiscussionEvent, data);

        // Delete the local files from the tmp folder.
        $mmaModForumHelper.clearTmpFiles($scope.newDiscussion.files);

        if ($ionicPlatform.isTablet()) {
            // Empty form.
            $scope.hasOffline = false;
            $scope.newDiscussion.subject = '';
            $scope.newDiscussion.text = '';
            $scope.newDiscussion.files = [];
            originalData = angular.copy($scope.newDiscussion);
        } else {
            // Go back to discussions list.
            $ionicHistory.goBack();
        }
    }

    // Ask to confirm if there are changes.
    function leaveView() {
        var promise;

        if (!$mmaModForumHelper.hasPostDataChanged($scope.newDiscussion, originalData)) {
            promise = $q.when();
        } else {
            // Show confirmation if some data has been modified.
            promise = $mmUtil.showConfirm($translate('mm.core.confirmcanceledit'));
        }

        return promise.then(function() {
            // Delete the local files from the tmp folder.
            $mmaModForumHelper.clearTmpFiles($scope.newDiscussion.files);
        });
    }

    // Add a new discussion.
    $scope.add = function() {
        var modal,
            forumName = $scope.forum.name,
            subject = $scope.newDiscussion.subject,
            message = $scope.newDiscussion.text,
            subscribe = $scope.newDiscussion.subscribe,
            groupId = $scope.newDiscussion.groupid,
            attachments = $scope.newDiscussion.files,
            discTimecreated = timecreated || Date.now(),
            saveOffline = false;

        if (!subject) {
            $mmUtil.showErrorModal('mma.mod_forum.erroremptysubject', true);
            return;
        }
        if (!message) {
            $mmUtil.showErrorModal('mma.mod_forum.erroremptymessage', true);
            return;
        }

        modal = $mmUtil.showModalLoading('mm.core.sending', true);

        // Check if rich text editor is enabled or not.
        $mmUtil.isRichTextEditorEnabled().then(function(enabled) {
            if (!enabled) {
                // Rich text editor not enabled, add some HTML to the message if needed.
                message = $mmText.formatHtmlLines(message);
            }

            // Upload attachments first if any.
            if (attachments.length) {
                return $mmaModForumHelper.uploadOrStoreNewDiscussionFiles(forumId, discTimecreated, attachments, false)
                        .catch(function() {
                    // Cannot upload them in online, save them in offline.
                    saveOffline = true;
                    return $mmaModForumHelper.uploadOrStoreNewDiscussionFiles(forumId, discTimecreated, attachments, true);
                });
            }
        }).then(function(attach) {
            if (saveOffline) {
                // Save discussion in offline.
                return $mmaModForumOffline.addNewDiscussion(forumId, forumName, courseId, subject,
                        message, subscribe, groupId, attach, discTimecreated).then(function() {
                    // Don't return anything.
                });
            } else {
                // Try to send it to server.
                // Don't allow offline if there are attachments since they were uploaded fine.
                return $mmaModForum.addNewDiscussion(forumId, forumName, courseId, subject, message, subscribe,
                        groupId, attach, undefined, discTimecreated, !attachments.length);
            }
        }).then(function(discussionId) {
            if (discussionId) {
                // Data sent to server, delete stored files (if any).
                $mmaModForumHelper.deleteNewDiscussionStoredFiles(forumId, discTimecreated);
            }

            returnToDiscussions(discussionId);
        }).catch(function(message) {
            $mmUtil.showErrorModalDefault(message, 'mma.mod_forum.cannotcreatediscussion', true);
        }).finally(function() {
            modal.dismiss();
        });
    };

    if (timecreated) {
        // Refresh data if this forum is synchronized automatically. Only if we're editing one.
        syncObserver = $mmEvents.on(mmaModForumAutomSyncedEvent, function(data) {
            if (data && data.siteid == $mmSite.getId() && data.forumid == forumId && data.userid == $mmSite.getUserId()) {
                $mmUtil.showModal('mm.core.notice', 'mm.core.contenteditingsynced');
                returnToDiscussions();
            }
        });
    }

    // Discard an offline saved discussion.
    $scope.discard = function() {
        return $mmUtil.showConfirm($translate('mm.core.areyousure')).then(function() {
            var promises = [];

            promises.push($mmaModForumOffline.deleteNewDiscussion(forumId, timecreated));
            promises.push($mmaModForumHelper.deleteNewDiscussionStoredFiles(forumId, timecreated).catch(function() {
                // Ignore errors, maybe there are no files.
            }));

            return $q.all(promises).then(function() {
                returnToDiscussions();
            });
        });
    };

    // Text changed when rendered.
    $scope.firstRender = function() {
        if (originalData) {
            originalData.text = $scope.newDiscussion.text;
        }
    };

    $scope.$on('$destroy', function(){
        syncObserver && syncObserver.off && syncObserver.off();
        if (syncId) {
            $mmSyncBlock.unblockOperation(mmaModForumComponent, syncId);
        }
    });
});
<|endoftext|>


<commit_before>{
	"Gitter — Where developers come to talk.": "Gitter — Місце, де спілкуються розробники",
	"Integrations": "Інтеграція",
	"Pricing": "Тарифи",
	"Apps": "Додатки",
	"Existing user login": "Вхід для користувачів",
	"login": "вхід",
	"Chat, for GitHub.": "Чат для GitHub",
	"Get unlimited public rooms and one-to-one chats, free forever. Private team plans start from $5 per user per month. No credit card required.": "В нас розміщені тисячі публічних чатів, в яких розробники з усього світу обговорюють майбутнє програмування, створюють чудові продукти і діляться найкращими картинками котиків.",
	"Sign up free with Github": "Зареєструйтеся через GitHub",
	"Sign up with Github": "Зареєструйстеся через GitHub",
	"By signing up you agree to our Terms and Conditions": "Реєструючись, Ви погоджуєтеся з умовами користувацької угоди.",
	"Markdown power": "Markdown",
	"Ever wanted full markdown support in chat? How about syntax highlighting?": "Ви завжди мріяли про повну підтримку markdown в чаті? А як щодо підсвітки синтаксису?",
	"Yip, we got your back.": "Звісно вони є в нас!",
	"Private conversations": "Приватні чати",
	"Public rooms are awesome (and free), but we get that some stuff is private. So we've got plans for that.": "Відкриті кімнати - чудові (і безкоштовні), але деякі речі є конфіденційними. В нас є засоби і для цього.",
	"Developer friendly": "Для розробників",
	"This is a picture of a cloud against a background of clouds. OMG! We integrate with a bunch of stuff in the cloud.": "Це картинка хмарки на фоні хмар. Це перемога! Ми інтегруємося з великою кількістю хмарних продуктів.",
	"Some of our communities": "Деякі з наших спільнот",
	"Create your own": "Створи власне",
	"Explore rooms": "досліджувати",
	"Explore": "досліджувати",
	"Integrated with your workflow": "Інтегрований з вашим робочим процесом",
	"Gitter is built on top of GitHub and is tightly integrated with your organisations, repositories, issues and activity.": "Gitter створений на основі GitHub і добре інтегрується з Вашими репозиторіями, баг-трекерами і активностями.",
	"We provide integrations with Trello, Jenkins, Travis CI, Heroku, Sentry, BitBucket, HuBoard, Logentries, Pagerduty & Sprintly. We also accept custom webhooks, have an open source repository for integrations as well as a flexible API.":"Ми надаємо інтеграцію з Trello, Jenkinks, Travis CI, Heroku, Sentry, BitBucker, HuBoard, Logentries, Pagerduty і Sprintly. Ми також використовуємо користувацькі webhook-и, маємо open source репозиторії для інтеграцій, а також надаємогнучке API.",
	"Loved by our users": "Обожнюваний нашими користувачами",
	"Gitter is nothing without the people using it day in and day out.": "Gitter - ніщо, без користувачів, які використовують його щодня.",
	"We asked a few what Gitter means to them, this is what they had to say. If you love Gitter, Tweet us %s and we'll update these quotes every month.":"Ми запитали деяких користувачів, що для них означає Gitter і ось що вони сказали. Якщо Вам подобається Gitter, пишіть про це в наш Tiwtter %s і ми будемо оновлювати ці повідомлення щомісяця.",
	"Personal pricing": "Для персонального використання",
	"Free": "Безплатний",
	"Forever": "Назавжди",
	"Unlimited public rooms": "Необмежена кількість публічних кімнат",
	"1 private room": "1 приватна кімната",
	"2 weeks’ chat history": "Історія чату за 2 тижні",
	"Get started for free": "Стартуйте безплатно",
	"Individual": "Індивідуальний",
	"monthly": "Місячний",
	"Unlimited private rooms": "Необмежена кількість публічних кімнат",
	"Unlimited chat history": "Необмежена кількість приватних кімнат",
	"Purchase now": "Купіть зараз",
	"Organisation pricing": "Для організація",
	"Bronze": "Бронза",
	"per user/month": "За користувача в місяць",
	"6 months’ chat history": "Історія чату за 6 місяців",
	"Silver": "Срібло",
	"Our free plan includes one private room per organisation you belong to on GitHub": "Наш безплатний план включає одну приватну кімнату для організації, в яку Ви входите на GitHub",
	"Our Individual plan allows you to create private rooms using your personal account as the owner": "Наш індивідуальний план дозволяє Вам створювати власні приватні кімнати, використовуючи Ваш обліковий запис",
	"Our organisation plans allow you to create private rooms using your GitHub organisation as the owner": "Наші план для організацій дозволяють Вам створювати приватні кімнати, власником яких буде ваша організація на GitHub",
	"Prices may be subject to VAT. By signing up you agree to our Terms and Conditions.": "Реєструючись, Ви погоджуєтеся з умовами користувацької угоди.",
	"Get Gitter now!": "Приєднуйется до Gitter зараз!",
	"Gitter is available in all modern browsers as well as apps for desktops and mobile phones.": "Gitter доступний у всіх сучасних браузерах, так само як і для мобільних пристроїв та персональних комп’ютерів",
	"We currently have apps for OSX, iOS, Android, Windows and Linux. We also have a sweet little IRC bridge": "В нас є додатки для OSX, iOS, Android, Windows i Linux. Ми такод маємо милий IRC-міст",
	"Mac": "Mac",
	"Windows": "Windows",
	"Linux 32": "Linux 32",
	"Linux 64": "Linux 64",
	"iPhone": "iPhone",
	"Android": "Android",
	"By developers, for developers": "Від розробників для розробників",
	"Gitter is built in London by a team of developers just like you. Hell even the product guy writes code, although we generally end up rewriting those bits.": "Gitter створений в Лондоні командою таких же розробників, як Ви. Чорт забирай, навіть менеджер писав код, правда ми зазвичай переписували ці частини.",
	"If you're ever around Silicon Roundabout, give us a shout, we love meeting up with other developers.": "Якщо Ви колись будете пробігати біля Silicon Roundabout, гукніть нам, ми ообожнюємо зустрічі з іншими розробниками",
	"Support": "Підтримка",
	"API": "API",
	"Blog": "Блог",
	"Twitter": "Twitter",
	"Translated By": "Dmytro Bezuh"
}
<commit_msg>Corrected few errors<commit_after>{
	"Gitter — Where developers come to talk.": "Gitter — Місце, де спілкуються розробники",
	"Integrations": "Інтеграція",
	"Pricing": "Тарифи",
	"Apps": "Додатки",
	"Existing user login": "Вхід для користувачів",
	"login": "вхід",
	"Chat, for GitHub.": "Чат для GitHub",
	"Get unlimited public rooms and one-to-one chats, free forever. Private team plans start from $5 per user per month. No credit card required.": "В нас розміщені тисячі публічних чатів, в яких розробники з усього світу обговорюють майбутнє програмування, створюють чудові продукти і діляться найкращими картинками котиків.",
	"Sign up free with Github": "Зареєструйтеся через GitHub",
	"Sign up with Github": "Зареєструйстеся через GitHub",
	"By signing up you agree to our Terms and Conditions": "Реєструючись, Ви погоджуєтеся з умовами користувацької угоди.",
	"Markdown power": "Markdown",
	"Ever wanted full markdown support in chat? How about syntax highlighting?": "Ви завжди мріяли про повну підтримку markdown в чаті? А як щодо підсвітки синтаксису?",
	"Yip, we got your back.": "Звісно вони є в нас!",
	"Private conversations": "Приватні чати",
	"Public rooms are awesome (and free), but we get that some stuff is private. So we've got plans for that.": "Відкриті кімнати - чудові (і безкоштовні), але деякі речі є конфіденційними. В нас є засоби і для цього.",
	"Developer friendly": "Для розробників",
	"This is a picture of a cloud against a background of clouds. OMG! We integrate with a bunch of stuff in the cloud.": "Це картинка хмарки на фоні хмар. Це перемога! Ми інтегруємося з великою кількістю хмарних продуктів.",
	"Some of our communities": "Деякі з наших спільнот",
	"Create your own": "Створи власне",
	"Explore rooms": "досліджувати",
	"Explore": "досліджувати",
	"Integrated with your workflow": "Інтегрований з вашим робочим процесом",
	"Gitter is built on top of GitHub and is tightly integrated with your organisations, repositories, issues and activity.": "Gitter створений на основі GitHub і добре інтегрується з Вашими репозиторіями, баг-трекерами і активностями.",
	"We provide integrations with Trello, Jenkins, Travis CI, Heroku, Sentry, BitBucket, HuBoard, Logentries, Pagerduty & Sprintly. We also accept custom webhooks, have an open source repository for integrations as well as a flexible API.":"Ми надаємо інтеграцію з Trello, Jenkins, Travis CI, Heroku, Sentry, BitBucket, HuBoard, Logentries, Pagerduty і Sprintly. Ми також використовуємо користувацькі webhook-и, маємо open source репозиторії для інтеграцій, а також надаємогнучке API.",
	"Loved by our users": "Обожнюваний нашими користувачами",
	"Gitter is nothing without the people using it day in and day out.": "Gitter - ніщо, без користувачів, які використовують його щодня.",
	"We asked a few what Gitter means to them, this is what they had to say. If you love Gitter, Tweet us %s and we'll update these quotes every month.":"Ми запитали деяких користувачів, що для них означає Gitter і ось що вони сказали. Якщо Вам подобається Gitter, пишіть про це в наш Tiwtter %s і ми будемо оновлювати ці повідомлення щомісяця.",
	"Personal pricing": "Для персонального використання",
	"Free": "Безкоштовний",
	"Forever": "Назавжди",
	"Unlimited public rooms": "Необмежена кількість публічних кімнат",
	"1 private room": "1 приватна кімната",
	"2 weeks’ chat history": "Історія чату за 2 тижні",
	"Get started for free": "Почніть безкоштовно",
	"Individual": "Індивідуальний",
	"monthly": "Місячний",
	"Unlimited private rooms": "Необмежена кількість публічних кімнат",
	"Unlimited chat history": "Необмежена кількість приватних кімнат",
	"Purchase now": "Купіть зараз",
	"Organisation pricing": "Для організацій",
	"Bronze": "Бронза",
	"per user/month": "За користувача в місяць",
	"6 months’ chat history": "Історія чату за 6 місяців",
	"Silver": "Срібло",
	"Our free plan includes one private room per organisation you belong to on GitHub": "Наш безкоштовний план включає одну приватну кімнату для організації, в яку Ви входите на GitHub",
	"Our Individual plan allows you to create private rooms using your personal account as the owner": "Наш індивідуальний план дозволяє Вам створювати власні приватні кімнати, використовуючи Ваш обліковий запис",
	"Our organisation plans allow you to create private rooms using your GitHub organisation as the owner": "Наші плани для організацій дозволяють Вам створювати приватні кімнати, власником яких буде Ваша організація на GitHub",
	"Prices may be subject to VAT. By signing up you agree to our Terms and Conditions.": "Реєструючись, Ви погоджуєтеся з умовами користувацької угоди.",
	"Get Gitter now!": "Приєднуйтеся до Gitter зараз!",
	"Gitter is available in all modern browsers as well as apps for desktops and mobile phones.": "Gitter доступний у всіх сучасних браузерах, так само як і для мобільних пристроїв та персональних комп’ютерів",
	"We currently have apps for OSX, iOS, Android, Windows and Linux. We also have a sweet little IRC bridge": "В нас є додатки для OSX, iOS, Android, Windows i Linux. Ми також маємо милий IRC-міст",
	"Mac": "Mac",
	"Windows": "Windows",
	"Linux 32": "Linux 32",
	"Linux 64": "Linux 64",
	"iPhone": "iPhone",
	"Android": "Android",
	"By developers, for developers": "Від розробників для розробників",
	"Gitter is built in London by a team of developers just like you. Hell even the product guy writes code, although we generally end up rewriting those bits.": "Gitter створений в Лондоні командою таких же розробників, як Ви. Чорт забирай, навіть менеджер писав код, правда ми зазвичай переписували ці частини.",
	"If you're ever around Silicon Roundabout, give us a shout, we love meeting up with other developers.": "Якщо Ви колись будете пробігати біля Silicon Roundabout, гукніть нам, ми обожнюємо зустрічі з іншими розробниками",
	"Support": "Підтримка",
	"API": "API",
	"Blog": "Блог",
	"Twitter": "Twitter",
	"Translated By": "Dmytro Bezuh"
}
<|endoftext|>