<commit_before>/* See license.txt for terms of usage */

define(function(require, exports, module) {

// Dependencies
const React = require("react");
const { Reps } = require("reps/reps");
const { LayerList } = require("layer-list");
const { LayerForm } = require("layer-form");
const { LayerStore } = require("layer-store");

// Shortcuts
const { TABLE, TR, TD, DIV, IMG, SPAN } = Reps.DOM;

/**
 * @react This template implements basic layout for the popup panel.
 * There are two components displayed:
 * 1. Layer list: list of all registered layers {@link Layerlist}.
 * 2. Layer form: a form displaying properties of the selected layer
 * {@link LayerForm}.
 *
 * If there are no layers, the popup panel displays default content
 * with instructions and one button: 'Add Layer' {@link DefaultContent}.
 */
var PopupPanel = React.createClass({
  getInitialState: function() {
    return {
      layers: [],
      selection: null,
    };
  },

  render: function() {
    var layers = this.state.layers;
    var selectedLayer = this.getLayer(this.state.selection);

    // If there are no layers, display default content with instructions
    // about how to create one.
    if (!layers || !layers.length) {
      return DefaultContent({
        version: this.state.version,
        addLayer: this.addLayer
      });
    }

    // Render list of layers and a form component.
    return (
      TABLE({className: "popupPanelTable"},
        TR({},
          TD({className: "layerListCell"},
            DIV({className: "layerList"},
              LayerList({
                layers: this.state.layers,
                selection: this.state.selection,
                selectLayer: this.selectLayer,
                addLayer: this.addLayer,
                removeLayer: this.removeLayer
              })
            )
          ),
          TD({className: "layerFormCell"},
            DIV({className: "layerForm"},
              LayerForm({
                layer: selectedLayer
              })
            )
          )
        )
      )
    )
  },

  getLayer: function(id) {
    var layers = this.state.layers;
    for (var i=0; i<layers.length; i++) {
      if (layers[i].id == id) {
        return layers[i];
      }
    }
  },

  // Commands

  selectLayer: function(layer) {
    this.state.selection = layer.id;
    this.setState(this.state);
  },

  addLayer: function() {
    LayerStore.add();
  },

  removeLayer: function(layer) {
    LayerStore.remove(layer.id);
  },
});

/**
 * @react This template renders default content in case there are
 * no layers registered. It displays basic instructions about how
 * to start working with Pixel Perfect.
 */
var DefaultContent = React.createFactory(React.createClass({
  render: function() {
    return (
      TABLE({className: "defaultContentTable"},
        TR({},
          TD({width: "10px;"},
            IMG({className: "defaultContentImage",
              src: "chrome://pixelperfect/skin/logo_32x32.png"})
          ),
          TD({className: "defaultContentHeader"},
            SPAN({}, Locale.$STR("pixelPerfect.title")),
            SPAN({}, " "),
            SPAN({}, this.props.version)
          )
        ),
        TR({},
          TD({colSpan: 2},
            DIV({className: "defaultContentDesc"},
              Locale.$STR("pixelPerfect.help.desc")
            )
          )
        ),
        TR({},
          TD({colSpan: 2},
            DIV({className: "layerImage add img-thumbnail"},
              DIV({onClick: this.props.addLayer},
                Locale.$STR("pixelPerfect.label.addLayer")
              )
            )
          )
        ),
        TR({},
          TD({colSpan: 2},
            SPAN({className: "defaultContentMore", onClick: this.onHomePage},
              Locale.$STR("pixelPerfect.help.more")
            )
          )
        )
      )
    )
  },

  onHomePage: function() {
    postChromeMessage("open-homepage");
  }
}));

// Exports from this module
exports.PopupPanel = React.createFactory(PopupPanel);
});
<commit_msg>Fix typo<commit_after>/* See license.txt for terms of usage */

define(function(require, exports, module) {

// Dependencies
const React = require("react");
const { Reps } = require("reps/reps");
const { LayerList } = require("layer-list");
const { LayerForm } = require("layer-form");
const { LayerStore } = require("layer-store");

// Shortcuts
const { TABLE, TR, TD, DIV, IMG, SPAN } = Reps.DOM;

/**
 * @react This template implements basic layout for the popup panel.
 * There are two components displayed:
 * 1. Layer list: list of all registered layers {@link LayerList}.
 * 2. Layer form: a form displaying properties of the selected layer
 * {@link LayerForm}.
 *
 * If there are no layers, the popup panel displays default content
 * with instructions and one button: 'Add Layer' {@link DefaultContent}.
 */
var PopupPanel = React.createClass({
  getInitialState: function() {
    return {
      layers: [],
      selection: null,
    };
  },

  render: function() {
    var layers = this.state.layers;
    var selectedLayer = this.getLayer(this.state.selection);

    // If there are no layers, display default content with instructions
    // about how to create one.
    if (!layers || !layers.length) {
      return DefaultContent({
        version: this.state.version,
        addLayer: this.addLayer
      });
    }

    // Render list of layers and a form component.
    return (
      TABLE({className: "popupPanelTable"},
        TR({},
          TD({className: "layerListCell"},
            DIV({className: "layerList"},
              LayerList({
                layers: this.state.layers,
                selection: this.state.selection,
                selectLayer: this.selectLayer,
                addLayer: this.addLayer,
                removeLayer: this.removeLayer
              })
            )
          ),
          TD({className: "layerFormCell"},
            DIV({className: "layerForm"},
              LayerForm({
                layer: selectedLayer
              })
            )
          )
        )
      )
    )
  },

  getLayer: function(id) {
    var layers = this.state.layers;
    for (var i=0; i<layers.length; i++) {
      if (layers[i].id == id) {
        return layers[i];
      }
    }
  },

  // Commands

  selectLayer: function(layer) {
    this.state.selection = layer.id;
    this.setState(this.state);
  },

  addLayer: function() {
    LayerStore.add();
  },

  removeLayer: function(layer) {
    LayerStore.remove(layer.id);
  },
});

/**
 * @react This template renders default content in case there are
 * no layers registered. It displays basic instructions about how
 * to start working with Pixel Perfect.
 */
var DefaultContent = React.createFactory(React.createClass({
  render: function() {
    return (
      TABLE({className: "defaultContentTable"},
        TR({},
          TD({width: "10px;"},
            IMG({className: "defaultContentImage",
              src: "chrome://pixelperfect/skin/logo_32x32.png"})
          ),
          TD({className: "defaultContentHeader"},
            SPAN({}, Locale.$STR("pixelPerfect.title")),
            SPAN({}, " "),
            SPAN({}, this.props.version)
          )
        ),
        TR({},
          TD({colSpan: 2},
            DIV({className: "defaultContentDesc"},
              Locale.$STR("pixelPerfect.help.desc")
            )
          )
        ),
        TR({},
          TD({colSpan: 2},
            DIV({className: "layerImage add img-thumbnail"},
              DIV({onClick: this.props.addLayer},
                Locale.$STR("pixelPerfect.label.addLayer")
              )
            )
          )
        ),
        TR({},
          TD({colSpan: 2},
            SPAN({className: "defaultContentMore", onClick: this.onHomePage},
              Locale.$STR("pixelPerfect.help.more")
            )
          )
        )
      )
    )
  },

  onHomePage: function() {
    postChromeMessage("open-homepage");
  }
}));

// Exports from this module
exports.PopupPanel = React.createFactory(PopupPanel);
});
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

// eslint-disable-next-line max-classes-per-file
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { FlatList, StyleSheet, View } from 'react-native';
import { complement } from 'set-manipulator';
import { SearchBar } from './SearchBar';
import { APP_FONT_FAMILY, WHITE } from '../globalStyles';
import { generalStrings } from '../localization';
import { withOnePress } from './withOnePress';
import { ResultRow } from '.';

export const AutocompleteSelector = props => {
  const {
    sortKeyString,
    queryString,
    queryStringSecondary,
    primaryFilterProperty,
    secondaryFilterProperty,
    options,
    onSelect,
    placeholderText,
    renderLeftText,
    renderRightText,
  } = props;

  const [queryText, setQueryText] = useState('');

  const onChangeText = () => setQueryText(queryText);

  /**
   * Filters a realm results object. Creates two realm results A, B
   * by two query strings. And concat A to B - A.
   */
  const filterResultData = () => {
    let data = options
      .filtered(queryString, queryText)
      .sorted(sortKeyString)
      .slice();

    if (queryStringSecondary) {
      data = data.concat(
        complement(options.filtered(queryStringSecondary, queryText).sorted(sortKeyString), data)
      );
    }

    return data;
  };

  /**
   * Filters an array by two filter properties, and user input query text.
   * Ignores case. Querying a realm result with filtered is more performant,
   * so have two cases for each.
   */
  const filterArrayData = () => {
    const regexFilter = RegExp(queryText, 'i');

    return options.filter(
      optionItem =>
        regexFilter.test(optionItem[primaryFilterProperty]) ||
        regexFilter.test(optionItem[secondaryFilterProperty])
    );
  };

  /**
   * Delegator of filtering process. Check if the object is a realm
   * object (has the filtered member) or if it is an array. Otherwise,
   * return an empty list to display.
   */
  const getData = () => {
    if (options && options.filtered && queryString) return filterResultData(options);
    if (Array.isArray(options) && primaryFilterProperty) return filterArrayData(options);
    return [];
  };

  const renderItem = useCallback(
    item => (
      <ResultRowWithOnePress
        data={item}
        onPress={onSelect}
        renderLeftText={renderLeftText}
        renderRightText={renderRightText}
        showCheckIcon={false}
      />
    ),
    [renderLeftText, renderRightText]
  );

  const keyExtractor = item => item.id || item.name;

  const data = getData();

  const ResultRowWithOnePress = withOnePress(ResultRow);

  return (
    <View style={localStyles.container}>
      <SearchBar
        autoCapitalize="none"
        autoCorrect={false}
        autoFocus
        color={WHITE}
        onChangeText={onChangeText}
        placeholder={placeholderText}
        placeholderTextColor="white"
        style={[localStyles.text, localStyles.searchBar]}
      />
      {data.length > 0 && (
        <FlatList
          data={data}
          keyExtractor={keyExtractor}
          renderItem={renderItem}
          keyboardShouldPersistTaps="always"
          style={localStyles.resultList}
        />
      )}
    </View>
  );
};

