<commit_before>// This is the JS API to talk to api.participedia.xyz
import queryString from "query-string";
import authService from "./AuthService";
import store from "store";
import Raven from "raven-js";
import emojiStrip from "emoji-strip";

let APIURL = process.env.REACT_APP_API_URL; // eslint-disable-line no-undef

if (!APIURL) {
  console.error(
    "No API URL was found. REACT_APP_API_URL should be set in environment variables."
  );
}

const signedFetch = function(url, method, payload) {
  let opts = {
    method: method || "get",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json"
    }
  };
  let profile = store.get("profile");
  if (profile) {
    profile = JSON.parse(profile);
    if (authService.isAuthenticated()) {
      opts["headers"]["Authorization"] = "Bearer " + authService.getToken();
      opts["headers"]["X-Auth0-Name"] = emojiStrip(profile.name);
      opts["headers"]["X-Auth0-UserId"] = profile.user_id;
    }
    // console.log("doing signed call to", url);
  } else {
    // console.log("doing unsigned call to", url);
    delete opts["headers"]["authorization"];
  }
  if (payload) opts.body = JSON.stringify(payload);
  return fetch(url, opts);
};

class API {
  secureFetch = signedFetch;

  fetchGeoJSON = function(countryCode) {
    let url = APIURL + "/countries/" + countryCode + ".geo.json";
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  countsByCountry = function() {
    let url = APIURL + "/case/countsByCountry";
    return signedFetch(url)
      .then(response => response.json())
      .then(function(json) {
        return json.data.countryCounts;
      })
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  performSearch = function(queryArgs) {
    let querystring = queryString.stringify(queryArgs);
    let url = `${APIURL}/search?${querystring}`;
    // console.log("queryArgs", queryArgs);
    // console.log("DOING SEARCH", url);
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  searchMapTokens = function(selectedCategory, search) {
    let url;
    if (search) {
      url =
        APIURL +
        `/search?selectedCategory=${selectedCategory}&query=${search}&resultType=map`;
    } else {
      url =
        APIURL + `/search?selectedCategory=${selectedCategory}&resultType=map`;
    }
    return signedFetch(url, "get")
      .then(response => response.json())
      .then(json => json.results)
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        throw error;
      });
  };

  fetchCaseById = function(caseId) {
    let url = APIURL + "/case/" + caseId;
    return signedFetch(url, "get")
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        throw error;
      });
  };

  saveNewThing = function(thingType, obj) {
    // console.log("saveNewThing", thingType, obj);
    if (
      thingType !== "case" &&
      thingType !== "method" &&
      thingType !== "organization"
    ) {
      let error = `Can only create cases, methods, and organizations. You sent: ${thingType}`;
      console.error(error);
      throw error;
    }
    let url = APIURL + "/" + thingType + "/new";
    // console.log("sending object", obj);
    return signedFetch(url, "POST", obj)
      .then(response => response.json())
      .then(function(response) {
        if (!response.OK) {
          console.log("Error doing saveNewThing's signedFetch: ", response);
          throw response.error;
        }
        // console.log("got response", response);
        return response.data;
      })
      .then(function(json) {
        // console.log("GOT JSON", json);
        obj.id = json.thingid;
        return obj;
      })
      .catch(function(error) {
        if (error) {
          let errorMsg = `There has been a problem with saving the ${thingType}: (${url}) ${JSON.stringify(
            error
          )}`;

          Raven.captureMessage(
            "error doing signedfetch " +
              JSON.stringify(obj) +
              " -> " +
              errorMsg,
            {
              level: "error"
            }
          );
          console.error(errorMsg);
          // throw error; XXX OIDP
        }
      });
  };

  saveThing = function(thingType, obj) {
    let url = APIURL + `/${thingType}/${obj.id}`;

    // console.log("saving", JSON.stringify(obj));
    delete obj.updated_date; // feels silly to have to do that.

    return signedFetch(url, "PUT", obj)
      .then(response => response.json())
      .then(function(response) {
        if (!response.OK) {
          console.log("Error doing saveThing's signedFetch: ", response);
          throw response.error;
        }
        // console.log("RESPONSE", response);
        return response.data;
      })
      .catch(function(error) {
        if (error) {
          Raven.captureMessage(
            "error in signed fetch sending: " + JSON.stringify(obj),
            {
              level: "error"
            }
          );

          console.error(
            `There has been a problem with saving the ${thingType}: (${url}) ${error}`
          );
          throw error;
        }
      });
  };

  fetchMethodById = function(methodId) {
    let url = APIURL + "/method/" + methodId;
    return signedFetch(url)
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  fetchOrgById = function(caseId) {
    let url = APIURL + "/organization/" + caseId;
    return signedFetch(url)
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  fetchNouns = function(noun) {
    let url = APIURL + "/search/getAllForType?objType=" + noun;
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  addBookmark = function(bookmarkType, thingid) {
    let url = APIURL + "/bookmark/add";
    return signedFetch(url, "POST", { bookmarkType, thingid })
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:addBookmark: (${url}) ${error}`
        );
        return error;
      });
  };
  removeBookmark = function(bookmarkType, thingid) {
    let url = APIURL + "/bookmark/delete";
    return signedFetch(url, "delete", {
      bookmarkType,
      thingid
    })
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:removeBookmark: (${url}) ${error}`
        );
        return error;
      });
  };
  fetchUser = function(userId) {
    let url = APIURL + `/user`;
    if (userId) {
      url = APIURL + `/user/${userId}`;
    }
    return signedFetch(url, "get")
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:fetchUser: (${url}) ${error}`
        );

        return error;
      });
  };

  updateUserMetaData = function(userId, data) {
    const payload = { user_metadata: data };
    console.log(data, "data being sent");
    const url = `https://participedia.auth0.com/api/v2/users/${userId}`;
    return signedFetch(url, "PATCH", payload).then(response => {
      store.set("profile", JSON.stringify(response));
    });
  };

  saveUser = function(user) {
    let url = APIURL + `/user`;
    return signedFetch(url, "post", user)
      .then(response => response.json())
      .then(function(response) {
        if (response.status >= 400) {
          throw new Error(response.statusText);
        }
        return response;
      })
      .catch(function(error) {
        console.log(
          `There has been a problem with API:fetchUser: (${url}) ${error}`
        );
        return error;
      });
  };
}

export default new API();
<commit_msg>bad URL formation<commit_after>// This is the JS API to talk to api.participedia.xyz
import queryString from "query-string";
import authService from "./AuthService";
import store from "store";
import Raven from "raven-js";
import emojiStrip from "emoji-strip";

let APIURL = process.env.REACT_APP_API_URL; // eslint-disable-line no-undef

if (!APIURL) {
  console.error(
    "No API URL was found. REACT_APP_API_URL should be set in environment variables."
  );
}

const signedFetch = function(url, method, payload) {
  let opts = {
    method: method || "get",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json"
    }
  };
  let profile = store.get("profile");
  if (profile) {
    profile = JSON.parse(profile);
    if (authService.isAuthenticated()) {
      opts["headers"]["Authorization"] = "Bearer " + authService.getToken();
      opts["headers"]["X-Auth0-Name"] = emojiStrip(profile.name);
      opts["headers"]["X-Auth0-UserId"] = profile.user_id;
    }
    // console.log("doing signed call to", url);
  } else {
    // console.log("doing unsigned call to", url);
    delete opts["headers"]["authorization"];
  }
  if (payload) opts.body = JSON.stringify(payload);
  return fetch(url, opts);
};

class API {
  secureFetch = signedFetch;

  fetchGeoJSON = function(countryCode) {
    let url = APIURL + "/countries/" + countryCode + ".geo.json";
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  countsByCountry = function() {
    let url = APIURL + "/case/countsByCountry";
    return signedFetch(url)
      .then(response => response.json())
      .then(function(json) {
        return json.data.countryCounts;
      })
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  performSearch = function(queryArgs) {
    let querystring = queryString.stringify(queryArgs);
    let url = `${APIURL}/search?${querystring}`;
    // console.log("queryArgs", queryArgs);
    // console.log("DOING SEARCH", url);
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  searchMapTokens = function(selectedCategory, search) {
    let url;
    if (search) {
      url =
        APIURL +
        `/search${search}&selectedCategory=${selectedCategory}&resultType=map`;
    } else {
      url =
        APIURL + `/search?selectedCategory=${selectedCategory}&resultType=map`;
    }
    return signedFetch(url, "get")
      .then(response => response.json())
      .then(json => json.results)
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        throw error;
      });
  };

  fetchCaseById = function(caseId) {
    let url = APIURL + "/case/" + caseId;
    return signedFetch(url, "get")
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.error(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        throw error;
      });
  };

  saveNewThing = function(thingType, obj) {
    // console.log("saveNewThing", thingType, obj);
    if (
      thingType !== "case" &&
      thingType !== "method" &&
      thingType !== "organization"
    ) {
      let error = `Can only create cases, methods, and organizations. You sent: ${thingType}`;
      console.error(error);
      throw error;
    }
    let url = APIURL + "/" + thingType + "/new";
    // console.log("sending object", obj);
    return signedFetch(url, "POST", obj)
      .then(response => response.json())
      .then(function(response) {
        if (!response.OK) {
          console.log("Error doing saveNewThing's signedFetch: ", response);
          throw response.error;
        }
        // console.log("got response", response);
        return response.data;
      })
      .then(function(json) {
        // console.log("GOT JSON", json);
        obj.id = json.thingid;
        return obj;
      })
      .catch(function(error) {
        if (error) {
          let errorMsg = `There has been a problem with saving the ${thingType}: (${url}) ${JSON.stringify(
            error
          )}`;

          Raven.captureMessage(
            "error doing signedfetch " +
              JSON.stringify(obj) +
              " -> " +
              errorMsg,
            {
              level: "error"
            }
          );
          console.error(errorMsg);
          // throw error; XXX OIDP
        }
      });
  };

  saveThing = function(thingType, obj) {
    let url = APIURL + `/${thingType}/${obj.id}`;

    // console.log("saving", JSON.stringify(obj));
    delete obj.updated_date; // feels silly to have to do that.

    return signedFetch(url, "PUT", obj)
      .then(response => response.json())
      .then(function(response) {
        if (!response.OK) {
          console.log("Error doing saveThing's signedFetch: ", response);
          throw response.error;
        }
        // console.log("RESPONSE", response);
        return response.data;
      })
      .catch(function(error) {
        if (error) {
          Raven.captureMessage(
            "error in signed fetch sending: " + JSON.stringify(obj),
            {
              level: "error"
            }
          );

          console.error(
            `There has been a problem with saving the ${thingType}: (${url}) ${error}`
          );
          throw error;
        }
      });
  };

  fetchMethodById = function(methodId) {
    let url = APIURL + "/method/" + methodId;
    return signedFetch(url)
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  fetchOrgById = function(caseId) {
    let url = APIURL + "/organization/" + caseId;
    return signedFetch(url)
      .then(response => response.json())
      .then(json => json.data)
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  fetchNouns = function(noun) {
    let url = APIURL + "/search/getAllForType?objType=" + noun;
    return signedFetch(url)
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with your fetch operation: (${url}) ${error}`
        );
        return error;
      });
  };

  addBookmark = function(bookmarkType, thingid) {
    let url = APIURL + "/bookmark/add";
    return signedFetch(url, "POST", { bookmarkType, thingid })
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:addBookmark: (${url}) ${error}`
        );
        return error;
      });
  };
  removeBookmark = function(bookmarkType, thingid) {
    let url = APIURL + "/bookmark/delete";
    return signedFetch(url, "delete", {
      bookmarkType,
      thingid
    })
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:removeBookmark: (${url}) ${error}`
        );
        return error;
      });
  };
  fetchUser = function(userId) {
    let url = APIURL + `/user`;
    if (userId) {
      url = APIURL + `/user/${userId}`;
    }
    return signedFetch(url, "get")
      .then(response => response.json())
      .catch(function(error) {
        console.log(
          `There has been a problem with API:fetchUser: (${url}) ${error}`
        );

        return error;
      });
  };

  updateUserMetaData = function(userId, data) {
    const payload = { user_metadata: data };
    console.log(data, "data being sent");
    const url = `https://participedia.auth0.com/api/v2/users/${userId}`;
    return signedFetch(url, "PATCH", payload).then(response => {
      store.set("profile", JSON.stringify(response));
    });
  };

  saveUser = function(user) {
    let url = APIURL + `/user`;
    return signedFetch(url, "post", user)
      .then(response => response.json())
      .then(function(response) {
        if (response.status >= 400) {
          throw new Error(response.statusText);
        }
        return response;
      })
      .catch(function(error) {
        console.log(
          `There has been a problem with API:fetchUser: (${url}) ${error}`
        );
        return error;
      });
  };
}

export default new API();
<|endoftext|>