export default AutocompleteSelector;

/* eslint-disable react/forbid-prop-types, react/require-default-props */
AutocompleteSelector.propTypes = {
  options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  queryString: PropTypes.string.isRequired,
  queryStringSecondary: PropTypes.string,
  sortKeyString: PropTypes.string.isRequired,
  placeholderText: PropTypes.string,
  onSelect: PropTypes.func.isRequired,
  renderLeftText: PropTypes.func,
  renderRightText: PropTypes.func,
  primaryFilterProperty: PropTypes.string,
  secondaryFilterProperty: PropTypes.string,
};

AutocompleteSelector.defaultProps = {
  placeholderText: generalStrings.start_typing_to_search,
};

const localStyles = StyleSheet.create({
  container: {
    flex: 1,
  },
  resultList: {
    flex: 1,
    marginHorizontal: 10,
    backgroundColor: 'white',
    borderColor: '#b9b9b9',
    borderRadius: 1,
    borderWidth: 1,
  },
  searchBar: {
    flex: 1,
    color: 'white',
  },
  text: {
    fontSize: 20,
    fontFamily: APP_FONT_FAMILY,
  },
  itemText: {
    marginHorizontal: 2,
    marginVertical: 8,
  },
  resultRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
});
<commit_msg>Destructure prop parameters<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

// eslint-disable-next-line max-classes-per-file
import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { FlatList, StyleSheet, View } from 'react-native';
import { complement } from 'set-manipulator';
import { SearchBar } from './SearchBar';
import { APP_FONT_FAMILY, WHITE } from '../globalStyles';
import { generalStrings } from '../localization';
import { withOnePress } from './withOnePress';
import { ResultRow } from '.';

export const AutocompleteSelector = ({
  sortKeyString,
  queryString,
  queryStringSecondary,
  primaryFilterProperty,
  secondaryFilterProperty,
  options,
  onSelect,
  placeholderText,
  renderLeftText,
  renderRightText,
}) => {
  const [queryText, setQueryText] = useState('');

  const onChangeText = () => setQueryText(queryText);

  /**
   * Filters a realm results object. Creates two realm results A, B
   * by two query strings. And concat A to B - A.
   */
  const filterResultData = () => {
    let data = options
      .filtered(queryString, queryText)
      .sorted(sortKeyString)
      .slice();

    if (queryStringSecondary) {
      data = data.concat(
        complement(options.filtered(queryStringSecondary, queryText).sorted(sortKeyString), data)
      );
    }

    return data;
  };

  /**
   * Filters an array by two filter properties, and user input query text.
   * Ignores case. Querying a realm result with filtered is more performant,
   * so have two cases for each.
   */
  const filterArrayData = () => {
    const regexFilter = RegExp(queryText, 'i');

    return options.filter(
      optionItem =>
        regexFilter.test(optionItem[primaryFilterProperty]) ||
        regexFilter.test(optionItem[secondaryFilterProperty])
    );
  };

  /**
   * Delegator of filtering process. Check if the object is a realm
   * object (has the filtered member) or if it is an array. Otherwise,
   * return an empty list to display.
   */
  const getData = () => {
    if (options && options.filtered && queryString) return filterResultData(options);
    if (Array.isArray(options) && primaryFilterProperty) return filterArrayData(options);
    return [];
  };

  const renderItem = useCallback(
    item => (
      <ResultRowWithOnePress
        data={item}
        onPress={onSelect}
        renderLeftText={renderLeftText}
        renderRightText={renderRightText}
        showCheckIcon={false}
      />
    ),
    [renderLeftText, renderRightText]
  );

  const keyExtractor = item => item.id || item.name;

  const data = getData();

  const ResultRowWithOnePress = withOnePress(ResultRow);

  return (
    <View style={localStyles.container}>
      <SearchBar
        autoCapitalize="none"
        autoCorrect={false}
        autoFocus
        color={WHITE}
        onChangeText={onChangeText}
        placeholder={placeholderText}
        placeholderTextColor="white"
        style={[localStyles.text, localStyles.searchBar]}
      />
      {data.length > 0 && (
        <FlatList
          data={data}
          keyExtractor={keyExtractor}
          renderItem={renderItem}
          keyboardShouldPersistTaps="always"
          style={localStyles.resultList}
        />
      )}
    </View>
  );
};

export default AutocompleteSelector;

/* eslint-disable react/forbid-prop-types, react/require-default-props */
AutocompleteSelector.propTypes = {
  options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  queryString: PropTypes.string.isRequired,
  queryStringSecondary: PropTypes.string,
  sortKeyString: PropTypes.string.isRequired,
  placeholderText: PropTypes.string,
  onSelect: PropTypes.func.isRequired,
  renderLeftText: PropTypes.func,
  renderRightText: PropTypes.func,
  primaryFilterProperty: PropTypes.string,
  secondaryFilterProperty: PropTypes.string,
};

AutocompleteSelector.defaultProps = {
  placeholderText: generalStrings.start_typing_to_search,
};

const localStyles = StyleSheet.create({
  container: {
    flex: 1,
  },
  resultList: {
    flex: 1,
    marginHorizontal: 10,
    backgroundColor: 'white',
    borderColor: '#b9b9b9',
    borderRadius: 1,
    borderWidth: 1,
  },
  searchBar: {
    flex: 1,
    color: 'white',
  },
  text: {
    fontSize: 20,
    fontFamily: APP_FONT_FAMILY,
  },
  itemText: {
    marginHorizontal: 2,
    marginVertical: 8,
  },
  resultRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
});
<|endoftext|>


<commit_before>"use strict";

const self = require("sdk/self");
const FRAME_SCRIPT_URL = self.data.url("recorder-frame-script.js");

function PageRecorderPanel(win, toolbox) {
  this.win = win;
  this.doc = this.win.document;
  this.toolbox = toolbox;

  this.onRecordings = this.onRecordings.bind(this);
  this.toggle = this.toggle.bind(this);
  this.search = this.search.bind(this);

  this.mm = toolbox.target.tab.linkedBrowser.messageManager;
  this.loadFrameScript();

  this.initUI();
}

exports.PageRecorderPanel = PageRecorderPanel;