<commit_before>module.exports = {
  include: [
    'dist/*.js',
    'static/protocol.proto',
    'static/protocol.json',
    'static/*.bundle-*.js',
    'static/index.html',
    'LICENSE',
    'package.json',
    'README.md'
  ],
  exclude: [
  ],
  releaseRepository: 'https://github.com/plantain-00/log-tool-release.git'
}
<commit_msg>fix release config<commit_after>module.exports = {
  include: [
    'dist/*.js',
    'static/protocol.proto',
    'static/*-protocol.json',
    'static/*.bundle-*.js',
    'static/index.html',
    'LICENSE',
    'package.json',
    'README.md'
  ],
  exclude: [
  ],
  releaseRepository: 'https://github.com/plantain-00/log-tool-release.git'
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name            Politiken Freeloader
// @namespace       http://mathemaniac.org
// @icon            http://politiken.dk/favicon.ico
// @description     Removes paywall on Politiken.dk
// @include         http://politiken.dk/*
// @match           http://politiken.dk/*
// @version         3
// @require        http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js
// ==/UserScript==

// Inspired by Politiken Freeloader by Kristian Thy: http://userscripts-mirror.org/scripts/show/168318.html

jQuery('<style>#teaserwrapper, #meteroverlay { display: none !important; }</style>').appendTo('head');
<commit_msg>Lad være med at fjerne scrollbar på Politiken<commit_after>// ==UserScript==
// @name            Politiken Freeloader
// @namespace       http://mathemaniac.org
// @icon            http://politiken.dk/favicon.ico
// @description     Removes paywall on Politiken.dk
// @include         http://politiken.dk/*
// @match           http://politiken.dk/*
// @version         3.0.1
// @require        http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js
// ==/UserScript==

jQuery('<style>#teaserwrapper, #meteroverlay { display: none !important; } body { overflow: visible !important; }</style>').appendTo('head');
<|endoftext|>


<commit_before>import { swalPrefix, swalClasses } from './classes.js';

export var mediaqueryId = swalPrefix + 'mediaquery';

// Remember state in cases where opening and handling a modal will fiddle with it.
export var states = {
    previousWindowKeyDown: null,
    previousActiveElement: null
};

/*
 * Manipulate DOM
 */
export var elementByClass = function(className) {
  return document.querySelector('.' + className);
};

export var getModal = function() {
  return elementByClass(swalClasses.modal);
};

export var getOverlay = function() {
  return elementByClass(swalClasses.overlay);
};

export var getConfirmButton = function() {
  return elementByClass(swalClasses.confirm);
};

export var getCancelButton = function() {
  return elementByClass(swalClasses.cancel);
};

export var getCloseButton = function() {
  return elementByClass(swalClasses.close);
};

export var hasClass = function(elem, className) {
  return elem.classList.contains(className);
};

export var focusInput = function(input) {
  input.focus();

  // http://stackoverflow.com/a/2345915/1331425
  var val = input.value;
  input.value = '';
  input.value = val;
};

export var addClass = function(elem, className) {
  if (!elem || !className) {
    return;
  }
  var classes = className.split(/\s+/);
  for (var i in classes) {
    elem.classList.add(classes[i]);
  }
};

export var removeClass = function(elem, className) {
  if (!elem || !className) {
    return;
  }
  var classes = className.split(/\s+/);
  for (var i in classes) {
    elem.classList.remove(classes[i]);
  }
};

export var getChildByClass = function(elem, className) {
  for (var i = 0; i < elem.childNodes.length; i++) {
    if (hasClass(elem.childNodes[i], className)) {
      return elem.childNodes[i];
    }
  }
};

export var _show = function(elem) {
  elem.style.opacity = '';
  elem.style.display = 'block';
};

export var show = function(elems) {
  if (elems && !elems.length) {
    return _show(elems);
  }
  for (var i = 0; i < elems.length; ++i) {
    _show(elems[i]);
  }
};

export var _hide = function(elem) {
  elem.style.opacity = '';
  elem.style.display = 'none';
};

export var hide = function(elems) {
  if (elems && !elems.length) {
    return _hide(elems);
  }
  for (var i = 0; i < elems.length; ++i) {
    _hide(elems[i]);
  }
};

export var removeStyleProperty = function(elem, property) {
  if (elem.style.removeProperty) {
    elem.style.removeProperty(property);
  } else {
    elem.style.removeAttribute(property);
  }
};

export var getTopMargin = function(elem) {
  var elemDisplay = elem.style.display;
  elem.style.left = '-9999px';
  elem.style.display = 'block';

  var height = elem.clientHeight;

  elem.style.left = '';
  elem.style.display = elemDisplay;
  return ('-' + parseInt(height / 2, 10) + 'px');
};

export var fadeIn = function(elem, interval) {
  if (+elem.style.opacity < 1) {
    interval = interval || 16;
    elem.style.opacity = 0;
    elem.style.display = 'block';
    var last = +new Date();
    var tick = function() {
      var newOpacity = +elem.style.opacity + (new Date() - last) / 100;
      elem.style.opacity = (newOpacity > 1) ? 1 : newOpacity;
      last = +new Date();

      if (+elem.style.opacity < 1) {
        setTimeout(tick, interval);
      }
    };
    tick();
  }
};

export var fadeOut = function(elem, interval) {
  if (+elem.style.opacity > 0) {
    interval = interval || 16;
    var opacity = elem.style.opacity;
    var last = +new Date();
    var tick = function() {
      var change = new Date() - last;
      var newOpacity = +elem.style.opacity - change / (opacity * 100);
      elem.style.opacity = newOpacity;
      last = +new Date();

      if (+elem.style.opacity > 0) {
        setTimeout(tick, interval);
      } else {
        _hide(elem);
      }
    };
    tick();
  }
};

export var fireClick = function(node) {
  // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
  // Then fixed for today's Chrome browser.
  if (typeof MouseEvent === 'function') {
    // Up-to-date approach
    var mevt = new MouseEvent('click', {
      view: window,
      bubbles: false,
      cancelable: true
    });
    node.dispatchEvent(mevt);
  } else if (document.createEvent) {
    // Fallback
    var evt = document.createEvent('MouseEvents');
    evt.initEvent('click', false, false);
    node.dispatchEvent(evt);
  } else if (document.createEventObject) {
    node.fireEvent('onclick');
  } else if (typeof node.onclick === 'function') {
    node.onclick();
  }
};

export var stopEventPropagation = function(e) {
  // In particular, make sure the space bar doesn't scroll the main window.
  if (typeof e.stopPropagation === 'function') {
    e.stopPropagation();
    e.preventDefault();
  } else if (window.event && window.event.hasOwnProperty('cancelBubble')) {
    window.event.cancelBubble = true;
  }
};

export var animationEndEvent = (function() {
  var testEl = document.createElement('div'),
    transEndEventNames = {
      'WebkitAnimation': 'webkitAnimationEnd',
      'MozAnimation': 'animationend',
      'OAnimation': 'oAnimationEnd oanimationend',
      'msAnimation': 'MSAnimationEnd',
      'animation': 'animationend'
    };
  for (var i in transEndEventNames) {
    if (transEndEventNames.hasOwnProperty(i) &&
      testEl.style[i] !== undefined) {
      return transEndEventNames[i];
    }
  }

  return false;
})();

// Reset the page to its previous state
export var resetPrevState = function() {
  var modal = getModal();
  window.onkeydown = states.previousWindowKeyDown;
  if (states.previousActiveElement) {
    states.previousActiveElement.focus();
  }
  clearTimeout(modal.timeout);

  // Remove dynamically created media query
  var head = document.getElementsByTagName('head')[0];
  var mediaquery = document.getElementById(mediaqueryId);
  if (mediaquery) {
    head.removeChild(mediaquery);
  }
};
<commit_msg>Iterating over an array using for-in will also iterate over any prototype methods, this was causing an InvalidCharacterError. (#223)<commit_after>import { swalPrefix, swalClasses } from './classes.js';

export var mediaqueryId = swalPrefix + 'mediaquery';

// Remember state in cases where opening and handling a modal will fiddle with it.
export var states = {
    previousWindowKeyDown: null,
    previousActiveElement: null
};

/*
 * Manipulate DOM
 */
export var elementByClass = function(className) {
  return document.querySelector('.' + className);
};

export var getModal = function() {
  return elementByClass(swalClasses.modal);
};

export var getOverlay = function() {
  return elementByClass(swalClasses.overlay);
};

export var getConfirmButton = function() {
  return elementByClass(swalClasses.confirm);
};

export var getCancelButton = function() {
  return elementByClass(swalClasses.cancel);
};

export var getCloseButton = function() {
  return elementByClass(swalClasses.close);
};

export var hasClass = function(elem, className) {
  return elem.classList.contains(className);
};

export var focusInput = function(input) {
  input.focus();

  // http://stackoverflow.com/a/2345915/1331425
  var val = input.value;
  input.value = '';
  input.value = val;
};

export var addClass = function(elem, className) {
  if (!elem || !className) {
    return;
  }
  var classes = className.split(/\s+/);
  classes.forEach(function (className) {
    elem.classList.add(className)
  });
};

export var removeClass = function(elem, className) {
  if (!elem || !className) {
    return;
  }
  var classes = className.split(/\s+/);
  classes.forEach(function (className) {
    elem.classList.remove(className);
  });
};

export var getChildByClass = function(elem, className) {
  for (var i = 0; i < elem.childNodes.length; i++) {
    if (hasClass(elem.childNodes[i], className)) {
      return elem.childNodes[i];
    }
  }
};

export var _show = function(elem) {
  elem.style.opacity = '';
  elem.style.display = 'block';
};

export var show = function(elems) {
  if (elems && !elems.length) {
    return _show(elems);
  }
  for (var i = 0; i < elems.length; ++i) {
    _show(elems[i]);
  }
};

export var _hide = function(elem) {
  elem.style.opacity = '';
  elem.style.display = 'none';
};

export var hide = function(elems) {
  if (elems && !elems.length) {
    return _hide(elems);
  }
  for (var i = 0; i < elems.length; ++i) {
    _hide(elems[i]);
  }
};

export var removeStyleProperty = function(elem, property) {
  if (elem.style.removeProperty) {
    elem.style.removeProperty(property);
  } else {
    elem.style.removeAttribute(property);
  }
};

export var getTopMargin = function(elem) {
  var elemDisplay = elem.style.display;
  elem.style.left = '-9999px';
  elem.style.display = 'block';

  var height = elem.clientHeight;

  elem.style.left = '';
  elem.style.display = elemDisplay;
  return ('-' + parseInt(height / 2, 10) + 'px');
};

export var fadeIn = function(elem, interval) {
  if (+elem.style.opacity < 1) {
    interval = interval || 16;
    elem.style.opacity = 0;
    elem.style.display = 'block';
    var last = +new Date();
    var tick = function() {
      var newOpacity = +elem.style.opacity + (new Date() - last) / 100;
      elem.style.opacity = (newOpacity > 1) ? 1 : newOpacity;
      last = +new Date();

      if (+elem.style.opacity < 1) {
        setTimeout(tick, interval);
      }
    };
    tick();
  }
};

export var fadeOut = function(elem, interval) {
  if (+elem.style.opacity > 0) {
    interval = interval || 16;
    var opacity = elem.style.opacity;
    var last = +new Date();
    var tick = function() {
      var change = new Date() - last;
      var newOpacity = +elem.style.opacity - change / (opacity * 100);
      elem.style.opacity = newOpacity;
      last = +new Date();

      if (+elem.style.opacity > 0) {
        setTimeout(tick, interval);
      } else {
        _hide(elem);
      }
    };
    tick();
  }
};

export var fireClick = function(node) {
  // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
  // Then fixed for today's Chrome browser.
  if (typeof MouseEvent === 'function') {
    // Up-to-date approach
    var mevt = new MouseEvent('click', {
      view: window,
      bubbles: false,
      cancelable: true
    });
    node.dispatchEvent(mevt);
  } else if (document.createEvent) {
    // Fallback
    var evt = document.createEvent('MouseEvents');
    evt.initEvent('click', false, false);
    node.dispatchEvent(evt);
  } else if (document.createEventObject) {
    node.fireEvent('onclick');
  } else if (typeof node.onclick === 'function') {
    node.onclick();
  }
};

export var stopEventPropagation = function(e) {
  // In particular, make sure the space bar doesn't scroll the main window.
  if (typeof e.stopPropagation === 'function') {
    e.stopPropagation();
    e.preventDefault();
  } else if (window.event && window.event.hasOwnProperty('cancelBubble')) {
    window.event.cancelBubble = true;
  }
};

export var animationEndEvent = (function() {
  var testEl = document.createElement('div'),
    transEndEventNames = {
      'WebkitAnimation': 'webkitAnimationEnd',
      'MozAnimation': 'animationend',
      'OAnimation': 'oAnimationEnd oanimationend',
      'msAnimation': 'MSAnimationEnd',
      'animation': 'animationend'
    };
  for (var i in transEndEventNames) {
    if (transEndEventNames.hasOwnProperty(i) &&
      testEl.style[i] !== undefined) {
      return transEndEventNames[i];
    }
  }

  return false;
})();

// Reset the page to its previous state
export var resetPrevState = function() {
  var modal = getModal();
  window.onkeydown = states.previousWindowKeyDown;
  if (states.previousActiveElement) {
    states.previousActiveElement.focus();
  }
  clearTimeout(modal.timeout);

  // Remove dynamically created media query
  var head = document.getElementsByTagName('head')[0];
  var mediaquery = document.getElementById(mediaqueryId);
  if (mediaquery) {
    head.removeChild(mediaquery);
  }
};
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2013, Olivier Biot
 * http://www.melonjs.org
 *
 */
(function () {
    /**
     * A pool of Object entity

     * This object is used for object pooling - a technique that might speed up your game
     * if used properly.

     * If some of your classes will be instantiated and removed a lot at a time, it is a
     * good idea to add the class to this entity pool. A separate pool for that class
     * will be created, which will reuse objects of the class. That way they won't be instantiated
     * each time you need a new one (slowing your game), but stored into that pool and taking one
     * already instantiated when you need it.

     * This object is also used by the engine to instantiate objects defined in the map,
     * which means, that on level loading the engine will try to instantiate every object
     * found in the map, based on the user defined name in each Object Properties

     *

     * @namespace me.pool
     * @memberOf me
     */
    me.pool = (function () {
        // hold public stuff in our singleton
        var api = {};

        var entityClass = {};

        /*
         * PUBLIC STUFF
         */

        /**
         * Constructor
         * @ignore
         */
        api.init = function () {
            // add default entity object
            api.register("me.Entity", me.Entity);
            api.register("me.CollectableEntity", me.CollectableEntity);
            api.register("me.LevelEntity", me.LevelEntity);
            api.register("TileObject", me.Sprite);
            api.register("me.Tween", me.Tween, true);
            api.register("me.Color", me.Color, true);
            api.register("me.Particle", me.Particle, true);
            api.register("me.Vector2d", me.Vector2d, true);
        };

        /**
         * register an object to the pool.

         * Pooling must be set to true if more than one such objects will be created.

         * (note) If pooling is enabled, you shouldn't instantiate objects with `new`.
         * See examples in {@link me.pool#pull}
         * @name register
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} className as defined in the Name field of the Object Properties (in Tiled)
         * @param {Object} class corresponding Class to be instantiated
         * @param {Boolean} [objectPooling=false] enables object pooling for the specified class
         * - speeds up the game by reusing existing objects
         * @example
         * // add our users defined entities in the entity pool
         * me.pool.register("playerspawnpoint", PlayerEntity);
         * me.pool.register("cherryentity", CherryEntity, true);
         * me.pool.register("heartentity", HeartEntity, true);
         * me.pool.register("starentity", StarEntity, true);
         */
        api.register = function (className, entityObj, pooling) {
            entityClass[className] = {
                "class" : entityObj,
                "pool" : (pooling ? [] : undefined)
            };
        };

        /**
         * Pull a new instance of the requested object (if added into the object pool)
         * @name pull
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} className as used in {@link me.pool#register}
         * @param {} [arguments...] arguments to be passed when instantiating/reinitializing the object
         * @example
         * me.pool.register("player", PlayerEntity);
         * var player = me.pool.pull("player");
         * @example
         * me.pool.register("bullet", BulletEntity, true);
         * me.pool.register("enemy", EnemyEntity, true);
         * // ...
         * // when we need to manually create a new bullet:
         * var bullet = me.pool.pull("bullet", x, y, direction);
         * // ...
         * // params aren't a fixed number
         * // when we need new enemy we can add more params, that the object construct requires:
         * var enemy = me.pool.pull("enemy", x, y, direction, speed, power, life);
         * // ...
         * // when we want to destroy existing object, the remove
         * // function will ensure the object can then be reallocated later
         * me.game.world.removeChild(enemy);
         * me.game.world.removeChild(bullet);
         */
        api.pull = function (name) {
            var args = new Array(arguments.length);
            for (var i = 0; i < arguments.length; i++) {
                args[i] = arguments[i];
            }
            var entity = entityClass[name];
            if (entity) {
                var proto = entity["class"],
                    pool = entity.pool,
                    obj;

                if (pool && ((obj = pool.pop()))) {
                    args.shift();
                    // call the object onResetEvent function if defined
                    if (typeof(obj.onResetEvent) === "function") {
                        obj.onResetEvent.apply(obj, args);
                    }
                    else {
                        obj.init.apply(obj, args);
                    }
                }
                else {
                    args[0] = proto;
                    obj = new (proto.bind.apply(proto, args))();
                    if (pool) {
                        obj.className = name;
                    }
                }
                return obj;
            }

            throw new me.Error("Cannot instantiate entity of type '" + name + "'");
        };

        /**
         * purge the entity pool from any inactive object

         * Object pooling must be enabled for this function to work

         * note: this will trigger the garbage collector
         * @name purge
         * @memberOf me.pool
         * @public
         * @function
         */
        api.purge = function () {
            for (var className in entityClass) {
                if (entityClass[className]) {
                    entityClass[className].pool = [];
                }
            }
        };

        /**
         * Push back an object instance into the entity pool

         * Object pooling for the object class must be enabled,
         * and object must have been instantiated using {@link me.pool#pull},
         * otherwise this function won't work
         * @name push
         * @memberOf me.pool
         * @public
         * @function
         * @param {Object} instance to be recycled
         */
        api.push = function (obj) {
            var name = obj.className;
            if (typeof(name) === "undefined" || !entityClass[name]) {
                // object is not registered, don't do anything
                return;
            }
            // store back the object instance for later recycling
            entityClass[name].pool.push(obj);
        };

        /**
         * Check if an object with the provided name is registered
         * @name exists
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} name of the registered object
         */
        api.exists = function (name) {
            return name in entityClass;
        };

        // return our object
        return api;
    })();
})();
<commit_msg>Update pooling.js<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2013, Olivier Biot
 * http://www.melonjs.org
 *
 */
(function () {
    /**
     * A pool of Object entity

     * This object is used for object pooling - a technique that might speed up your game
     * if used properly.

     * If some of your classes will be instantiated and removed a lot at a time, it is a
     * good idea to add the class to this entity pool. A separate pool for that class
     * will be created, which will reuse objects of the class. That way they won't be instantiated
     * each time you need a new one (slowing your game), but stored into that pool and taking one
     * already instantiated when you need it.

     * This object is also used by the engine to instantiate objects defined in the map,
     * which means, that on level loading the engine will try to instantiate every object
     * found in the map, based on the user defined name in each Object Properties

     *

     * @namespace me.pool
     * @memberOf me
     */
    me.pool = (function () {
        // hold public stuff in our singleton
        var api = {};

        var entityClass = {};

        /*
         * PUBLIC STUFF
         */

        /**
         * Constructor
         * @ignore
         */
        api.init = function () {
            // add default entity object
            api.register("me.Entity", me.Entity);
            api.register("me.CollectableEntity", me.CollectableEntity);
            api.register("me.LevelEntity", me.LevelEntity);
            api.register("TileObject", me.Sprite);
            api.register("me.Tween", me.Tween, true);
            api.register("me.Color", me.Color, true);
            api.register("me.Particle", me.Particle, true);
            api.register("me.Vector2d", me.Vector2d, true);
        };

        /**
         * register an object to the pool.

         * Pooling must be set to true if more than one such objects will be created.

         * (note) If pooling is enabled, you shouldn't instantiate objects with `new`.
         * See examples in {@link me.pool#pull}
         * @name register
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} className as defined in the Name field of the Object Properties (in Tiled)
         * @param {Object} class corresponding Class to be instantiated
         * @param {Boolean} [objectPooling=false] enables object pooling for the specified class
         * - speeds up the game by reusing existing objects
         * @example
         * // add our users defined entities in the entity pool
         * me.pool.register("playerspawnpoint", PlayerEntity);
         * me.pool.register("cherryentity", CherryEntity, true);
         * me.pool.register("heartentity", HeartEntity, true);
         * me.pool.register("starentity", StarEntity, true);
         */
        api.register = function (className, entityObj, pooling) {
            entityClass[className] = {
                "class" : entityObj,
                "pool" : (pooling ? [] : undefined)
            };
        };

        /**
         * Pull a new instance of the requested object (if added into the object pool)
         * @name pull
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} className as used in {@link me.pool#register}
         * @param {} [arguments...] arguments to be passed when instantiating/reinitializing the object
         * @return {Object} the instance of the requested object
         * @example
         * me.pool.register("player", PlayerEntity);
         * var player = me.pool.pull("player");
         * @example
         * me.pool.register("bullet", BulletEntity, true);
         * me.pool.register("enemy", EnemyEntity, true);
         * // ...
         * // when we need to manually create a new bullet:
         * var bullet = me.pool.pull("bullet", x, y, direction);
         * // ...
         * // params aren't a fixed number
         * // when we need new enemy we can add more params, that the object construct requires:
         * var enemy = me.pool.pull("enemy", x, y, direction, speed, power, life);
         * // ...
         * // when we want to destroy existing object, the remove
         * // function will ensure the object can then be reallocated later
         * me.game.world.removeChild(enemy);
         * me.game.world.removeChild(bullet);
         */
        api.pull = function (name) {
            var args = new Array(arguments.length);
            for (var i = 0; i < arguments.length; i++) {
                args[i] = arguments[i];
            }
            var entity = entityClass[name];
            if (entity) {
                var proto = entity["class"],
                    pool = entity.pool,
                    obj;

                if (pool && ((obj = pool.pop()))) {
                    args.shift();
                    // call the object onResetEvent function if defined
                    if (typeof(obj.onResetEvent) === "function") {
                        obj.onResetEvent.apply(obj, args);
                    }
                    else {
                        obj.init.apply(obj, args);
                    }
                }
                else {
                    args[0] = proto;
                    obj = new (proto.bind.apply(proto, args))();
                    if (pool) {
                        obj.className = name;
                    }
                }
                return obj;
            }

            throw new me.Error("Cannot instantiate entity of type '" + name + "'");
        };

        /**
         * purge the entity pool from any inactive object

         * Object pooling must be enabled for this function to work

         * note: this will trigger the garbage collector
         * @name purge
         * @memberOf me.pool
         * @public
         * @function
         */
        api.purge = function () {
            for (var className in entityClass) {
                if (entityClass[className]) {
                    entityClass[className].pool = [];
                }
            }
        };

        /**
         * Push back an object instance into the entity pool

         * Object pooling for the object class must be enabled,
         * and object must have been instantiated using {@link me.pool#pull},
         * otherwise this function won't work
         * @name push
         * @memberOf me.pool
         * @public
         * @function
         * @param {Object} instance to be recycled
         */
        api.push = function (obj) {
            var name = obj.className;
            if (typeof(name) === "undefined" || !entityClass[name]) {
                // object is not registered, don't do anything
                return;
            }
            // store back the object instance for later recycling
            entityClass[name].pool.push(obj);
        };

        /**
         * Check if an object with the provided name is registered
         * @name exists
         * @memberOf me.pool
         * @public
         * @function
         * @param {String} name of the registered object
         * @return {Boolean} true if the classname is registered
         */
        api.exists = function (name) {
            return name in entityClass;
        };

        // return our object
        return api;
    })();
})();
<|endoftext|>


<commit_before>dojo.provide("dojox.charting.plot2d.OHLC");

dojo.require("dojox.charting.plot2d.common");
dojo.require("dojox.charting.plot2d.Base");

dojo.require("dojox.lang.utils");
dojo.require("dojox.lang.functional");
dojo.require("dojox.lang.functional.reversed");

(function(){
	var df = dojox.lang.functional, du = dojox.lang.utils,
		dc = dojox.charting.plot2d.common,
		purgeGroup = df.lambda("item.purgeGroup()");

	//	Candlesticks are based on the Bars plot type; we expect the following passed
	//	as values in a series:
	//	{ x?, open, close, high, low }
	//	if x is not provided, the array index is used.
	//	failing to provide the OHLC values will throw an error.
	dojo.declare("dojox.charting.plot2d.OHLC", dojox.charting.plot2d.Base, {
		defaultParams: {
			hAxis: "x",		// use a horizontal axis named "x"
			vAxis: "y",		// use a vertical axis named "y"
			gap:	2,		// gap between columns in pixels
			shadows: null	// draw shadows
		},
		optionalParams: {
			minBarSize: 1,	// minimal bar size in pixels
			maxBarSize: 1	// maximal bar size in pixels
		},

		constructor: function(chart, kwArgs){
			this.opt = dojo.clone(this.defaultParams);
			du.updateWithObject(this.opt, kwArgs);
			du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
			this.series = [];
			this.hAxis = this.opt.hAxis;
			this.vAxis = this.opt.vAxis;
		},

		collectStats: function(series){
			//	we have to roll our own, since we need to use all four passed
			//	values to figure out our stats, and common only assumes x and y.
			var stats = dojo.clone(dc.defaultStats);
			for(var i=0; i<series.length; i++){
				var run = series[i];
				if(!run.data.length){ continue; }
				var old_vmin = stats.vmin, old_vmax = stats.vmax;
				if(!("ymin" in run) || !("ymax" in run)){
					dojo.forEach(run.data, function(val, idx){
						var x = val.x || idx + 1;
						stats.hmin = Math.min(stats.hmin, x);
						stats.hmax = Math.max(stats.hmax, x);
						stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low);
						stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low);
					});
				}
				if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); }
				if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); }
			}
			return stats;
		},

		calculateAxes: function(dim){
			var stats = this.collectStats(this.series), t;
			stats.hmin -= 0.5;
			stats.hmax += 0.5;
			this._calc(dim, stats);
			return this;
		},

		render: function(dim, offsets){
			this.dirty = this.isDirty();
			if(this.dirty){
				dojo.forEach(this.series, purgeGroup);
				this.cleanGroup();
				var s = this.group;
				df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
			}
			var t = this.chart.theme, color, stroke, fill, f, gap, width,
				ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
				vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler),
				baseline = Math.max(0, this._vScaler.bounds.lower),
				baselineHeight = vt(baseline),
				events = this.events();
			f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt);
			gap = f.gap;
			width = f.size;
			this.resetEvents();
			for(var i = this.series.length - 1; i >= 0; --i){
				var run = this.series[i];
				if(!this.dirty && !run.dirty){ continue; }
				run.cleanGroup();
				var s = run.group;
				if(!run.fill || !run.stroke){
					// need autogenerated color
					color = run.dyn.color = new dojo.Color(t.next("color"));
				}
				//	note that fill does not get used with this
				stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color);
				fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color);

				for(var j = 0; j < run.data.length; ++j){
					var v = run.data[j];

					//	calculate the points we need for OHLC
					var x = ht(v.x || (j+0.5)) + offsets.l + gap,
						y = dim.height - offsets.b,
						open = vt(v.open),
						close = vt(v.close),
						high = vt(v.high),
						low = vt(v.low);
					if(low > high){
						var tmp = high;
						high = low;
						low = tmp;
					}

					if(width >= 1){
						//	draw the line and rect, set up as a group and pass that to the events.
						var hl = { x1: width/2, x2: width/2, y1: y - high, y2: y - low },
							op = { x1: 0, x2: width/2, y1: y-open, y2: y-open},
							cl = { x1: width/2, x2: width, y1: y-close, y2: y-close };
						shape = s.createGroup();
						shape.setTransform({dx: x, dy: 0 });
						shape.createLine(hl).setStroke(stroke);
						shape.createLine(op).setStroke(stroke);
						shape.createLine(cl).setStroke(stroke);

						//	TODO: double check this.
						run.dyn.fill   = fill;
						run.dyn.stroke = stroke;
						if(events){
							var o = {
								element: "ohlc",
								index:   j,
								run:     run,
								plot:    this,
								hAxis:   this.hAxis || null,
								vAxis:   this.vAxis || null,
								shape:   shape,
								x:       v,
								y:       j + 1.5
							};
							this._connectEvents(shape, o);
						}
					}
				}
				run.dirty = false;
			}
			this.dirty = false;
			return this;
		}
	});
})();
<commit_msg>Getting Magnify and Tooltip working with OHLC.  !strict.<commit_after>dojo.provide("dojox.charting.plot2d.OHLC");

dojo.require("dojox.charting.plot2d.common");
dojo.require("dojox.charting.plot2d.Base");

dojo.require("dojox.lang.utils");
dojo.require("dojox.lang.functional");
dojo.require("dojox.lang.functional.reversed");

(function(){
	var df = dojox.lang.functional, du = dojox.lang.utils,
		dc = dojox.charting.plot2d.common,
		purgeGroup = df.lambda("item.purgeGroup()");

	//	Candlesticks are based on the Bars plot type; we expect the following passed
	//	as values in a series:
	//	{ x?, open, close, high, low }
	//	if x is not provided, the array index is used.
	//	failing to provide the OHLC values will throw an error.
	dojo.declare("dojox.charting.plot2d.OHLC", dojox.charting.plot2d.Base, {
		defaultParams: {
			hAxis: "x",		// use a horizontal axis named "x"
			vAxis: "y",		// use a vertical axis named "y"
			gap:	2,		// gap between columns in pixels
			shadows: null	// draw shadows
		},
		optionalParams: {
			minBarSize: 1,	// minimal bar size in pixels
			maxBarSize: 1	// maximal bar size in pixels
		},

		constructor: function(chart, kwArgs){
			this.opt = dojo.clone(this.defaultParams);
			du.updateWithObject(this.opt, kwArgs);
			du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
			this.series = [];
			this.hAxis = this.opt.hAxis;
			this.vAxis = this.opt.vAxis;
		},

		collectStats: function(series){
			//	we have to roll our own, since we need to use all four passed
			//	values to figure out our stats, and common only assumes x and y.
			var stats = dojo.clone(dc.defaultStats);
			for(var i=0; i<series.length; i++){
				var run = series[i];
				if(!run.data.length){ continue; }
				var old_vmin = stats.vmin, old_vmax = stats.vmax;
				if(!("ymin" in run) || !("ymax" in run)){
					dojo.forEach(run.data, function(val, idx){
						var x = val.x || idx + 1;
						stats.hmin = Math.min(stats.hmin, x);
						stats.hmax = Math.max(stats.hmax, x);
						stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low);
						stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low);
					});
				}
				if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); }
				if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); }
			}
			return stats;
		},

		calculateAxes: function(dim){
			var stats = this.collectStats(this.series), t;
			stats.hmin -= 0.5;
			stats.hmax += 0.5;
			this._calc(dim, stats);
			return this;
		},

		render: function(dim, offsets){
			this.dirty = this.isDirty();
			if(this.dirty){
				dojo.forEach(this.series, purgeGroup);
				this.cleanGroup();
				var s = this.group;
				df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
			}
			var t = this.chart.theme, color, stroke, fill, f, gap, width,
				ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
				vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler),
				baseline = Math.max(0, this._vScaler.bounds.lower),
				baselineHeight = vt(baseline),
				events = this.events();
			f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt);
			gap = f.gap;
			width = f.size;
			this.resetEvents();
			for(var i = this.series.length - 1; i >= 0; --i){
				var run = this.series[i];
				if(!this.dirty && !run.dirty){ continue; }
				run.cleanGroup();
				var s = run.group;
				if(!run.fill || !run.stroke){
					// need autogenerated color
					color = run.dyn.color = new dojo.Color(t.next("color"));
				}
				//	note that fill does not get used with this
				stroke = run.stroke ? run.stroke : dc.augmentStroke(t.series.stroke, color);
				fill = run.fill ? run.fill : dc.augmentFill(t.series.fill, color);

				for(var j = 0; j < run.data.length; ++j){
					var v = run.data[j];

					//	calculate the points we need for OHLC
					var x = ht(v.x || (j+0.5)) + offsets.l + gap,
						y = dim.height - offsets.b,
						open = vt(v.open),
						close = vt(v.close),
						high = vt(v.high),
						low = vt(v.low);
					if(low > high){
						var tmp = high;
						high = low;
						low = tmp;
					}

					if(width >= 1){
						//	draw the line and rect, set up as a group and pass that to the events.
						var hl = { x1: width/2, x2: width/2, y1: y - high, y2: y - low },
							op = { x1: 0, x2: width/2, y1: y-open, y2: y-open},
							cl = { x1: width/2, x2: width, y1: y-close, y2: y-close };
						shape = s.createGroup();
						shape.setTransform({dx: x, dy: 0 });
						var inner = shape.createGroup();
						inner.createLine(hl).setStroke(stroke);
						inner.createLine(op).setStroke(stroke);
						inner.createLine(cl).setStroke(stroke);

						//	TODO: double check this.
						run.dyn.fill   = fill;
						run.dyn.stroke = stroke;
						if(events){
							var o = {
								element: "candlestick",
								index:   j,
								run:     run,
								plot:    this,
								hAxis:   this.hAxis || null,
								vAxis:   this.vAxis || null,
								shape:	 inner,
								x:       x,
								y:       y-Math.max(open, close),
								cx:		 width/2,
								cy:		 (y-Math.max(open, close)) + (Math.max(open > close ? open-close : close-open, 1)/2),
								width:	 width,
								height:  Math.max(open > close ? open-close : close-open, 1),
								data:	 v
							};
							this._connectEvents(shape, o);
						}
					}
				}
				run.dirty = false;
			}
			this.dirty = false;
			return this;
		}
	});
})();
<|endoftext|>