PageRecorderPanel.prototype = {
  destroy() {
    this.win = this.doc = this.toolbox = this.mm = null;
  },

  loadFrameScript() {
    this.mm.loadFrameScript(FRAME_SCRIPT_URL, false);
    this.mm.addMessageListener("PageRecorder:OnUpdate", this.onRecordings);

    // Make sure this is only called once on this instance
    this.loadFrameScript = () => {
      console.warn("Frame script " + FRAME_SCRIPT_URL + " has already been loaded");
    };
  },

  initUI() {
    this.recordsEl = this.doc.querySelector(".records");
    this.screenshotEl = this.doc.querySelector(".screenshots img");
    this.toggleEl = this.doc.querySelector("#toggle");
    this.searchBox = this.doc.querySelector("#search-input");

    this.toggleEl.addEventListener("click", this.toggle);
    this.searchBox.addEventListener("input", this.search);
  },

  toggle() {
    if (!this.isStarted) {
      this.start();
    } else {
      this.stop();
    }
  },

  start() {
    if (this.isStarted) {
      return;
    }
    this.isStarted = true;

    this.toggleEl.setAttribute("checked", "true")
    this.recordsEl.innerHTML = "";
    this.searchBox.value = "";
    this.mm.sendAsyncMessage("PageRecorder:Start");
  },

  stop() {
    if (!this.isStarted) {
      return;
    }
    this.isStarted = false;

    this.toggleEl.removeAttribute("checked");
    this.mm.sendAsyncMessage("PageRecorder:Stop");
  },

  search() {
    let query = this.searchBox.value.toLowerCase();
    if(!this.recordsEl.mozMatchesSelector(":empty") || !this.recordsEl.matches(":empty")) {
      [].forEach.call(this.recordsEl.querySelectorAll("li"), function(el) {
        if(query == "" || el.textContent.toLowerCase().indexOf(query) > -1) {
          el.style.removeProperty("display");
        }
        else {
          el.style.display = "none";
        }
      });
    }
  },

  onRecordings({data: records, objects}) {
    if (!this.isStarted) {
      return;
    }

    // data and objects are 2 equally sized arrays, data contains the actual
    // records, and objects the corresponding CPOW targets if any.
    for (let i = 0; i < records.length; i ++) {
      let record = records[i];
      let target = objects[i];

      if (record.type === "screenshot") {
        this.lastScreenshot = record.data;
        continue;
      }

      let li = this.doc.createElement("li");
      li.classList.add("record");
      li.classList.add(record.type);
      let self = this;
      (function(src, node) {
        li.addEventListener("mouseover", () => {
          self.screenshotEl.src = src;
          self.mm.sendAsyncMessage("PageRecorder:HighlightNode", null, {node});
        });
        li.addEventListener("mouseout", () => {
          self.mm.sendAsyncMessage("PageRecorder:UnhighlightNode");
        });
      })(this.lastScreenshot, target);

      if (target) {
        li.appendChild(this.buildTargetOutput(target));
      }

      let formatterData = {
        parentEl: li,
        data: record.data,
        time: record.time
      };
      if (this["buildRecordOutputFor_" + record.type]) {
        this["buildRecordOutputFor_" + record.type](formatterData);
      } else {
        this["buildRecordOutputFor_unknown"](formatterData);
      }

      if (query !== "" || li.textContent.toLowerCase().indexOf(query) == -1) {
        el.style.display = "none";
      }

      this.recordsEl.appendChild(li);
    }

    this.recordsEl.scrollTop = this.recordsEl.scrollHeight;
  },

  buildRecordOutputFor_mutation(formatterData) {
    // Break it down further by mutation type
    let type = formatterData.data.type;
    if (this["buildRecordOutputFor_mutation_" + type]) {
      this["buildRecordOutputFor_mutation_" + type](formatterData);
    } else {
      this["buildRecordOutputFor_unknown"](formatterData);
    }
  },

  buildRecordOutputFor_mutation_attributes({parentEl, data, time}) {
    let {name, oldValue, value} = data.reason;

    let outputEl = this.doc.createElement("span");
    outputEl.innerHTML = "Attribute " + name +
                         " changed from \"" +
                         oldValue + "\" to \"" +
                         value + "\"";
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_mutation_childList({parentEl, data, time}) {
    let {addedNodes, removedNodes} = data.reason;

    let outputEl = this.doc.createElement("span");
    outputEl.innerHTML = "Child nodes changed: added " + addedNodes + " nodes" +
                         " and removed " + removedNodes + " nodes";
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_event({parentEl, data, time}) {
    let outputEl = this.doc.createElement("span");
    outputEl.textContent = "Event " + data.type;
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_unknown({parentEl, data, time}) {
    let outputEl = this.doc.createElement("span");
    outputEl.textContent = "Unknown change";
    parentEl.appendChild(outputEl);
  },

  buildTargetOutput(target) {
    let targetEl = this.doc.createElement("span");
    targetEl.classList.add("target");

    let tagEl = this.doc.createElement("span");
    tagEl.classList.add("target-tag");
    tagEl.textContent = "<" + target.localName;
    targetEl.appendChild(tagEl);

    if (target.id) {
      let idEl = this.doc.createElement("span");
      idEl.classList.add("target-id");
      idEl.textContent = "#" + target.id;
      targetEl.appendChild(idEl);
    }
    if (target.classList.length) {
      let classesEl = this.doc.createElement("span");
      classesEl.classList.add("target-classes");
      classesEl.textContent = "." + [...target.classList].join(".");
      targetEl.appendChild(classesEl);
    }

    let closeTagEl = this.doc.createElement("span");
    closeTagEl.classList.add("target-tag");
    closeTagEl.textContent = ">";
    targetEl.appendChild(closeTagEl);

    return targetEl;
  }
};
<commit_msg>Added live update support<commit_after>"use strict";

const self = require("sdk/self");
const FRAME_SCRIPT_URL = self.data.url("recorder-frame-script.js");

function PageRecorderPanel(win, toolbox) {
  this.win = win;
  this.doc = this.win.document;
  this.toolbox = toolbox;

  this.onRecordings = this.onRecordings.bind(this);
  this.toggle = this.toggle.bind(this);
  this.search = this.search.bind(this);

  this.mm = toolbox.target.tab.linkedBrowser.messageManager;
  this.loadFrameScript();

  this.initUI();
}

exports.PageRecorderPanel = PageRecorderPanel;

PageRecorderPanel.prototype = {
  destroy() {
    this.win = this.doc = this.toolbox = this.mm = null;
  },

  loadFrameScript() {
    this.mm.loadFrameScript(FRAME_SCRIPT_URL, false);
    this.mm.addMessageListener("PageRecorder:OnUpdate", this.onRecordings);

    // Make sure this is only called once on this instance
    this.loadFrameScript = () => {
      console.warn("Frame script " + FRAME_SCRIPT_URL + " has already been loaded");
    };
  },

  initUI() {
    this.recordsEl = this.doc.querySelector(".records");
    this.screenshotEl = this.doc.querySelector(".screenshots img");
    this.toggleEl = this.doc.querySelector("#toggle");
    this.searchBox = this.doc.querySelector("#search-input");

    this.toggleEl.addEventListener("click", this.toggle);
    this.searchBox.addEventListener("input", this.search);
  },

  toggle() {
    if (!this.isStarted) {
      this.start();
    } else {
      this.stop();
    }
  },

  start() {
    if (this.isStarted) {
      return;
    }
    this.isStarted = true;

    this.toggleEl.setAttribute("checked", "true")
    this.recordsEl.innerHTML = "";
    this.searchBox.value = "";
    this.mm.sendAsyncMessage("PageRecorder:Start");
  },

  stop() {
    if (!this.isStarted) {
      return;
    }
    this.isStarted = false;

    this.toggleEl.removeAttribute("checked");
    this.mm.sendAsyncMessage("PageRecorder:Stop");
  },

  search() {
    let query = this.searchBox.value.toLowerCase();
    if(!this.recordsEl.mozMatchesSelector(":empty") || !this.recordsEl.matches(":empty")) {
      [].forEach.call(this.recordsEl.querySelectorAll("li"), function(el) {
        if(query == "" || el.textContent.toLowerCase().indexOf(query) > -1) {
          el.style.removeProperty("display");
        }
        else {
          el.style.display = "none";
        }
      });
    }
  },

  onRecordings({data: records, objects}) {
    if (!this.isStarted) {
      return;
    }

    // data and objects are 2 equally sized arrays, data contains the actual
    // records, and objects the corresponding CPOW targets if any.
    for (let i = 0; i < records.length; i ++) {
      let record = records[i];
      let target = objects[i];

      if (record.type === "screenshot") {
        this.lastScreenshot = record.data;
        continue;
      }

      let li = this.doc.createElement("li");
      li.classList.add("record");
      li.classList.add(record.type);
      let self = this;
      (function(src, node) {
        li.addEventListener("mouseover", () => {
          self.screenshotEl.src = src;
          self.mm.sendAsyncMessage("PageRecorder:HighlightNode", null, {node});
        });
        li.addEventListener("mouseout", () => {
          self.mm.sendAsyncMessage("PageRecorder:UnhighlightNode");
        });
      })(this.lastScreenshot, target);

      if (target) {
        li.appendChild(this.buildTargetOutput(target));
      }

      let formatterData = {
        parentEl: li,
        data: record.data,
        time: record.time
      };
      if (this["buildRecordOutputFor_" + record.type]) {
        this["buildRecordOutputFor_" + record.type](formatterData);
      } else {
        this["buildRecordOutputFor_unknown"](formatterData);
      }

      let query = this.searchBox.value.toLowerCase();
      if (query !== "" || li.textContent.toLowerCase().indexOf(query) == -1) {
        li.style.display = "none";
      }

      this.recordsEl.appendChild(li);
    }

    this.recordsEl.scrollTop = this.recordsEl.scrollHeight;
  },

  buildRecordOutputFor_mutation(formatterData) {
    // Break it down further by mutation type
    let type = formatterData.data.type;
    if (this["buildRecordOutputFor_mutation_" + type]) {
      this["buildRecordOutputFor_mutation_" + type](formatterData);
    } else {
      this["buildRecordOutputFor_unknown"](formatterData);
    }
  },

  buildRecordOutputFor_mutation_attributes({parentEl, data, time}) {
    let {name, oldValue, value} = data.reason;

    let outputEl = this.doc.createElement("span");
    outputEl.innerHTML = "Attribute " + name +
                         " changed from \"" +
                         oldValue + "\" to \"" +
                         value + "\"";
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_mutation_childList({parentEl, data, time}) {
    let {addedNodes, removedNodes} = data.reason;

    let outputEl = this.doc.createElement("span");
    outputEl.innerHTML = "Child nodes changed: added " + addedNodes + " nodes" +
                         " and removed " + removedNodes + " nodes";
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_event({parentEl, data, time}) {
    let outputEl = this.doc.createElement("span");
    outputEl.textContent = "Event " + data.type;
    parentEl.appendChild(outputEl);
  },

  buildRecordOutputFor_unknown({parentEl, data, time}) {
    let outputEl = this.doc.createElement("span");
    outputEl.textContent = "Unknown change";
    parentEl.appendChild(outputEl);
  },

  buildTargetOutput(target) {
    let targetEl = this.doc.createElement("span");
    targetEl.classList.add("target");

    let tagEl = this.doc.createElement("span");
    tagEl.classList.add("target-tag");
    tagEl.textContent = "<" + target.localName;
    targetEl.appendChild(tagEl);

    if (target.id) {
      let idEl = this.doc.createElement("span");
      idEl.classList.add("target-id");
      idEl.textContent = "#" + target.id;
      targetEl.appendChild(idEl);
    }
    if (target.classList.length) {
      let classesEl = this.doc.createElement("span");
      classesEl.classList.add("target-classes");
      classesEl.textContent = "." + [...target.classList].join(".");
      targetEl.appendChild(classesEl);
    }

    let closeTagEl = this.doc.createElement("span");
    closeTagEl.classList.add("target-tag");
    closeTagEl.textContent = ">";
    targetEl.appendChild(closeTagEl);

    return targetEl;
  }
};
<|endoftext|>


<commit_before>Ext4.define('Kwf.Ext4.Controller.Grid', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    uses: [ 'Ext.window.MessageBox' ],
    autoSync: true,
    autoLoad: false,

    grid: null,
    deleteButton: null,

    _store: null,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        this.init();
    },

    init: function()
    {
        if (!this.grid) Ext4.Error.raise('grid config is required');
        if (!this.grid instanceof Ext4.grid.Panel) Ext4.Error.raise('grid config needs to be a Ext.grid.Panel');
        var grid = this.grid;

        if (typeof this.deleteButton == 'undefined') this.deleteButton = grid.down('button#delete');
        if (this.deleteButton && !this.deleteButton instanceof Ext4.button.Button) Ext4.Error.raise('deleteButton config needs to be a Ext.button.Button');
        if (this.deleteButton) {
            this.deleteButton.disable();
            this.deleteButton.on('click', this.onDeleteClick, this);
        }

        if (typeof this.exportCsvButton == 'undefined') this.exportCsvButton = grid.down('button#exportCsv');
        if (this.exportCsvButton && !this.exportCsvButton instanceof Ext4.button.Button) Ext4.Error.raise('exportCsvButton config needs to be a Ext.button.Button');
        if (this.exportCsvButton) {
            this.exportCsvButton.on('click', this.csvExport, this);
        }

        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (this.deleteButton) this.deleteButton.enable();
            } else {
                if (this.deleteButton) this.deleteButton.disable();
            }
        }, this);
        Ext4.each(grid.query('> toolbar[dock=top] field'), function(field) {
            field.on('change', function() {
                var filterId = 'filter-'+field.getName();
                var v = field.getValue();
                var filter = this.grid.getStore().filters.get(filterId);
                if (!filter || filter.value != v) {
                    this.grid.getStore().addFilter({
                        id: filterId,
                        property: field.getName(),
                        value: v
                    });
                }
            }, this, { buffer: 300 });
        }, this);

        if (grid.getStore()) this.onBindStore();
        Ext4.Function.interceptAfter(grid, "bindStore", this.onBindStore, this);

        if (this.autoLoad) {
            this.grid.getStore().load();
        }
    },

    onDeleteClick: function()
    {
        if (this.autoSync) {
            Ext4.Msg.show({
                title: trlKwf('Delete'),
                msg: trlKwf('Do you really wish to remove this entry?'),
                buttons: Ext4.Msg.YESNO,
                scope: this,
                fn: function(button) {
                    if (button == 'yes') {
                        this.deleteSelected();
                    }
                }
            });
        } else {
            this.deleteSelected();
        }
    },

    deleteSelected: function()
    {
        this.grid.getStore().remove(this.grid.getSelectionModel().getSelection());
        if (this.autoSync) {
            this.grid.getStore().sync({
                success: function() {
                    this.fireEvent('savesuccess');
                },
                scope: this
            });
            this.fireEvent('save');
        }
    },

    onBindStore: function()
    {
        var s = this.grid.getStore();
        this._store = s;
        Ext4.each(this.grid.query('pagingtoolbar'), function(i) {
            i.bindStore(s);
        }, this);
        Ext4.each(this.grid.query('> toolbar[dock=top] field'), function(field) {
            var filterId = 'filter-'+field.getName();
            var v = field.getValue();
            if (typeof v == 'undefined') v = null;
            this.grid.getStore().addFilter({
                id: filterId,
                property: field.getName(),
                value: v
            }, false);
        }, this);

        this.fireEvent('bindstore', s);
    },

    csvExport: function()
    {
        var csv = '';

        //header
        var sep = '';
        Ext4.each(this.grid.columns, function(col) {
            if (!col.dataIndex) return;
            csv += sep+col.text;
            sep = ';';
        }, this);
        csv += '\n';

        var pageSize = 25;
        var totalCount = this._store.getTotalCount();
        var pageCount = Math.ceil(totalCount / pageSize);
        var page = 1;

        //create own store, so grid doesn't display loaded rows
        var store = this._store.self.create({
            filters: this._store.filters.items,
            sorters: this._store.sorters.items,
            pageSize: pageSize
        });

        Ext4.Msg.show({
            title: trlKwf('Export'),
            msg: trlKwf('Exporting rows...'),
            progress: true,
            buttons: Ext4.Msg.CANCEL
        });

        loadPage.call(this);

        function loadPage()
        {
            if (!Ext4.Msg.isVisible()) return; //export cancelled
            Ext4.Msg.updateProgress((page-1)/pageCount);
            store.loadPage(page, {
                callback: function() {
                    exportRows.call(this);
                    if (page < pageCount) {
                        page++;
                        loadPage.call(this);
                    } else {
                        Ext4.Msg.updateProgress(1);
                        createDownload.call(this);
                    }
                },
                scope: this
            });
        }

        function exportRows()
        {
            store.each(function(row) {
                var sep = '';
                Ext4.each(this.grid.columns, function(col) {
                    if (!col.dataIndex) return;
                    var val = row.get(col.dataIndex);
                    if (col.renderer) {
                        val = Ext.util.Format.stripTags(col.renderer(val, col, row));
                    }
                    if (!val) val = '';
                    csv += sep;
                    csv += String(val).replace('\\', '\\\\').replace(';', '\;').replace('\n', '\\n');
                    sep = ';';
                }, this);
                csv += '\n';
            }, this);
        }

        function createDownload()
        {
            //TODO IE8 compatibility
            var URL = window.URL || window.webkiURL;
            var blob = new Blob([csv]);
            var blobURL = URL.createObjectURL(blob);
            var a = this.grid.el.createChild({
                tag: 'a',
                href: blobURL,
                style: 'display:none;',
                download: 'export.csv'
            });
            a.dom.click();
            a.remove();
            Ext4.Msg.hide();
        }
    }
});
<commit_msg>Ext4: don't initialize with null as auto-detecting the button doesn't work anymore then<commit_after>Ext4.define('Kwf.Ext4.Controller.Grid', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    uses: [ 'Ext.window.MessageBox' ],
    autoSync: true,
    autoLoad: false,

    grid: null,

    _store: null,
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);
        this.init();
    },

    init: function()
    {
        if (!this.grid) Ext4.Error.raise('grid config is required');
        if (!this.grid instanceof Ext4.grid.Panel) Ext4.Error.raise('grid config needs to be a Ext.grid.Panel');
        var grid = this.grid;

        if (typeof this.deleteButton == 'undefined') this.deleteButton = grid.down('button#delete');
        if (this.deleteButton && !this.deleteButton instanceof Ext4.button.Button) Ext4.Error.raise('deleteButton config needs to be a Ext.button.Button');
        if (this.deleteButton) {
            this.deleteButton.disable();
            this.deleteButton.on('click', this.onDeleteClick, this);
        }

        if (typeof this.exportCsvButton == 'undefined') this.exportCsvButton = grid.down('button#exportCsv');
        if (this.exportCsvButton && !this.exportCsvButton instanceof Ext4.button.Button) Ext4.Error.raise('exportCsvButton config needs to be a Ext.button.Button');
        if (this.exportCsvButton) {
            this.exportCsvButton.on('click', this.csvExport, this);
        }

        grid.on('selectionchange', function(model, rows) {
            if (rows[0]) {
                var row = rows[0];
                if (this.deleteButton) this.deleteButton.enable();
            } else {
                if (this.deleteButton) this.deleteButton.disable();
            }
        }, this);
        Ext4.each(grid.query('> toolbar[dock=top] field'), function(field) {
            field.on('change', function() {
                var filterId = 'filter-'+field.getName();
                var v = field.getValue();
                var filter = this.grid.getStore().filters.get(filterId);
                if (!filter || filter.value != v) {
                    this.grid.getStore().addFilter({
                        id: filterId,
                        property: field.getName(),
                        value: v
                    });
                }
            }, this, { buffer: 300 });
        }, this);

        if (grid.getStore()) this.onBindStore();
        Ext4.Function.interceptAfter(grid, "bindStore", this.onBindStore, this);

        if (this.autoLoad) {
            this.grid.getStore().load();
        }
    },

    onDeleteClick: function()
    {
        if (this.autoSync) {
            Ext4.Msg.show({
                title: trlKwf('Delete'),
                msg: trlKwf('Do you really wish to remove this entry?'),
                buttons: Ext4.Msg.YESNO,
                scope: this,
                fn: function(button) {
                    if (button == 'yes') {
                        this.deleteSelected();
                    }
                }
            });
        } else {
            this.deleteSelected();
        }
    },

    deleteSelected: function()
    {
        this.grid.getStore().remove(this.grid.getSelectionModel().getSelection());
        if (this.autoSync) {
            this.grid.getStore().sync({
                success: function() {
                    this.fireEvent('savesuccess');
                },
                scope: this
            });
            this.fireEvent('save');
        }
    },

    onBindStore: function()
    {
        var s = this.grid.getStore();
        this._store = s;
        Ext4.each(this.grid.query('pagingtoolbar'), function(i) {
            i.bindStore(s);
        }, this);
        Ext4.each(this.grid.query('> toolbar[dock=top] field'), function(field) {
            var filterId = 'filter-'+field.getName();
            var v = field.getValue();
            if (typeof v == 'undefined') v = null;
            this.grid.getStore().addFilter({
                id: filterId,
                property: field.getName(),
                value: v
            }, false);
        }, this);

        this.fireEvent('bindstore', s);
    },

    csvExport: function()
    {
        var csv = '';

        //header
        var sep = '';
        Ext4.each(this.grid.columns, function(col) {
            if (!col.dataIndex) return;
            csv += sep+col.text;
            sep = ';';
        }, this);
        csv += '\n';

        var pageSize = 25;
        var totalCount = this._store.getTotalCount();
        var pageCount = Math.ceil(totalCount / pageSize);
        var page = 1;

        //create own store, so grid doesn't display loaded rows
        var store = this._store.self.create({
            filters: this._store.filters.items,
            sorters: this._store.sorters.items,
            pageSize: pageSize
        });

        Ext4.Msg.show({
            title: trlKwf('Export'),
            msg: trlKwf('Exporting rows...'),
            progress: true,
            buttons: Ext4.Msg.CANCEL
        });

        loadPage.call(this);

        function loadPage()
        {
            if (!Ext4.Msg.isVisible()) return; //export cancelled
            Ext4.Msg.updateProgress((page-1)/pageCount);
            store.loadPage(page, {
                callback: function() {
                    exportRows.call(this);
                    if (page < pageCount) {
                        page++;
                        loadPage.call(this);
                    } else {
                        Ext4.Msg.updateProgress(1);
                        createDownload.call(this);
                    }
                },
                scope: this
            });
        }

        function exportRows()
        {
            store.each(function(row) {
                var sep = '';
                Ext4.each(this.grid.columns, function(col) {
                    if (!col.dataIndex) return;
                    var val = row.get(col.dataIndex);
                    if (col.renderer) {
                        val = Ext.util.Format.stripTags(col.renderer(val, col, row));
                    }
                    if (!val) val = '';
                    csv += sep;
                    csv += String(val).replace('\\', '\\\\').replace(';', '\;').replace('\n', '\\n');
                    sep = ';';
                }, this);
                csv += '\n';
            }, this);
        }

        function createDownload()
        {
            //TODO IE8 compatibility
            var URL = window.URL || window.webkiURL;
            var blob = new Blob([csv]);
            var blobURL = URL.createObjectURL(blob);
            var a = this.grid.el.createChild({
                tag: 'a',
                href: blobURL,
                style: 'display:none;',
                download: 'export.csv'
            });
            a.dom.click();
            a.remove();
            Ext4.Msg.hide();
        }
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import Backbone from 'backbone';
import stores from 'stores';
import ProjectSelect from '../instance_launch/ProjectSelect.react';
import BootstrapModalMixin from 'components/mixins/BootstrapModalMixin.react';

export default React.createClass({
      displayName: "ProjectAddImageModal",

      mixins: [BootstrapModalMixin],

      propTypes: {
        image: React.PropTypes.instanceOf(Backbone.Model).isRequired,
      },
      selectFirstAvailable: function(projects, existing_projects) {
         let firstProjectID = 0;
         if(projects == null || existing_projects == null) {
             return firstProjectID;
         }
         projects.forEach(function (project) {
             let project_id = project.id;
             project_match = existing_projects.filter(function (existing_project) {
                 let test_project_id = existing_project.get('project').id;
                 return (test_project_id == project_id)
	     });
             if (firstProjectID !== 0) {
                 return;
             }
             if (project_match.length === 0) {
                 firstProjectID = project_match.id;
             }
         });
        return firstProjectID;
      },
      getInitialState: function() {
          //Note: This should be available already. But we have a 'fallback' in render()
          let projects = stores.ProjectStore.getAll();
          let existing_projects = stores.ProjectImageStore.getProjectsFor(
              this.props.image.id);
          if(projects != null && projects.length > 0) {
              projectId = this.selectFirstAvailable(projects, existing_projects)
          } else {
              projectId = 0;
          }
          return {
            projectId: projectId,
            projects: projects,
            existing: existing_projects,
          }
      },
      updateState: function() {
        let updatedState = {};
        if(this.state.projects == null) {
            projects = stores.ProjectStore.getAll();
            updatedState.projects = projects;
        }
        if(this.state.existing == null) {
            existing = stores.ProjectImageStore.getProjectsFor(
            this.props.image.id);
            updatedState.existing = existing;
        }
        this.setState(updatedState);
      },

      isSubmittable: function () {
        let projects = this.filterRemainingProjects();
        let hasProject = !!this.state.projectId && projects.length > 0;
        return hasProject;
      },

      //
      // Mounting & State
      // ----------------
      //

      componentDidMount: function () {
        stores.ProjectStore.addChangeListener(this.updateState);
        stores.ProjectImageStore.addChangeListener(this.updateState);
      },

      componentWillUnmount: function () {
        stores.ProjectStore.removeChangeListener(this.updateState);
        stores.ProjectImageStore.removeChangeListener(this.updateState);
      },

      //
      // Internal Modal Callbacks
      // ------------------------
      //

      cancel: function () {
        this.hide();
      },

      confirm: function () {
        this.hide();
        var project = this.state.projects.get(this.state.projectId);
        //Action to add 'image' to 'project' happens in 'props.onConfirm'
        this.props.onConfirm(project, this.props.image);
      },

      //
      // Custom Modal Callbacks
      // ----------------------
      //

      onProjectChange: function (e) {
        var newProjectId = parseInt(e.target.value); //Remove parseInt when we use UUIDs here.
        this.setState({projectId: newProjectId});
      },

      //
      // Render
      // ------
      //

      renderImage: function () {
        return (

 {this.props.image.get('name')}

        );
      },
      renderExistingProjects: function () {
          if(this.state.existing == null) {
              return (
);
          }
          project_divs = this.state.existing.map(function(existing_project) {
              return (

                  {existing_project.get('project').name}

              );
          });

          return (

               Added to Project(s)

                 {project_divs}

          );
      },
      filterRemainingProjects: function() {
          if(this.state.projects == null || this.state.existing == null) {
              return [];
          }
          var self = this;
          var project_arr = this.state.projects.filter(function(project) {
              needle = project.id;
              haystack_matches = self.state.existing.filter(
                  function(existing_project) {
                      project_id = existing_project.get('project').id;
                      return project_id == needle;
              });
              return haystack_matches.length == 0;
          });
          projects = new Backbone.Collection(project_arr);
          return projects;
      },
      renderProjects: function () {
          if(this.state.projects == null || this.state.existing == null) {
              return (
);
          }
          let projects = this.filterRemainingProjects();
          if( projects.length == 0) {
              //NOTE: This could be better achieved by handling the 'empty case' (Exception message)
              //separately from the 'null case' (loading)..
              //Because the component re-used was not in 'common' I will leave this as an exercise
              //for the reader.
              return (

                  Project
                  <select className='form-control' id='project'>
                    <option value="-1">No Project Available.</option>
                  </select>

              );
          } else if(this.state.projectId == 0) {
              //Zero represents a value that has not yet been set.
              //BUG!
              this.state.projectId = projects.first().id;
          }

          return (

               Project
               <ProjectSelect
                 projectId={this.state.projectId}
                 projects={projects}
                 onChange={this.onProjectChange}
                 />

          );
      },
      renderBody: function () {
         return (

               Add Image to Project

Select a project to add the image:

                 {this.renderImage()}

             {this.renderProjects()}
             {this.renderExistingProjects()}

         );
      },

      render: function () {
        // todo: If the user only has one project, provide an action to create another project

        return (

                  {this.renderCloseButton()}
                  Add Image to Project

                  {this.renderBody()}

                  <button type="button" className="btn btn-danger" onClick={this.cancel}>
                    Cancel
                  </button>
                  <button type="button" className="btn btn-primary" onClick={this.confirm}
                          disabled={!this.isSubmittable()}>
                    Add image to project
                  </button>

        );
      }
});
<commit_msg>Removed mix of tabs/spaces & reformatted<commit_after>import React from 'react';
import Backbone from 'backbone';
import stores from 'stores';
import ProjectSelect from '../instance_launch/ProjectSelect.react';
import BootstrapModalMixin from 'components/mixins/BootstrapModalMixin.react';

export default React.createClass({
    displayName: 'ProjectAddImageModal',

    mixins: [BootstrapModalMixin],

    propTypes: {
        image: React.PropTypes.instanceOf(Backbone.Model).isRequired,
    },
    selectFirstAvailable: function(projects, existing_projects) {
        let firstProjectID = 0;
        if (projects == null || existing_projects == null) {
            return firstProjectID;
        }
        projects.forEach(function(project) {
            let project_id = project.id;
            project_match = existing_projects.filter(function(existing_project) {
                let test_project_id = existing_project.get('project').id;
                return (test_project_id == project_id)
            });
            if (firstProjectID !== 0) {
                return;
            }
            if (project_match.length === 0) {
                firstProjectID = project_match.id;
            }
        });
        return firstProjectID;
    },
    getInitialState: function() {
        //Note: This should be available already. But we have a 'fallback' in render()
        let projects = stores.ProjectStore.getAll();
        let existing_projects = stores.ProjectImageStore.getProjectsFor(
            this.props.image.id);
        if (projects != null && projects.length > 0) {
            projectId = this.selectFirstAvailable(projects, existing_projects)
        } else {
            projectId = 0;
        }
        return {
            projectId: projectId,
            projects: projects,
            existing: existing_projects,
        }
    },
    updateState: function() {
        let updatedState = {};
        if (this.state.projects == null) {
            projects = stores.ProjectStore.getAll();
            updatedState.projects = projects;
        }
        if (this.state.existing == null) {
            existing = stores.ProjectImageStore.getProjectsFor(
                this.props.image.id);
            updatedState.existing = existing;
        }
        this.setState(updatedState);
    },

    isSubmittable: function() {
        let projects = this.filterRemainingProjects();
        let hasProject = !!this.state.projectId && projects.length > 0;
        return hasProject;
    },

    //
    // Mounting & State
    // ----------------
    //

    componentDidMount: function() {
        stores.ProjectStore.addChangeListener(this.updateState);
        stores.ProjectImageStore.addChangeListener(this.updateState);
    },

    componentWillUnmount: function() {
        stores.ProjectStore.removeChangeListener(this.updateState);
        stores.ProjectImageStore.removeChangeListener(this.updateState);
    },

    //
    // Internal Modal Callbacks
    // ------------------------
    //

    cancel: function() {
        this.hide();
    },

    confirm: function() {
        this.hide();
        var project = this.state.projects.get(this.state.projectId);
        //Action to add 'image' to 'project' happens in 'props.onConfirm'
        this.props.onConfirm(project, this.props.image);
    },

    //
    // Custom Modal Callbacks
    // ----------------------
    //

    onProjectChange: function(e) {
        var newProjectId = parseInt(e.target.value); //Remove parseInt when we use UUIDs here.
        this.setState({
            projectId: newProjectId
        });
    },

    //
    // Render
    // ------
    //

    renderImage: function() {
        return (

            { this.props.image.get('name') }

        );
    },
    renderExistingProjects: function() {
        if (this.state.existing == null) {
            return (
);
        }
        project_divs = this.state.existing.map(function(existing_project) {
            return (

                { existing_project.get('project').name }

            );
        });

        return (

                Added to Project(s)

                { project_divs }

        );
    },
    filterRemainingProjects: function() {
        if (this.state.projects == null || this.state.existing == null) {
            return [];
        }
        var self = this;
        var project_arr = this.state.projects.filter(function(project) {
            needle = project.id;
            haystack_matches = self.state.existing.filter(
                function(existing_project) {
                    project_id = existing_project.get('project').id;
                    return project_id == needle;
                });
            return haystack_matches.length == 0;
        });
        projects = new Backbone.Collection(project_arr);
        return projects;
    },
    renderProjects: function() {
        if (this.state.projects == null || this.state.existing == null) {
            return (
);
        }
        let projects = this.filterRemainingProjects();
        if (projects.length == 0) {
            //NOTE: This could be better achieved by handling the 'empty case' (Exception message)
            //separately from the 'null case' (loading)..
            //Because the component re-used was not in 'common' I will leave this as an exercise
            //for the reader.
            return (

                    Project

                <select className='form-control' id='project'>
                    <option value="-1">
                        No Project Available.
                    </option>
                </select>

            );
        } else if (this.state.projectId == 0) {
            //Zero represents a value that has not yet been set.
            //BUG!
            this.state.projectId = projects.first().id;
        }

        return (

                Project

            <ProjectSelect projectId={ this.state.projectId } projects={ projects } onChange={ this.onProjectChange } />

        );
    },
    renderBody: function() {
        return (

                    Add Image to Project

                    Select a project to add the image:

                    { this.renderImage() }

            { this.renderProjects() }
            { this.renderExistingProjects() }

        );
    },

    render: function() {
        // todo: If the user only has one project, provide an action to create another project

        return (

                        { this.renderCloseButton() }
                        Add Image to Project

                        { this.renderBody() }

                        <button type="button" className="btn btn-danger" onClick={ this.cancel }>
                            Cancel
                        </button>
                        <button type="button"
                                className="btn btn-primary"
                                onClick={ this.confirm }
                                disabled={ !this.isSubmittable() }>
                            Add image to project
                        </button>

        );
    }
});
<|endoftext|>


<commit_before>/*
 * runtime.js
 * Objective-J
 *
 * Created by Francisco Tolmasky.
 * Copyright 2008, 280 North, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

var CLS_CLASS           = 0x1,
    CLS_META            = 0x2,
    CLS_INITIALIZED     = 0x4,
    CLS_INITIALIZING    = 0x8;

#define CHANGEINFO(aClass, aSetInfoMask, aClearInfoMask) aClass.info = (aClass.info | (aSetInfoMask)) & ~(aClearInfoMask)
#define GETINFO(aClass, anInfoMask) (aClass.info & (anInfoMask))
#define SETINFO(aClass, anInfoMask) CHANGEINFO(aClass, anInfoMask, 0)
#define CLEARINFO(aClass, anInfoMask) CHANGEINFO(aClass, 0, anInfoMask)
#define ISMETA(aClass) (GETINFO(aClass, CLS_META))
#define GETMETA(aClass) (ISMETA(aClass) ? aClass : aClass.isa)
#define ISINITIALIZED(aClass) GETINFO(GETMETA(aClass), CLS_INITIALIZED)

function objj_ivar(/*String*/ aName, /*String*/ aType)
{
    this.name = aName;
    this.type = aType;
}

function objj_method(/*String*/ aName, /*IMP*/ anImplementation, /*String*/ types)
{
    this.name = aName;
    this.method_imp = anImplementation;
    this.types = types;
}

function objj_class()
{
    this.isa            = NULL;

    this.super_class    = NULL;
    this.sub_classes    = [];

    this.name           = NULL;
    this.info           = 0;
    this.ivars          = [];

    this.method_list    = [];
    this.method_hash    = {};

    this.method_store   = function() { };
    this.method_dtable  = this.method_store.prototype;

    this.allocator      = function() { };
    this.__address      = -1;
}

function objj_object()
{
    this.isa        = NULL;
    this.__address  = -1;
}

// Addressing Objects

var OBJECT_COUNT   = 0;

function _objj_generateObjectHash()
{
    return OBJECT_COUNT++;
}

#define _objj_generateObjectHash() (OBJECT_COUNT++)

// Working with Classes

function class_getName(/*Class*/ aClass)
{
    if (aClass == Nil)
        return "";

    return aClass.name;
}

function class_isMetaClass(/*Class*/ aClass)
{
    if (!aClass)
        return NO;

    return ISMETA(aClass);
}

function class_getSuperclass(/*Class*/ aClass)
{
    if (aClass == Nil)
        return Nil;

    return aClass.super_class;
}