<commit_before>angular.module('app.auth', [])

.controller('AuthController', ['$scope', '$http', function($scope, $http){
  $scope.test = function(){
    console.log('Auth controller is working');
  };
  $scope.test();

  $scope.attemptLogin = function () {
    $http.get('/api/login?username=' + $scope.attemptedUsername + '&password=' + $scope.attemptedPassword )
    .then(
      //success callback
      function(res){
        console.log('sent login credentials');
      },
      //error callback
      function(err, res){
        console.log('res::::', res);
        console.log('error sending credentials', err);
        if(res.statusCode === 401){
          console.log('unauthorized credentials');
        }
      }
      );
  };
}]);<commit_msg>handling response if 401<commit_after>angular.module('app.auth', [])

.controller('AuthController', ['$scope', '$http', function($scope, $http){
  $scope.test = function(){
    console.log('Auth controller is working');
  };
  $scope.test();

  $scope.attemptLogin = function () {
    $http.get('/api/login?username=' + $scope.attemptedUsername + '&password=' + $scope.attemptedPassword )
    .then(
      //success callback
      function(res){
        console.log('sent login credentials');
        console.log('res.data', res.data);
      },
      //error callback
      function(res){
        console.log('res:', res);
        if(res.status === 401){
          console.log('unauthorized credentials');
        }
      }
      );
  };
}]);<|endoftext|>


<commit_before>/*
* Hangman chat plugin
* By bumbadadabum and Zarel. Art by crobat.
*/

'use strict';

const maxMistakes = 6;

class Hangman extends Rooms.RoomGame {
	constructor(room, user, word, hint) {
		super(room);

		if (room.gameNumber) {
			room.gameNumber++;
		} else {
			room.gameNumber = 1;
		}

		this.gameid = 'hangman';
		this.title = 'Hangman';
		this.creator = user.userid;
		this.word = word;
		this.hint = hint;
		this.incorrectGuesses = 0;

		this.guesses = [];
		this.letterGuesses = [];
		this.lastGuesser = '';
		this.wordSoFar = [];

		for (let i = 0; i < word.length; i++) {
			if (/[a-zA-Z]/.test(word[i])) {
				this.wordSoFar.push('_');
			} else {
				this.wordSoFar.push(word[i]);
			}
		}
	}

	guess(word, user) {
		if (user.userid === this.room.game.creator) return user.sendTo(this.room, "You can't guess in your own hangman game.");

		let sanitized = word.replace(/[^A-Za-z ]/g, '');
		let normalized = toId(sanitized);
		if (normalized.length < 1) return user.sendTo(this.room, "Guess too short.");
		if (sanitized.length > 30) return user.sendTo(this.room, "Guess too long.");

		for (let i = 0; i < this.guesses.length; i++) {
			if (normalized === toId(this.guesses[i])) return user.sendTo(this.room, "Your guess has already been guessed.");
		}

		if (sanitized.length > 1) {
			if (!this.guessWord(sanitized, user.name)) {
				user.sendTo(this.room, "Invalid guess");
			} else {
				this.room.send(user.name + " guessed \"" + sanitized + "\"!");
			}
		} else {
			if (!this.guessLetter(sanitized, user.name)) {
				user.sendTo(this.room, "Invalid guess");
			}
		}
	}

	guessLetter(letter, guesser) {
		letter = letter.toUpperCase();
		if (this.guesses.includes(letter)) return false;
		if (this.word.toUpperCase().includes(letter)) {
			for (let i = 0; i < this.word.length; i++) {
				if (this.word[i].toUpperCase() === letter) {
					this.wordSoFar[i] = this.word[i];
				}
			}

			if (!this.wordSoFar.includes('_')) {
				this.incorrectGuesses = -1;
				this.guesses.push(letter);
				this.letterGuesses.push(letter + '1');
				this.lastGuesser = guesser;
				this.finish();
				return true;
			}
			this.letterGuesses.push(letter + '1');
		} else {
			this.incorrectGuesses++;
			this.letterGuesses.push(letter + '0');
		}

		this.guesses.push(letter);
		this.lastGuesser = guesser;
		this.update();
		return true;
	}

	guessWord(word, guesser) {
		let ourWord = toId(this.word);
		let guessedWord = toId(word);
		if (ourWord === guessedWord) {
			for (let i = 0; i < this.wordSoFar.length; i++) {
				if (this.wordSoFar[i] === '_') {
					this.wordSoFar[i] = this.word[i];
				}
			}
			this.incorrectGuesses = -1;
			this.guesses.push(word);
			this.lastGuesser = guesser;
			this.finish();
			return true;
		} else if (ourWord.length === guessedWord.length) {
			this.incorrectGuesses++;
			this.guesses.push(word);
			this.lastGuesser = guesser;
			this.update();
			return true;
		}
		return false;
	}

	hangingMan() {
		return '';
	}

	generateWindow() {
		let result = 0;

		if (this.incorrectGuesses === maxMistakes) {
			result = 1;
		} else if (!this.wordSoFar.includes('_')) {
			result = 2;
		}

		let output = '
';
		output += '
' + (result === 1 ? 'Too bad! The mon has been hanged.' : (result === 2 ? 'The word has been guessed. Congratulations!' : 'Hangman')) + '
';
		output += '

' + this.hangingMan() + '	';

		let wordString = this.wordSoFar.join('');
		if (result === 1) {
			let word = this.word;
			wordString = wordString.replace(/_+/g, (match, offset) =>
				'' + word.substr(offset, match.length) + ''
			);
		}

		if (this.hint) output += '
(Hint: ' + Chat.escapeHTML(this.hint) + ')
';
		output += '
' + wordString + '
';
		if (this.guesses.length) {
			if (this.letterGuesses.length) {
				output += 'Letters: ' + this.letterGuesses.map(g =>
					'' + Chat.escapeHTML(g[0]) + ''
				).join(', ');
			}
			if (result === 2) {
				output += '
Winner: ' + Chat.escapeHTML(this.lastGuesser);
			} else if (this.guesses[this.guesses.length - 1].length === 1) {
				// last guess was a letter
				output += ' – ' + Chat.escapeHTML(this.lastGuesser) + '';
			} else {
				output += '
Guessed: ' + this.guesses[this.guesses.length - 1] + ' – ' + Chat.escapeHTML(this.lastGuesser) + '';
			}
		}

		output += '

';

		return output;
	}