function class_setSuperclass(/*Class*/ aClass, /*Class*/ aSuperClass)
{
    // FIXME: implement.
}

function class_isMetaClass(/*Class*/ aClass)
{
    return ISMETA(aClass);
}

function class_addIvar(/*Class*/ aClass, /*String*/ aName, /*String*/ aType)
{
    var thePrototype = aClass.allocator.prototype;

    if (typeof thePrototype[aName] != "undefined")
        return NO;

    aClass.ivars.push(new objj_ivar(aName, aType));
    thePrototype[aName] = NULL;

    return YES;
}

function class_addIvars(/*Class*/ aClass, /*Array*/ivars)
{
    var index = 0,
        count = ivars.length,
        thePrototype = aClass.allocator.prototype;

    for (; index < count; ++index)
    {
        var ivar = ivars[index],
            name = ivar.name;

        if (typeof thePrototype[name] == "undefined")
        {
            aClass.ivars.push(ivar);
            thePrototype[name] = NULL;
        }
    }
}

function class_copyIvarList(/*Class*/ aClass)
{
    return aClass.ivars.slice(0);
}

//#define class_copyIvarList(aClass) (aClass.ivars.slice(0))

function class_addMethod(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anImplementation, /*String*/aType)
{
    if (aClass.method_hash[aName])
        return NO;

    var method = new objj_method(aName, anImplementation, aType);

    aClass.method_list.push(method);
    aClass.method_dtable[aName] = method;

    // FIXME: Should this be done here?
    // If this is a root class...
    if (!ISMETA(aClass) && GETMETA(aClass).isa == GETMETA(aClass))
        class_addMethods(GETMETA(aClass), methods);

    return YES;
}