	display(user, broadcast) {
		if (broadcast) {
			this.room.add('|uhtml|hangman' + this.room.gameNumber + '|' + this.generateWindow());
		} else {
			user.sendTo(this.room, '|uhtml|hangman' + this.room.gameNumber + '|' + this.generateWindow());
		}
	}

	update() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|' + this.generateWindow());

		if (this.incorrectGuesses === maxMistakes) {
			this.finish();
		}
	}

	end() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|
(The game of hangman was ended.)
');
		this.room.add("The game of hangman was ended.");
		delete this.room.game;
	}

	finish() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|
(The game of hangman has ended – scroll down to see the results)
');
		this.room.add('|html|' + this.generateWindow());
		delete this.room.game;
	}
}

exports.commands = {
	hangman: {
		create: 'new',
		new: function (target, room, user) {
			let params = target.split(',');

			if (!this.can('minigame', null, room)) return false;
			if (room.hangmanDisabled) return this.errorReply("Hangman is disabled for this room.");
			if (!this.canTalk()) return;
			if (room.game) return this.errorReply("There is already a game of " + room.game.title + " in progress in this room.");

			if (!params) return this.errorReply("No word entered.");
			let word = params[0].replace(/[^A-Za-z '-]/g, '');
			if (word.replace(/ /g, '').length < 1) return this.errorReply("Enter a valid word");
			if (word.length > 30) return this.errorReply("Phrase must be less than 30 characters.");
			if (word.split(' ').some(w => w.length > 20)) return this.errorReply("Each word in the phrase must be less than 20 characters.");
			if (!/[a-zA-Z]/.test(word)) return this.errorReply("Word must contain at least one letter.");

			let hint;
			if (params.length > 1) {
				hint = params.slice(1).join(',').trim();
				if (hint.length > 150) return this.errorReply("Hint too long.");
			}

			room.game = new Hangman(room, user, word, hint);
			room.game.display(user, true);

			return this.privateModCommand("(A game of hangman was started by " + user.name + ".)");
		},
		createhelp: ["/hangman create [word], [hint] - Makes a new hangman game. Requires: % @ * # & ~"],

		guess: function (target, room, user) {
			if (!target) return this.parse('/help guess');
			if (!room.game || room.game.gameid !== 'hangman') return this.errorReply("There is no game of hangman running in this room.");
			if (!this.canTalk()) return;

			room.game.guess(target, user);
		},
		guesshelp: [
			"/hangman guess [letter] - Makes a guess for the letter entered.",
			"/hangman guess [word] - Same as a letter, but guesses an entire word.",
		],

		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return;
			if (!room.game || room.game.gameid !== 'hangman') return this.errorReply("There is no game of hangman running in this room.");

			room.game.end();
			return this.privateModCommand("(The game of hangman was ended by " + user.name + ".)");
		},
		endhelp: ["/hangman end - Ends the game of hangman before the man is hanged or word is guessed. Requires: % @ * # & ~"],

		disable: function (target, room, user) {
			if (!this.can('gamemanagement', null, room)) return;
			if (room.hangmanDisabled) {
				return this.errorReply("Hangman is already disabled.");
			}
			room.hangmanDisabled = true;
			if (room.chatRoomData) {
				room.chatRoomData.hangmanDisabled = true;
				Rooms.global.writeChatRoomData();
			}
			return this.sendReply("Hangman has been disabled for this room.");
		},

		enable: function (target, room, user) {
			if (!this.can('gamemanagement', null, room)) return;
			if (!room.hangmanDisabled) {
				return this.errorReply("Hangman is already enabled.");
			}
			delete room.hangmanDisabled;
			if (room.chatRoomData) {
				delete room.chatRoomData.hangmanDisabled;
				Rooms.global.writeChatRoomData();
			}
			return this.sendReply("Hangman has been enabled for this room.");
		},

		display: function (target, room, user) {
			if (!room.game || room.game.title !== 'Hangman') return this.errorReply("There is no game of hangman running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			room.game.display(user, this.broadcasting);
		},

		'': function (target, room, user) {
			return this.parse('/help hangman');
		},
	},

	hangmanhelp: [
		"/hangman allows users to play the popular game hangman in PS rooms.",
		"Accepts the following commands:",
		"/hangman create [word], [hint] - Makes a new hangman game. Requires: % @ * # & ~",
		"/hangman guess [letter] - Makes a guess for the letter entered.",
		"/hangman guess [word] - Same as a letter, but guesses an entire word.",
		"/hangman display - Displays the game.",
		"/hangman end - Ends the game of hangman before the man is hanged or word is guessed. Requires: % @ * # & ~",
		"/hangman [enable/disable] - Enables or disables hangman from being started in a room. Requires: # & ~",
	],

	guess: function (target, room, user) {
		if (!room.game) return this.errorReply("There is no game running in this room.");
		if (!this.canTalk()) return;
		if (!room.game.guess) return this.errorReply("You can't guess anything in this game.");

		room.game.guess(target, user);
	},
	guesshelp: [
		"/guess - Shortcut for /hangman guess.",
		"/hangman guess [letter] - Makes a guess for the letter entered.",
		"/hangman guess [word] - Same as a letter, but guesses an entire word.",
	],
};
<commit_msg>Hangman: Give EXP to Winner (#323)<commit_after>/*
* Hangman chat plugin
* By bumbadadabum and Zarel. Art by crobat.
*/

'use strict';

const maxMistakes = 6;

class Hangman extends Rooms.RoomGame {
	constructor(room, user, word, hint) {
		super(room);

		if (room.gameNumber) {
			room.gameNumber++;
		} else {
			room.gameNumber = 1;
		}

		this.gameid = 'hangman';
		this.title = 'Hangman';
		this.creator = user.userid;
		this.word = word;
		this.hint = hint;
		this.incorrectGuesses = 0;

		this.guesses = [];
		this.letterGuesses = [];
		this.lastGuesser = '';
		this.wordSoFar = [];

		for (let i = 0; i < word.length; i++) {
			if (/[a-zA-Z]/.test(word[i])) {
				this.wordSoFar.push('_');
			} else {
				this.wordSoFar.push(word[i]);
			}
		}
	}

	guess(word, user) {
		if (user.userid === this.room.game.creator) return user.sendTo(this.room, "You can't guess in your own hangman game.");

		let sanitized = word.replace(/[^A-Za-z ]/g, '');
		let normalized = toId(sanitized);
		if (normalized.length < 1) return user.sendTo(this.room, "Guess too short.");
		if (sanitized.length > 30) return user.sendTo(this.room, "Guess too long.");

		for (let i = 0; i < this.guesses.length; i++) {
			if (normalized === toId(this.guesses[i])) return user.sendTo(this.room, "Your guess has already been guessed.");
		}

		if (sanitized.length > 1) {
			if (!this.guessWord(sanitized, user.name)) {
				user.sendTo(this.room, "Invalid guess");
			} else {
				this.room.send(user.name + " guessed \"" + sanitized + "\"!");
			}
		} else {
			if (!this.guessLetter(sanitized, user.name)) {
				user.sendTo(this.room, "Invalid guess");
			}
		}
	}

	guessLetter(letter, guesser) {
		letter = letter.toUpperCase();
		if (this.guesses.includes(letter)) return false;
		if (this.word.toUpperCase().includes(letter)) {
			for (let i = 0; i < this.word.length; i++) {
				if (this.word[i].toUpperCase() === letter) {
					this.wordSoFar[i] = this.word[i];
				}
			}

			if (!this.wordSoFar.includes('_')) {
				this.incorrectGuesses = -1;
				this.guesses.push(letter);
				this.letterGuesses.push(letter + '1');
				this.lastGuesser = guesser;
				this.finish();
				return true;
			}
			this.letterGuesses.push(letter + '1');
		} else {
			this.incorrectGuesses++;
			this.letterGuesses.push(letter + '0');
		}

		this.guesses.push(letter);
		this.lastGuesser = guesser;
		this.update();
		return true;
	}

	guessWord(word, guesser) {
		let ourWord = toId(this.word);
		let guessedWord = toId(word);
		if (ourWord === guessedWord) {
			for (let i = 0; i < this.wordSoFar.length; i++) {
				if (this.wordSoFar[i] === '_') {
					this.wordSoFar[i] = this.word[i];
				}
			}
			this.incorrectGuesses = -1;
			this.guesses.push(word);
			this.lastGuesser = guesser;
			this.finish();
			return true;
		} else if (ourWord.length === guessedWord.length) {
			this.incorrectGuesses++;
			this.guesses.push(word);
			this.lastGuesser = guesser;
			this.update();
			return true;
		}
		return false;
	}

	hangingMan() {
		return '';
	}

	generateWindow() {
		let result = 0;

		if (this.incorrectGuesses === maxMistakes) {
			result = 1;
		} else if (!this.wordSoFar.includes('_')) {
			result = 2;
		}

		let output = '
';
		output += '
' + (result === 1 ? 'Too bad! The mon has been hanged.' : (result === 2 ? 'The word has been guessed. Congratulations!' : 'Hangman')) + '
';
		output += '

' + this.hangingMan() + '	';

		let wordString = this.wordSoFar.join('');
		if (result === 1) {
			let word = this.word;
			wordString = wordString.replace(/_+/g, (match, offset) =>
				'' + word.substr(offset, match.length) + ''
			);
		}

		if (this.hint) output += '
(Hint: ' + Chat.escapeHTML(this.hint) + ')
';
		output += '
' + wordString + '
';
		if (this.guesses.length) {
			if (this.letterGuesses.length) {
				output += 'Letters: ' + this.letterGuesses.map(g =>
					'' + Chat.escapeHTML(g[0]) + ''
				).join(', ');
			}
			if (result === 2) {
				output += '
Winner: ' + Chat.escapeHTML(this.lastGuesser);
				SG.addExp(this.lastGuesser, this.room, 5);
			} else if (this.guesses[this.guesses.length - 1].length === 1) {
				// last guess was a letter
				output += ' – ' + Chat.escapeHTML(this.lastGuesser) + '';
			} else {
				output += '
Guessed: ' + this.guesses[this.guesses.length - 1] + ' – ' + Chat.escapeHTML(this.lastGuesser) + '';
			}
		}

		output += '

';

		return output;
	}

	display(user, broadcast) {
		if (broadcast) {
			this.room.add('|uhtml|hangman' + this.room.gameNumber + '|' + this.generateWindow());
		} else {
			user.sendTo(this.room, '|uhtml|hangman' + this.room.gameNumber + '|' + this.generateWindow());
		}
	}

	update() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|' + this.generateWindow());

		if (this.incorrectGuesses === maxMistakes) {
			this.finish();
		}
	}

	end() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|
(The game of hangman was ended.)
');
		this.room.add("The game of hangman was ended.");
		delete this.room.game;
	}

	finish() {
		this.room.add('|uhtmlchange|hangman' + this.room.gameNumber + '|
(The game of hangman has ended – scroll down to see the results)
');
		this.room.add('|html|' + this.generateWindow());
		delete this.room.game;
	}
}

exports.commands = {
	hangman: {
		create: 'new',
		new: function (target, room, user) {
			let params = target.split(',');

			if (!this.can('minigame', null, room)) return false;
			if (room.hangmanDisabled) return this.errorReply("Hangman is disabled for this room.");
			if (!this.canTalk()) return;
			if (room.game) return this.errorReply("There is already a game of " + room.game.title + " in progress in this room.");

			if (!params) return this.errorReply("No word entered.");
			let word = params[0].replace(/[^A-Za-z '-]/g, '');
			if (word.replace(/ /g, '').length < 1) return this.errorReply("Enter a valid word");
			if (word.length > 30) return this.errorReply("Phrase must be less than 30 characters.");
			if (word.split(' ').some(w => w.length > 20)) return this.errorReply("Each word in the phrase must be less than 20 characters.");
			if (!/[a-zA-Z]/.test(word)) return this.errorReply("Word must contain at least one letter.");

			let hint;
			if (params.length > 1) {
				hint = params.slice(1).join(',').trim();
				if (hint.length > 150) return this.errorReply("Hint too long.");
			}

			room.game = new Hangman(room, user, word, hint);
			room.game.display(user, true);

			return this.privateModCommand("(A game of hangman was started by " + user.name + ".)");
		},
		createhelp: ["/hangman create [word], [hint] - Makes a new hangman game. Requires: % @ * # & ~"],

		guess: function (target, room, user) {
			if (!target) return this.parse('/help guess');
			if (!room.game || room.game.gameid !== 'hangman') return this.errorReply("There is no game of hangman running in this room.");
			if (!this.canTalk()) return;

			room.game.guess(target, user);
		},
		guesshelp: [
			"/hangman guess [letter] - Makes a guess for the letter entered.",
			"/hangman guess [word] - Same as a letter, but guesses an entire word.",
		],

		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return;
			if (!room.game || room.game.gameid !== 'hangman') return this.errorReply("There is no game of hangman running in this room.");

			room.game.end();
			return this.privateModCommand("(The game of hangman was ended by " + user.name + ".)");
		},
		endhelp: ["/hangman end - Ends the game of hangman before the man is hanged or word is guessed. Requires: % @ * # & ~"],

		disable: function (target, room, user) {
			if (!this.can('gamemanagement', null, room)) return;
			if (room.hangmanDisabled) {
				return this.errorReply("Hangman is already disabled.");
			}
			room.hangmanDisabled = true;
			if (room.chatRoomData) {
				room.chatRoomData.hangmanDisabled = true;
				Rooms.global.writeChatRoomData();
			}
			return this.sendReply("Hangman has been disabled for this room.");
		},

		enable: function (target, room, user) {
			if (!this.can('gamemanagement', null, room)) return;
			if (!room.hangmanDisabled) {
				return this.errorReply("Hangman is already enabled.");
			}
			delete room.hangmanDisabled;
			if (room.chatRoomData) {
				delete room.chatRoomData.hangmanDisabled;
				Rooms.global.writeChatRoomData();
			}
			return this.sendReply("Hangman has been enabled for this room.");
		},

		display: function (target, room, user) {
			if (!room.game || room.game.title !== 'Hangman') return this.errorReply("There is no game of hangman running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			room.game.display(user, this.broadcasting);
		},

		'': function (target, room, user) {
			return this.parse('/help hangman');
		},
	},

	hangmanhelp: [
		"/hangman allows users to play the popular game hangman in PS rooms.",
		"Accepts the following commands:",
		"/hangman create [word], [hint] - Makes a new hangman game. Requires: % @ * # & ~",
		"/hangman guess [letter] - Makes a guess for the letter entered.",
		"/hangman guess [word] - Same as a letter, but guesses an entire word.",
		"/hangman display - Displays the game.",
		"/hangman end - Ends the game of hangman before the man is hanged or word is guessed. Requires: % @ * # & ~",
		"/hangman [enable/disable] - Enables or disables hangman from being started in a room. Requires: # & ~",
	],

	guess: function (target, room, user) {
		if (!room.game) return this.errorReply("There is no game running in this room.");
		if (!this.canTalk()) return;
		if (!room.game.guess) return this.errorReply("You can't guess anything in this game.");

		room.game.guess(target, user);
	},
	guesshelp: [
		"/guess - Shortcut for /hangman guess.",
		"/hangman guess [letter] - Makes a guess for the letter entered.",
		"/hangman guess [word] - Same as a letter, but guesses an entire word.",
	],
};
<|endoftext|>


<commit_before>angular.module('swot').controller('EditQuizCtrl', function (quiz, $scope, $timeout, focus, $debounce) {
    $scope.quiz = {
        _id: _quizId || null,
        name: "",
        questions: [{}]
    };
    $scope.alerts = [];
    $scope.saveStatus = "";
    $scope.saveStatusTimeout = null;
    $scope.saveMessage = "";
    $scope.isSaving = false;
    $scope.savedSuccessfully = true;
    $scope.addQuestionTooltipsRemaining = 2;    // Stop showing the tooltip after adding a couple questions.

    $scope.isNew = function () {
        return $scope.quiz._id === null;
    }

    $scope.load = function () {
        quiz.load($scope.quiz._id, function (response) {
            $scope.quiz = response.quiz;
        }, function (error) {
            $scope.showError('An error occurred while loading the quiz: \n' + error);
        });
    };

    $scope.save = function (callback) {
        $scope.closeAllAlerts();
        $scope.isSaving = true;

        if ($scope.saveStatusTimeout !== null) {
            $timeout.cancel($scope.saveStatusTimeout);
        }

        var onSaveFinished = function () {
            $scope.saveStatus = "Saved";
            $scope.savedSuccessfully = true;
            $scope.isSaving = false;
            $scope.saveStatusTimeout = $timeout(function () {
                $scope.saveStatus = "";
            }, 2000);
            if (typeof (callback) === 'function') {
                callback(true);
            };
            $scope.editQuizForm.$setPristine();
        };

        var onError = function (error) {
            $scope.saveStatus = "";
            $scope.saveMessage = 'An error occurred while saving the quiz: ' + error;
            $scope.savedSuccessfully = false;
            $scope.isSaving = false;
            if (typeof (callback) === 'function') {
                callback(false);
            };
        };

        if ($scope.isNew()) {
            quiz.create($scope.quiz, function (id) {
                $scope.quiz._id = id;
                onSaveFinished();
            }, onError);
        } else {
            quiz.save($scope.quiz, onSaveFinished, onError);
        }
    };

    $scope.autosave = function () {
        if ($scope.editQuizForm.$dirty) { $scope.save(); }
    };

    $scope.deleteQuiz = function () {
        $scope.closeAllAlerts();

        quiz.deleteQuiz($scope.quiz._id, function () {
            bootbox.alert("The quiz has been deleted successfully.", function () {
                window.location.href = "/quizzes";
            });
        }, function (error) {
            $scope.showError("An error occurred while deleting the quiz: " + error);
        });
    };

    $scope.addQuestion = function () {
        $scope.quiz.questions.push({});

        // Focus on the first input field for the newly-added question
        focus('newQuestionAdded');

        // Mark the form as dirty
        $scope.editQuizForm.$setDirty();

        // Stop showing the tooltip on the Add Question button after adding a couple questions
        // Note that tooltip is now shown where there are no questions, so adding the first question
        // should not decrement the count.
        if ($scope.quiz.questions.length > 1) {
            $scope.addQuestionTooltipsRemaining--;
        }
    };

    $scope.removeQuestion = function (index) {
        $scope.$broadcast('blurEditor');
        $scope.quiz.questions.splice(index, 1);
        $scope.editQuizForm.$setDirty();
    };

    $scope.exportJson = function () {
        bootbox.confirm('The quiz will be saved before exporting. Continue?', function (confirmed) {
            if (confirmed) {
                $scope.save(function (success) {
                    if (success) {
                        location.href = '/export?id=' + $scope.quiz._id;
                    }
                });
            }
        });
    };

    $scope.sortableOptions = {
        update: function(e, ui) { },
        placeholder: 'sortable-list-placeholder',
        handle: '.drag-handle',
        forcePlaceholderSize: true,
        start: function (e, ui) {
            ui.placeholder.height(ui.item.outerHeight());

            var $list = $(ui.item).closest('.ui-sortable');

            // Clear any previous shadow lists
            $('.shadow-list').remove();

            // Create a shadow list by cloning the original
            var $shadow = $('

', {
                'class': 'shadow-list'
            });
            $list.after($shadow);

            // Loop over the original list items...
            $list.children("li").each(function() {

                // clone the original items to make their
                // absolute-positioned counterparts...
                var item = $(this);
                var item_clone = item.clone();

                // 'store' the clone for later use...
                item.data("clone", item_clone);

                // set the initial position/size of the clone
                var position = item.position();
                item_clone.css("left", position.left);
                item_clone.css("top", position.top);
                item_clone.width(item.outerWidth());

                // append the clone...
                $shadow.append(item_clone);
            });

            // loop through the items, except the one we're
            // currently dragging, and hide it...
            ui.helper.addClass("exclude-me");
            $list.children("li:not(.exclude-me)").css("visibility", "hidden");

            // get the clone that's under it and hide it...
            ui.helper.data("clone").hide();
        },
        change: function(e, ui) {
            $scope.editQuizForm.$setDirty();

            var $list = $(ui.item).closest('.ui-sortable');

            // get all invisible items that are also not placeholders
            // and process them when ordering changes...
            $list.find("li:not(.exclude-me, .ui-sortable-placeholder)").each(function() {
                var item = jQuery(this);
                var clone = item.data("clone");

                // stop current clone animations...
                clone.stop(true, false);

                // get the invisible item, which has snapped to a new
                // location, get its position, and animate the visible
                // clone to it...
                var position = item.position();
                clone.animate( { left: position.left, top:position.top }, 500);
            });
        },
        stop: function (e, ui) {
            var $list = $(ui.item).closest('.ui-sortable');

            // get the item we were just dragging, and
            // its clone, and adjust accordingly...
            $list.children("li.exclude-me").each(function(){
                var item = $(this);
                var clone = item.data("clone");
                var position = item.position();

                // move the clone under the item we've just dropped...
                clone.css("left", position.left);
                clone.css("top", position.top);
                clone.show();

                // remove unnecessary class...
                item.removeClass("exclude-me");

                // Remove the "position: relative;" style that was added to the item - otherwise,
                // this item will look wrong when reordering things a second time.
                item.css("position", "");
            });

            // make sure all our original items are visible again...
            $list.children("li").css("visibility", "visible");

            // Remove the shadow list
            $('.shadow-list').remove();
        }
    };

    $scope.ckEditorConfig = {
        toolbar : [
            { name:  'lists_and_indentation',
              items: [  'NumberedList', 'BulletedList', 'Outdent', 'Indent', 'Blockquote',
                        '-',
                        'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock',
                        '-',
                        'Link', 'Unlink', '-' ] },

            { name:  'tables_and_images',
              items: [  'Image', 'Table', 'HorizontalRule', 'SpecialChar' ] },

            '/',

            { name:  'formatting',
              items: [  'Bold', 'Italic', 'Underline',
                        '-',
                        'TextColor', 'BGColor',
                        '-',
                        'Superscript', 'Subscript', 'Strike',
                        '-',
                        'RemoveFormat' , 'Font', 'FontSize'
                        ] },
        ]
    };

    $scope.addQuestionTooltip = function () {
        if ($scope.addQuestionTooltipsRemaining <= 0 || $scope.quiz.questions.length == 0) {
            return "";
        }

        return "You can also add a question by hitting the TAB key after typing in the last answer.";
    }

    // Alerts
    // ------

    $scope.showError = function (message) {
        return $scope.alerts.push({ type: 'danger', msg: message }) - 1;
    };

    $scope.showSuccess = function (message) {
        return $scope.alerts.push({ type: 'success', msg: message }) - 1;
    };

    $scope.showInfo = function (message) {
        return $scope.alerts.push({ type: 'info', msg: message }) - 1;
    };

    $scope.showWarning = function (message) {
        return $scope.alerts.push({ type: 'warning', msg: message }) - 1;
    };

    $scope.closeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    $scope.closeAllAlerts = function() {
        $scope.alerts = [];
    };

    // Events
    // ------

    $scope.answerKeypress = function ($index, $event) {
        // Add new question when hitting TAB on the last input
        if ($index === $scope.quiz.questions.length - 1) {
            if ($event.keyCode === 9 &&  !$event.ctrlKey && !$event.metaKey && !$event.altKey && !$event.shiftKey) {
                $scope.addQuestion();
            }
        }
    }

    // Initialization
    // --------------

    if ($scope.quiz._id) { $scope.load(); }

    $scope.$watch('quiz', $debounce($scope.autosave, 1000), true);
});
<commit_msg>Edit Quiz: A couple minor fixes for saving multiple times in quick succession<commit_after>angular.module('swot').controller('EditQuizCtrl', function (quiz, $scope, $timeout, focus, $debounce) {
    $scope.quiz = {
        _id: _quizId || null,
        name: "",
        questions: [{}]
    };
    $scope.alerts = [];
    $scope.saveStatus = "";
    $scope.saveStatusTimeout = null;
    $scope.saveMessage = "";
    $scope.isSaving = false;
    $scope.savedSuccessfully = true;
    $scope.addQuestionTooltipsRemaining = 2;    // Stop showing the tooltip after adding a couple questions.

    $scope.isNew = function () {
        return $scope.quiz._id === null;
    }

    $scope.load = function () {
        quiz.load($scope.quiz._id, function (response) {
            $scope.quiz = response.quiz;
        }, function (error) {
            $scope.showError('An error occurred while loading the quiz: \n' + error);
        });
    };

    $scope.save = function (callback) {
        if ($scope.isSaving) { return; }

        $scope.closeAllAlerts();
        $scope.isSaving = true;

        if ($scope.saveStatusTimeout !== null) {
            $timeout.cancel($scope.saveStatusTimeout);
        }

        var onSaveFinished = function () {
            $scope.saveStatus = "Saved";
            $scope.savedSuccessfully = true;
            $scope.isSaving = false;
            $scope.saveStatusTimeout = $timeout(function () {
                $scope.saveStatus = "";
                $scope.saveStatusTimeout = null;
            }, 2000);
            if (typeof (callback) === 'function') {
                callback(true);
            };
            $scope.editQuizForm.$setPristine();
        };

        var onError = function (error) {
            $scope.saveStatus = "";
            $scope.saveMessage = 'An error occurred while saving the quiz: ' + error;
            $scope.savedSuccessfully = false;
            $scope.isSaving = false;
            if (typeof (callback) === 'function') {
                callback(false);
            };
        };

        if ($scope.isNew()) {
            quiz.create($scope.quiz, function (id) {
                $scope.quiz._id = id;
                onSaveFinished();
            }, onError);
        } else {
            quiz.save($scope.quiz, onSaveFinished, onError);
        }
    };

    $scope.autosave = function () {
        if ($scope.editQuizForm.$dirty) { $scope.save(); }
    };

    $scope.deleteQuiz = function () {
        $scope.closeAllAlerts();

        quiz.deleteQuiz($scope.quiz._id, function () {
            bootbox.alert("The quiz has been deleted successfully.", function () {
                window.location.href = "/quizzes";
            });
        }, function (error) {
            $scope.showError("An error occurred while deleting the quiz: " + error);
        });
    };

    $scope.addQuestion = function () {
        $scope.quiz.questions.push({});

        // Focus on the first input field for the newly-added question
        focus('newQuestionAdded');

        // Mark the form as dirty
        $scope.editQuizForm.$setDirty();

        // Stop showing the tooltip on the Add Question button after adding a couple questions
        // Note that tooltip is now shown where there are no questions, so adding the first question
        // should not decrement the count.
        if ($scope.quiz.questions.length > 1) {
            $scope.addQuestionTooltipsRemaining--;
        }
    };

    $scope.removeQuestion = function (index) {
        $scope.$broadcast('blurEditor');
        $scope.quiz.questions.splice(index, 1);
        $scope.editQuizForm.$setDirty();
    };

    $scope.exportJson = function () {
        bootbox.confirm('The quiz will be saved before exporting. Continue?', function (confirmed) {
            if (confirmed) {
                $scope.save(function (success) {
                    if (success) {
                        location.href = '/export?id=' + $scope.quiz._id;
                    }
                });
            }
        });
    };

    $scope.sortableOptions = {
        update: function(e, ui) { },
        placeholder: 'sortable-list-placeholder',
        handle: '.drag-handle',
        forcePlaceholderSize: true,
        start: function (e, ui) {
            ui.placeholder.height(ui.item.outerHeight());

            var $list = $(ui.item).closest('.ui-sortable');

            // Clear any previous shadow lists
            $('.shadow-list').remove();

            // Create a shadow list by cloning the original
            var $shadow = $('

', {
                'class': 'shadow-list'
            });
            $list.after($shadow);

            // Loop over the original list items...
            $list.children("li").each(function() {

                // clone the original items to make their
                // absolute-positioned counterparts...
                var item = $(this);
                var item_clone = item.clone();

                // 'store' the clone for later use...
                item.data("clone", item_clone);

                // set the initial position/size of the clone
                var position = item.position();
                item_clone.css("left", position.left);
                item_clone.css("top", position.top);
                item_clone.width(item.outerWidth());

                // append the clone...
                $shadow.append(item_clone);
            });

            // loop through the items, except the one we're
            // currently dragging, and hide it...
            ui.helper.addClass("exclude-me");
            $list.children("li:not(.exclude-me)").css("visibility", "hidden");

            // get the clone that's under it and hide it...
            ui.helper.data("clone").hide();
        },
        change: function(e, ui) {
            $scope.editQuizForm.$setDirty();

            var $list = $(ui.item).closest('.ui-sortable');

            // get all invisible items that are also not placeholders
            // and process them when ordering changes...
            $list.find("li:not(.exclude-me, .ui-sortable-placeholder)").each(function() {
                var item = jQuery(this);
                var clone = item.data("clone");

                // stop current clone animations...
                clone.stop(true, false);

                // get the invisible item, which has snapped to a new
                // location, get its position, and animate the visible
                // clone to it...
                var position = item.position();
                clone.animate( { left: position.left, top:position.top }, 500);
            });
        },
        stop: function (e, ui) {
            var $list = $(ui.item).closest('.ui-sortable');

            // get the item we were just dragging, and
            // its clone, and adjust accordingly...
            $list.children("li.exclude-me").each(function(){
                var item = $(this);
                var clone = item.data("clone");
                var position = item.position();

                // move the clone under the item we've just dropped...
                clone.css("left", position.left);
                clone.css("top", position.top);
                clone.show();

                // remove unnecessary class...
                item.removeClass("exclude-me");

                // Remove the "position: relative;" style that was added to the item - otherwise,
                // this item will look wrong when reordering things a second time.
                item.css("position", "");
            });

            // make sure all our original items are visible again...
            $list.children("li").css("visibility", "visible");

            // Remove the shadow list
            $('.shadow-list').remove();
        }
    };

    $scope.ckEditorConfig = {
        toolbar : [
            { name:  'lists_and_indentation',
              items: [  'NumberedList', 'BulletedList', 'Outdent', 'Indent', 'Blockquote',
                        '-',
                        'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock',
                        '-',
                        'Link', 'Unlink', '-' ] },

            { name:  'tables_and_images',
              items: [  'Image', 'Table', 'HorizontalRule', 'SpecialChar' ] },

            '/',

            { name:  'formatting',
              items: [  'Bold', 'Italic', 'Underline',
                        '-',
                        'TextColor', 'BGColor',
                        '-',
                        'Superscript', 'Subscript', 'Strike',
                        '-',
                        'RemoveFormat' , 'Font', 'FontSize'
                        ] },
        ]
    };

    $scope.addQuestionTooltip = function () {
        if ($scope.addQuestionTooltipsRemaining <= 0 || $scope.quiz.questions.length == 0) {
            return "";
        }

        return "You can also add a question by hitting the TAB key after typing in the last answer.";
    }

    // Alerts
    // ------

    $scope.showError = function (message) {
        return $scope.alerts.push({ type: 'danger', msg: message }) - 1;
    };

    $scope.showSuccess = function (message) {
        return $scope.alerts.push({ type: 'success', msg: message }) - 1;
    };

    $scope.showInfo = function (message) {
        return $scope.alerts.push({ type: 'info', msg: message }) - 1;
    };

    $scope.showWarning = function (message) {
        return $scope.alerts.push({ type: 'warning', msg: message }) - 1;
    };

    $scope.closeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    $scope.closeAllAlerts = function() {
        $scope.alerts = [];
    };

    // Events
    // ------

    $scope.answerKeypress = function ($index, $event) {
        // Add new question when hitting TAB on the last input
        if ($index === $scope.quiz.questions.length - 1) {
            if ($event.keyCode === 9 &&  !$event.ctrlKey && !$event.metaKey && !$event.altKey && !$event.shiftKey) {
                $scope.addQuestion();
            }
        }
    }

    // Initialization
    // --------------

    if ($scope.quiz._id) { $scope.load(); }

    $scope.$watch('quiz', $debounce($scope.autosave, 1000), true);
});
<|endoftext|>