function class_addMethods(/*Class*/ aClass, /*Array*/ methods)
{
    var index = 0,
        count = methods.length,

        method_list = aClass.method_list,
        method_dtable = aClass.method_dtable;

    for (; index < count; ++index)
    {
        var method = methods[index];

        if (aClass.method_hash[method.name])
            continue;

        method_list.push(method);
        method_dtable[method.name] = method;
    }

    // If this is a root class...
    if (!ISMETA(aClass) && GETMETA(aClass).isa == GETMETA(aClass))
        class_addMethods(GETMETA(aClass), methods);
}

function class_getInstanceMethod(/*Class*/ aClass, /*SEL*/ aSelector)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = aClass.method_dtable[aSelector];

    return method ? method : NULL;
}

function class_getClassMethod(/*Class*/ aClass, /*SEL*/ aSelector)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = GETMETA(aClass).method_dtable[aSelector];

    return method ? method : NULL;
}

function class_copyMethodList(/*Class*/ aClass)
{
    return aClass.method_list.slice(0);
}

function class_replaceMethod(/*Class*/ aClass, /*SEL*/ aSelector, /*IMP*/ aMethodImplementation)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = aClass.method_dtable[aSelector],
        method_imp = NULL;

    if (method)
        method_imp = method.method_imp;

    method.method_imp = aMethodImplementation;

    return method_imp;
}

var _class_initialize = function(/*Class*/ aClass)
{
    var meta = GETMETA(aClass);

    if (GETINFO(aClass, CLS_META))
        aClass = objj_getClass(aClass.name);

    if (aClass.super_class && !ISINITIALIZED(aClass.super_class))
        _class_initialize(aClass.super_class);

    if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING))
    {
        SETINFO(meta, CLS_INITIALIZING);

        objj_msgSend(aClass, "initialize");

        CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING);
    }
}

var _objj_forward = new objj_method("forward", function(self, _cmd)
{
    return objj_msgSend(self, "forward::", _cmd, arguments);
});

// I think this forward:: may need to be a common method, instead of defined in CPObject.
#define CLASS_GET_METHOD_IMPLEMENTATION(aMethodImplementation, aClass, aSelector)\
    if (!ISINITIALIZED(aClass))\
        _class_initialize(aClass);\
    \
    var method = aClass.method_dtable[aSelector];\
    \
    if (!method)\
        method = _objj_forward;\
    \
    aMethodImplementation = method.method_imp;

function class_getMethodImplementation(/*Class*/ aClass, /*SEL*/ aSelector)
{
    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aClass, aSelector);

    return implementation;
}

// Adding Classes

var GLOBAL_NAMESPACE    = this,
    REGISTERED_CLASSES  = {};

function objj_allocateClassPair(/*Class*/ superclass, /*String*/ aName)
{
    var classObject = new objj_class(),
        metaClassObject = new objj_class(),
        rootClassObject = classObject;

    // If we don't have a superclass, we are the root class.
    if (superclass)
    {
        rootClassObject = superclass;

        while (rootClassObject.superclass)
            rootClassObject = rootClassObject.superclass;

        // Give our current allocator all the instance variables of our super class' allocator.
        classObject.allocator.prototype = new superclass.allocator;

        // "Inheret" parent methods.
        classObject.method_store.prototype = new superclass.method_store;
        classObject.method_dtable = classObject.method_store.prototype;

        metaClassObject.method_store.prototype = new superclass.isa.method_store;
        metaClassObject.method_dtable = metaClassObject.method_store.prototype;

        // Set up the actual class hierarchy.
        classObject.super_class = superclass;
        metaClassObject.super_class = superclass.isa;
    }
    else
        classObject.allocator.prototype = new objj_object();

    classObject.isa = metaClassObject;
    classObject.name = aName;
    classObject.info = CLS_CLASS;
    classObject.__address = _objj_generateObjectHash();

    metaClassObject.isa = rootClassObject.isa;
    metaClassObject.name = aName;
    metaClassObject.info = CLS_META;
    metaClassObject.__address = _objj_generateObjectHash();

    return classObject;
}

function objj_registerClassPair(/*Class*/ aClass)
{
    GLOBAL_NAMESPACE[aClass.name] = aClass;
    REGISTERED_CLASSES[aClass.name] = aClass;
}

// Instantiating Classes

function class_createInstance(/*Class*/ aClass)
{
    if (!aClass)
        objj_exception_throw(new objj_exception(OBJJNilClassException, "*** Attempting to create object with Nil class."));

    var object = new aClass.allocator;

    object.__address = _objj_generateObjectHash();
    object.isa = aClass;

    return object;
}

// Opera 9.5.1 has a bug where prototypes "inheret" members from instances when "with" is used.
// Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go
// ahead and do that.

var prototype_bug = function() { }

prototype_bug.prototype.member = false;

with (new prototype_bug())
    member = true;

// If the bug exists, go down the slow path.
if (new prototype_bug().member)
{

var fast_class_createInstance = class_createInstance;

class_createInstance = function(/*Class*/ aClass)
{
    var object = fast_class_createInstance(aClass);

    if (object)
    {
        var theClass = object.isa,
            actualClass = theClass;

        while (theClass)
        {
            var ivars = theClass.ivars;
                count = ivars.length;

            while (count--)
                object[ivars[count].name] = NULL;

            theClass = theClass.super_class;
        }

        object.isa = actualClass;
    }

    return object;
}

}

// Working with Instances

function object_getClassName(/*id*/ anObject)
{
    if (!anObject)
        return "";

    var theClass = anObject.isa;

    return theClass ? class_getName(theClass) : "";
}

//objc_getClassList
function objj_lookUpClass(/*String*/ aName)
{
    var theClass = REGISTERED_CLASSES[aName];

    return theClass ? theClass : Nil;
}

function objj_getClass(/*String*/ aName)
{
    var theClass = REGISTERED_CLASSES[aName];

    if (!theClass)
    {
        // class handler callback???
    }

    return theClass ? theClass : Nil;
}

//objc_getRequiredClass
function objj_getMetaClass(/*String*/ aName)
{
    var theClass = objj_getClass(aName);

    return GETMETA(theClass);
}

// Working with Instance Variables

function ivar_getName(anIvar)
{
    return anIvar.name;
}

function ivar_getTypeEncoding(anIvar)
{
    return anIvar.type;
}

// Sending Messages

function objj_msgSend(/*id*/ aReceiver, /*SEL*/ aSelector)
{
    if (aReceiver == nil)
        return nil;

    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aReceiver.isa, aSelector);

    return implementation.apply(aReceiver, arguments);
}

function objj_msgSendSuper(/*id*/ aSuper, /*SEL*/ aSelector)
{
    var super_class = aSuper.super_class;

    arguments[0] = aSuper.receiver;

    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, super_class, aSelector);

    return implementation.apply(aSuper.receiver, arguments);
}

// Working with Methods

function method_getName(/*Method*/ aMethod)
{
    return aMethod.name;
}

function method_getImplementation(/*Method*/ aMethod)
{
    return aMethod.method_imp;
}

function method_setImplementation(/*Method*/ aMethod, /*IMP*/ anImplementation)
{
    var oldImplementation = aMethod.method_imp;

    aMethod.method_imp = anImplementation;

    return oldImplementation;
}

function method_exchangeImplementations(/*Method*/ lhs, /*Method*/ rhs)
{
    var lhs_imp = method_getImplementation(lhs),
        rhs_imp = method_getImplementation(rhs);

    method_setImplementation(lhs, rhs_imp);
    method_setImplementation(rhs, lhs_imp);
}

// Working with Selectors

function sel_getName(aSelector)
{
    return aSelector ? aSelector : "<null selector>";
}

function sel_getUid(/*String*/ aName)
{
    return aName;
}

function sel_isEqual(/*SEL*/ lhs, /*SEL*/ rhs)
{
    return lhs == rhs;
}

function sel_registerName(aName)
{
    return aName;
}
<commit_msg>Fix for all ObjJ methods showing up as anonymous functions in Safari profiler.<commit_after>/*
 * runtime.js
 * Objective-J
 *
 * Created by Francisco Tolmasky.
 * Copyright 2008, 280 North, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

var CLS_CLASS           = 0x1,
    CLS_META            = 0x2,
    CLS_INITIALIZED     = 0x4,
    CLS_INITIALIZING    = 0x8;

#define CHANGEINFO(aClass, aSetInfoMask, aClearInfoMask) aClass.info = (aClass.info | (aSetInfoMask)) & ~(aClearInfoMask)
#define GETINFO(aClass, anInfoMask) (aClass.info & (anInfoMask))
#define SETINFO(aClass, anInfoMask) CHANGEINFO(aClass, anInfoMask, 0)
#define CLEARINFO(aClass, anInfoMask) CHANGEINFO(aClass, 0, anInfoMask)
#define ISMETA(aClass) (GETINFO(aClass, CLS_META))
#define GETMETA(aClass) (ISMETA(aClass) ? aClass : aClass.isa)
#define ISINITIALIZED(aClass) GETINFO(GETMETA(aClass), CLS_INITIALIZED)

function objj_ivar(/*String*/ aName, /*String*/ aType)
{
    this.name = aName;
    this.type = aType;
}

function objj_method(/*String*/ aName, /*IMP*/ anImplementation, /*String*/ types)
{
    this.name = aName;
    this.method_imp = anImplementation;
    this.types = types;
}

function objj_class()
{
    this.isa            = NULL;

    this.super_class    = NULL;
    this.sub_classes    = [];

    this.name           = NULL;
    this.info           = 0;
    this.ivars          = [];

    this.method_list    = [];
    this.method_hash    = {};

    this.method_store   = function() { };
    this.method_dtable  = this.method_store.prototype;

    this.allocator      = function() { };
    this.__address      = -1;
}

function objj_object()
{
    this.isa        = NULL;
    this.__address  = -1;
}

// Addressing Objects

var OBJECT_COUNT   = 0;

function _objj_generateObjectHash()
{
    return OBJECT_COUNT++;
}

#define _objj_generateObjectHash() (OBJECT_COUNT++)

// Working with Classes

function class_getName(/*Class*/ aClass)
{
    if (aClass == Nil)
        return "";

    return aClass.name;
}

function class_isMetaClass(/*Class*/ aClass)
{
    if (!aClass)
        return NO;

    return ISMETA(aClass);
}

function class_getSuperclass(/*Class*/ aClass)
{
    if (aClass == Nil)
        return Nil;

    return aClass.super_class;
}

function class_setSuperclass(/*Class*/ aClass, /*Class*/ aSuperClass)
{
    // FIXME: implement.
}

function class_isMetaClass(/*Class*/ aClass)
{
    return ISMETA(aClass);
}

function class_addIvar(/*Class*/ aClass, /*String*/ aName, /*String*/ aType)
{
    var thePrototype = aClass.allocator.prototype;

    if (typeof thePrototype[aName] != "undefined")
        return NO;

    aClass.ivars.push(new objj_ivar(aName, aType));
    thePrototype[aName] = NULL;

    return YES;
}

function class_addIvars(/*Class*/ aClass, /*Array*/ivars)
{
    var index = 0,
        count = ivars.length,
        thePrototype = aClass.allocator.prototype;

    for (; index < count; ++index)
    {
        var ivar = ivars[index],
            name = ivar.name;

        if (typeof thePrototype[name] === "undefined")
        {
            aClass.ivars.push(ivar);
            thePrototype[name] = NULL;
        }
    }
}

function class_copyIvarList(/*Class*/ aClass)
{
    return aClass.ivars.slice(0);
}

//#define class_copyIvarList(aClass) (aClass.ivars.slice(0))

#define METHOD_DISPLAY_NAME(aClass, aMethod) (ISMETA(aClass) ? '+' : '-') + " [" + class_getName(aClass) + ' ' + method_getName(aMethod) + ']'

function class_addMethod(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anImplementation, /*String*/aType)
{
    if (aClass.method_hash[aName])
        return NO;

    var method = new objj_method(aName, anImplementation, aType);

    aClass.method_list.push(method);
    aClass.method_dtable[aName] = method;

    // Give this function a "pretty" name for the console.
    method.method_imp.displayName = METHOD_DISPLAY_NAME(aClass, method);

    // FIXME: Should this be done here?
    // If this is a root class...
    if (!ISMETA(aClass) && GETMETA(aClass).isa === GETMETA(aClass))
        class_addMethods(GETMETA(aClass), methods);

    return YES;
}

function class_addMethods(/*Class*/ aClass, /*Array*/ methods)
{
    var index = 0,
        count = methods.length,

        method_list = aClass.method_list,
        method_dtable = aClass.method_dtable;

    for (; index < count; ++index)
    {
        var method = methods[index];

        if (aClass.method_hash[method.name])
            continue;

        method_list.push(method);
        method_dtable[method.name] = method;

        // Give this function a "pretty" name for the console.
        method.method_imp.displayName = METHOD_DISPLAY_NAME(aClass, method);
    }

    // If this is a root class...
    if (!ISMETA(aClass) && GETMETA(aClass).isa === GETMETA(aClass))
        class_addMethods(GETMETA(aClass), methods);
}

function class_getInstanceMethod(/*Class*/ aClass, /*SEL*/ aSelector)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = aClass.method_dtable[aSelector];

    return method ? method : NULL;
}

function class_getClassMethod(/*Class*/ aClass, /*SEL*/ aSelector)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = GETMETA(aClass).method_dtable[aSelector];

    return method ? method : NULL;
}

function class_copyMethodList(/*Class*/ aClass)
{
    return aClass.method_list.slice(0);
}

function class_replaceMethod(/*Class*/ aClass, /*SEL*/ aSelector, /*IMP*/ aMethodImplementation)
{
    if (!aClass || !aSelector)
        return NULL;

    var method = aClass.method_dtable[aSelector],
        method_imp = NULL;

    if (method)
        method_imp = method.method_imp;

    method.method_imp = aMethodImplementation;

    return method_imp;
}

var _class_initialize = function(/*Class*/ aClass)
{
    var meta = GETMETA(aClass);

    if (GETINFO(aClass, CLS_META))
        aClass = objj_getClass(aClass.name);

    if (aClass.super_class && !ISINITIALIZED(aClass.super_class))
        _class_initialize(aClass.super_class);

    if (!GETINFO(meta, CLS_INITIALIZED) && !GETINFO(meta, CLS_INITIALIZING))
    {
        SETINFO(meta, CLS_INITIALIZING);

        objj_msgSend(aClass, "initialize");

        CHANGEINFO(meta, CLS_INITIALIZED, CLS_INITIALIZING);
    }
}

var _objj_forward = new objj_method("forward", function(self, _cmd)
{
    return objj_msgSend(self, "forward::", _cmd, arguments);
});

// I think this forward:: may need to be a common method, instead of defined in CPObject.
#define CLASS_GET_METHOD_IMPLEMENTATION(aMethodImplementation, aClass, aSelector)\
    if (!ISINITIALIZED(aClass))\
        _class_initialize(aClass);\
    \
    var method = aClass.method_dtable[aSelector];\
    \
    if (!method)\
        method = _objj_forward;\
    \
    aMethodImplementation = method.method_imp;

function class_getMethodImplementation(/*Class*/ aClass, /*SEL*/ aSelector)
{
    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aClass, aSelector);

    return implementation;
}

// Adding Classes

var GLOBAL_NAMESPACE    = this,
    REGISTERED_CLASSES  = {};

function objj_allocateClassPair(/*Class*/ superclass, /*String*/ aName)
{
    var classObject = new objj_class(),
        metaClassObject = new objj_class(),
        rootClassObject = classObject;

    // If we don't have a superclass, we are the root class.
    if (superclass)
    {
        rootClassObject = superclass;

        while (rootClassObject.superclass)
            rootClassObject = rootClassObject.superclass;

        // Give our current allocator all the instance variables of our super class' allocator.
        classObject.allocator.prototype = new superclass.allocator;

        // "Inheret" parent methods.
        classObject.method_store.prototype = new superclass.method_store;
        classObject.method_dtable = classObject.method_store.prototype;

        metaClassObject.method_store.prototype = new superclass.isa.method_store;
        metaClassObject.method_dtable = metaClassObject.method_store.prototype;

        // Set up the actual class hierarchy.
        classObject.super_class = superclass;
        metaClassObject.super_class = superclass.isa;
    }
    else
        classObject.allocator.prototype = new objj_object();

    classObject.isa = metaClassObject;
    classObject.name = aName;
    classObject.info = CLS_CLASS;
    classObject.__address = _objj_generateObjectHash();

    metaClassObject.isa = rootClassObject.isa;
    metaClassObject.name = aName;
    metaClassObject.info = CLS_META;
    metaClassObject.__address = _objj_generateObjectHash();

    return classObject;
}

function objj_registerClassPair(/*Class*/ aClass)
{
    GLOBAL_NAMESPACE[aClass.name] = aClass;
    REGISTERED_CLASSES[aClass.name] = aClass;
}

// Instantiating Classes

function class_createInstance(/*Class*/ aClass)
{
    if (!aClass)
        objj_exception_throw(new objj_exception(OBJJNilClassException, "*** Attempting to create object with Nil class."));

    var object = new aClass.allocator;

    object.__address = _objj_generateObjectHash();
    object.isa = aClass;

    return object;
}

// Opera 9.5.1 has a bug where prototypes "inheret" members from instances when "with" is used.
// Given that the Opera team is so fond of bug-testing instead of version-testing, we'll go
// ahead and do that.

var prototype_bug = function() { }

prototype_bug.prototype.member = false;

with (new prototype_bug())
    member = true;

// If the bug exists, go down the slow path.
if (new prototype_bug().member)
{

var fast_class_createInstance = class_createInstance;

class_createInstance = function(/*Class*/ aClass)
{
    var object = fast_class_createInstance(aClass);

    if (object)
    {
        var theClass = object.isa,
            actualClass = theClass;

        while (theClass)
        {
            var ivars = theClass.ivars;
                count = ivars.length;

            while (count--)
                object[ivars[count].name] = NULL;

            theClass = theClass.super_class;
        }

        object.isa = actualClass;
    }

    return object;
}

}

// Working with Instances

function object_getClassName(/*id*/ anObject)
{
    if (!anObject)
        return "";

    var theClass = anObject.isa;

    return theClass ? class_getName(theClass) : "";
}

//objc_getClassList
function objj_lookUpClass(/*String*/ aName)
{
    var theClass = REGISTERED_CLASSES[aName];

    return theClass ? theClass : Nil;
}

function objj_getClass(/*String*/ aName)
{
    var theClass = REGISTERED_CLASSES[aName];

    if (!theClass)
    {
        // class handler callback???
    }

    return theClass ? theClass : Nil;
}

//objc_getRequiredClass
function objj_getMetaClass(/*String*/ aName)
{
    var theClass = objj_getClass(aName);

    return GETMETA(theClass);
}

// Working with Instance Variables

function ivar_getName(anIvar)
{
    return anIvar.name;
}

function ivar_getTypeEncoding(anIvar)
{
    return anIvar.type;
}

// Sending Messages

function objj_msgSend(/*id*/ aReceiver, /*SEL*/ aSelector)
{
    if (aReceiver == nil)
        return nil;

    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aReceiver.isa, aSelector);

    return implementation.apply(aReceiver, arguments);
}

function objj_msgSendSuper(/*id*/ aSuper, /*SEL*/ aSelector)
{
    var super_class = aSuper.super_class;

    arguments[0] = aSuper.receiver;

    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, super_class, aSelector);

    return implementation.apply(aSuper.receiver, arguments);
}

// Working with Methods

function method_getName(/*Method*/ aMethod)
{
    return aMethod.name;
}

function method_getImplementation(/*Method*/ aMethod)
{
    return aMethod.method_imp;
}

function method_setImplementation(/*Method*/ aMethod, /*IMP*/ anImplementation)
{
    var oldImplementation = aMethod.method_imp;

    aMethod.method_imp = anImplementation;

    return oldImplementation;
}

function method_exchangeImplementations(/*Method*/ lhs, /*Method*/ rhs)
{
    var lhs_imp = method_getImplementation(lhs),
        rhs_imp = method_getImplementation(rhs);

    method_setImplementation(lhs, rhs_imp);
    method_setImplementation(rhs, lhs_imp);
}

// Working with Selectors

function sel_getName(aSelector)
{
    return aSelector ? aSelector : "<null selector>";
}

function sel_getUid(/*String*/ aName)
{
    return aName;
}

function sel_isEqual(/*SEL*/ lhs, /*SEL*/ rhs)
{
    return lhs === rhs;
}

function sel_registerName(aName)
{
    return aName;
}
<|endoftext|>