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

var PortletWindowController = function (facade) {
    var win, _self = this, app = facade,
        Device, WindowManager, PortalWindow, Styles, UI, LocalDictionary, Login, UPM, Session,
        activityIndicator, titleBar, navBar, navBackButton, webView,
        initialized, winListeners = [], activePortlet, _homeURL, _lastVideoOpened = '',
        pathToRoot = '../../',
        init, drawWindow, getQualifiedURL, getLocalUrl,
        includePortlet, onPortletLoad, onPortletBeforeLoad, onWindowOpen, onAppResume, onBackBtnPress;

    init = function () {
        Ti.API.debug("init() in PortletWindowController");
        _self.key = 'portlet';

        Ti.App.addEventListener('updatestylereference', function (e) {
            Styles = app.styles;
        });
    };

    this.close = function (options) {
        Ti.API.info("close() in PortletWindowController");
        if (win) {
            win.close();
        }
    };

    this.open = function (portlet) {
        Ti.API.debug("open() in PortletWindowController");

        if (!initialized) {
            //Declare pointers to facade members
            Device = app.models.deviceProxy;
            WindowManager = app.models.windowManager;
            PortalWindow = app.controllers.portalWindowController;
            Styles = app.styles;
            UI = app.UI;
            LocalDictionary = app.localDictionary;
            Login = app.models.loginProxy;
            UPM = app.config;
            Session = app.models.sessionProxy;

            initialized = true;
        }

        if (portlet) {
            activePortlet = portlet;
        }
        else {
            Ti.API.error("No portlet was passed to includePortlet() in PortletWindowController");
            WindowManager.openWindow(PortalWindow.key);
            return;
        }

        win = Titanium.UI.createWindow({
            url: 'js/views/WindowContext.js',
            key: 'portlet',
            backgroundColor: Styles.backgroundColor,
            exitOnClose: false,
            navBarHidden: true
            // orientationModes: [Ti.UI.PORTRAIT]
        });
        win.open();

        titleBar = UI.createTitleBar({
            title: portlet.title,
            settingsButton: false,
            homeButton: true
        });

        // initialize navigation bar for URLs outside the portal
        navBar = UI.createSecondaryNavBar({
            backButton: true,
            backButtonHandler: onBackBtnPress,
            btnFloatLeft: true
        });
        navBar.top = 40;
        navBar.visible = false;

        activityIndicator = UI.createActivityIndicator();
        activityIndicator.hide();

        win.add(titleBar);
        win.add(navBar);
        if (Device.isIOS() || !webView) {
            Ti.API.debug("The device is iOS or there isn't a webView yet");
            webView = Titanium.UI.createWebView(Styles.portletView);
            webView.addEventListener('load', onPortletLoad);
            webView.addEventListener('beforeload', onPortletBeforeLoad);
            webView.hide();
        }
        else {
            Ti.API.debug("It's Android and there's already a webview");
            try {
                win.remove(webView);
            }
            catch (e) {
                Ti.API.error("Couldn't remove webview: " + JSON.stringify(e));
            }
        }
        win.add(webView);
        win.add(activityIndicator);

        includePortlet(activePortlet);

        Titanium.App.addEventListener('dimensionchanges', function (e) {
            if (webView.canGoBack()) {
                navBar.visible = true;
                webView.top = titleBar.height + navBar.height;
                webView.height = win.height - titleBar.height - navBar.height;
            }
            else {
                Ti.API.info("Webview can't go back");
                webView.top = titleBar.height;
                webView.height = win.height - titleBar.height;
            }
        });
    };

    includePortlet = function (portlet) {
        Ti.API.debug("includePortlet() in PortletWindowController");
        webView.visible = false;
        activityIndicator.setLoadingMessage(LocalDictionary.loading);
        activityIndicator.show();

        _homeURL = getQualifiedURL(portlet.url);
        webView.url = _homeURL;

        titleBar.updateTitle(portlet.title);
    };

    getLocalUrl = function (url) {
        var localUrl, isValidSession;
        Ti.API.debug("getLocalUrl() in SharedWebView");
        /*
        This method determines if a session is valid for the webview, and will
        either modify the URL and load, or will load the URL as-is if session is active.
        This method only returns a URL, doesn't actually set the url property of the webview.
        */

        if (!Device.checkNetwork()) {
            return false;
        }

        //We only need to check the session if it's a link to the portal.
        isValidSession = Login.isValidWebViewSession();
        if (!isValidSession) {
            Ti.API.debug("!isValidSession in getLocalUrl()");
            Login.getLoginURL(url);
            localUrl = Login.getLoginURL(url);
        }
        else {
            Ti.API.debug("isValidSession in getLocalUrl()");
            if (url.indexOf('/') === 0) {
                Ti.API.info("Index of / in URL is 0");
                var newUrl = UPM.BASE_PORTAL_URL + url;
                Ti.API.info(newUrl);
                localUrl = newUrl;
            }
            else {
                Ti.API.info("Index of / in URL is NOT 0");
                localUrl = url;
            }
        }
        return localUrl;
    };

    getQualifiedURL = function (url) {
        Ti.API.debug("getQualifiedURL() in PortletWindowController");
        var _url;
        if (url.indexOf('/') == 0) {
            Ti.API.debug("Portlet URL is local");
            if (Session.validateSessions()[LoginProxy.sessionTimeContexts.WEBVIEW]) {
                Ti.API.debug("Session is active in getQualifiedURL()");
                _url = getLocalUrl(url);
            }
            else {
                Ti.API.debug("Session is NOT active in getQualifiedURL(): " + JSON.stringify(Session.validateSessions()));
                _url = Login.getLoginURL(url);
            }
            webView.externalModule = false;
            webView.top = titleBar.height;
        } else {
            Ti.API.debug("Portlet URL is external in getQualifiedURL()");
            _url = url;
            webView.externalModule = true;
        }

        return _url;
    };

    onPortletBeforeLoad = function (e) {
        Ti.API.debug("onPortletBeforeLoad() in PortletWindowController" + webView.url);

        if (Device.isAndroid()) {
            webView.hide();
        }
        //We want to make sure we don't need to re-establish a session.
        if (webView.url !== getQualifiedURL(webView.url)) {
            webView.stopLoading();
            webView.url = getQualifiedURL(webView.url);
        }
        else if (e.url.indexOf('http://m.youtube.com') === 0 && Device.isAndroid()) {
        	var _URLToOpen = e.url, _params = e.url.split('?')[1].split('&');
        	webView.stopLoading();

        	for (var i=0, iLength = _params.length; i<iLength; i++) {
        		Ti.API.info("iterating through url params");
        		if (_params[i].indexOf('desktop_uri') > -1) {
        			Ti.API.info("Found the desktop URI:" + _params[i]);
        			_URLToOpen = _params[i].split('=', 2)[1];
        			_URLToOpen = decodeURIComponent(_URLToOpen);
        			break;
        		}
        	}
        	Ti.API.info("Opening: " + _URLToOpen);
        	if (_URLToOpen !== _lastVideoOpened) {
        	    webView.stopLoading();
                Ti.Platform.openURL(_URLToOpen);
                //Set the last video to this, so that it doesn't try to broadcast the intent twice.
                _lastVideoOpened = _URLToOpen;
        	}
        }
        activityIndicator.setLoadingMessage(LocalDictionary.loading);
        activityIndicator.show();
    };

    onBackBtnPress = function (e) {
        webView.goBack();
    };

    onPortletLoad = function (e) {
        var portalIndex = e.url.indexOf(UPM.BASE_PORTAL_URL);
        Ti.API.debug("onPortletLoad() in PortletWindowController, index: " + portalIndex);
        webView.show();

        activityIndicator.hide();

        if (portalIndex >= 0) {
            Ti.API.debug("Visiting a portal link");
            Ti.App.fireEvent('SessionActivity', {context: LoginProxy.sessionTimeContexts.WEBVIEW});
            //We want to be able to open any video now, so we'll clear the YouTube workaround variable
            _lastVideoOpened = '';
            webView.externalModule = false;
            navBar.visible = false;
            webView.top = titleBar.height;
            webView.height = win.height - titleBar.height;
            // webView.setTop(Styles.titleBar.height);
            // Login.updateSessionTimeout(Login.sessionTimeContexts.WEBVIEW);
        }
        else {
            Ti.API.debug("Visiting an external link. Webview.url = " + webView.url + " & e.url = " + e.url);
            webView.externalModule = true;
            if (webView.canGoBack() && e.url !== _homeURL) {
                navBar.visible = true;
                webView.top = titleBar.height + navBar.height;
                webView.height = win.height - titleBar.height - navBar.height;
            }
            else {
                Ti.API.info("Webview can't go back");
                webView.top = titleBar.height;
                webView.height = win.height - titleBar.height;
            }
            Ti.API.debug("WebView height is: " + webView.height);
            // webView.setTop(Styles.titleBar.height + navBar.height);
        }
        webView.show();
        activityIndicator.hide();
    };

    if (!initialized) {
        init();
    }
};<commit_msg>UMOBILE-110 fixed issue in Android with artifact from previous portlet showing when opening a new portlet.<commit_after>/*
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var PortletWindowController = function (facade) {
    var win, _self = this, app = facade,
        Device, WindowManager, PortalWindow, Styles, UI, LocalDictionary, Login, UPM, Session,
        activityIndicator, titleBar, navBar, navBackButton, webView,
        initialized, winListeners = [], activePortlet, _homeURL, _lastVideoOpened = '',
        pathToRoot = '../../',
        init, drawWindow, getQualifiedURL, getLocalUrl,
        includePortlet, onPortletLoad, onPortletBeforeLoad, onWindowOpen, onAppResume, onBackBtnPress;

    init = function () {
        Ti.API.debug("init() in PortletWindowController");
        _self.key = 'portlet';

        Ti.App.addEventListener('updatestylereference', function (e) {
            Styles = app.styles;
        });
    };

    this.close = function (options) {
        Ti.API.info("close() in PortletWindowController");
        if (webView && Device && Device.isAndroid()) {
            webView.url = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'html/blank.html').nativePath;
        }
        if (win) {
            win.close();
        }
    };

    this.open = function (portlet) {
        Ti.API.debug("open() in PortletWindowController");

        if (!initialized) {
            //Declare pointers to facade members
            Device = app.models.deviceProxy;
            WindowManager = app.models.windowManager;
            PortalWindow = app.controllers.portalWindowController;
            Styles = app.styles;
            UI = app.UI;
            LocalDictionary = app.localDictionary;
            Login = app.models.loginProxy;
            UPM = app.config;
            Session = app.models.sessionProxy;

            initialized = true;
        }

        if (portlet) {
            activePortlet = portlet;
        }
        else {
            Ti.API.error("No portlet was passed to includePortlet() in PortletWindowController");
            WindowManager.openWindow(PortalWindow.key);
            return;
        }

        win = Titanium.UI.createWindow({
            url: 'js/views/WindowContext.js',
            key: 'portlet',
            backgroundColor: Styles.backgroundColor,
            exitOnClose: false,
            navBarHidden: true
            // orientationModes: [Ti.UI.PORTRAIT]
        });
        win.open();

        titleBar = UI.createTitleBar({
            title: portlet.title,
            settingsButton: false,
            homeButton: true
        });

        // initialize navigation bar for URLs outside the portal
        navBar = UI.createSecondaryNavBar({
            backButton: true,
            backButtonHandler: onBackBtnPress,
            btnFloatLeft: true
        });
        navBar.top = 40;
        navBar.visible = false;

        activityIndicator = UI.createActivityIndicator();
        activityIndicator.hide();

        win.add(titleBar);
        win.add(navBar);
        if (Device.isIOS() || !webView) {
            Ti.API.debug("The device is iOS or there isn't a webView yet");
            webView = Titanium.UI.createWebView(Styles.portletView);
            webView.addEventListener('load', onPortletLoad);
            webView.addEventListener('beforeload', onPortletBeforeLoad);
            webView.hide();
        }
        else {
            Ti.API.debug("It's Android and there's already a webview");
            try {
                win.remove(webView);
            }
            catch (e) {
                Ti.API.error("Couldn't remove webview: " + JSON.stringify(e));
            }
        }
        webView.visible = false;
        win.add(webView);
        win.add(activityIndicator);

        includePortlet(activePortlet);

        Titanium.App.addEventListener('dimensionchanges', function (e) {
            if (webView.canGoBack()) {
                navBar.visible = true;
                webView.top = titleBar.height + navBar.height;
                webView.height = win.height - titleBar.height - navBar.height;
            }
            else {
                Ti.API.info("Webview can't go back");
                webView.top = titleBar.height;
                webView.height = win.height - titleBar.height;
            }
        });
    };

    includePortlet = function (portlet) {
        Ti.API.debug("includePortlet() in PortletWindowController");
        webView.visible = false;
        activityIndicator.setLoadingMessage(LocalDictionary.loading);
        activityIndicator.show();

        _homeURL = getQualifiedURL(portlet.url);
        webView.url = _homeURL;

        titleBar.updateTitle(portlet.title);
    };

    getLocalUrl = function (url) {
        var localUrl, isValidSession;
        Ti.API.debug("getLocalUrl() in SharedWebView");
        /*
        This method determines if a session is valid for the webview, and will
        either modify the URL and load, or will load the URL as-is if session is active.
        This method only returns a URL, doesn't actually set the url property of the webview.
        */

        if (!Device.checkNetwork()) {
            return false;
        }

        //We only need to check the session if it's a link to the portal.
        isValidSession = Login.isValidWebViewSession();
        if (!isValidSession) {
            Ti.API.debug("!isValidSession in getLocalUrl()");
            Login.getLoginURL(url);
            localUrl = Login.getLoginURL(url);
        }
        else {
            Ti.API.debug("isValidSession in getLocalUrl()");
            if (url.indexOf('/') === 0) {
                Ti.API.info("Index of / in URL is 0");
                var newUrl = UPM.BASE_PORTAL_URL + url;
                Ti.API.info(newUrl);
                localUrl = newUrl;
            }
            else {
                Ti.API.info("Index of / in URL is NOT 0");
                localUrl = url;
            }
        }
        return localUrl;
    };

    getQualifiedURL = function (url) {
        Ti.API.debug("getQualifiedURL() in PortletWindowController");
        var _url;
        if (url.indexOf('/') == 0) {
            Ti.API.debug("Portlet URL is local");
            if (Session.validateSessions()[LoginProxy.sessionTimeContexts.WEBVIEW]) {
                Ti.API.debug("Session is active in getQualifiedURL()");
                _url = getLocalUrl(url);
            }
            else {
                Ti.API.debug("Session is NOT active in getQualifiedURL(): " + JSON.stringify(Session.validateSessions()));
                _url = Login.getLoginURL(url);
            }
            webView.externalModule = false;
            webView.top = titleBar.height;
        } else {
            Ti.API.debug("Portlet URL is external in getQualifiedURL()");
            _url = url;
            webView.externalModule = true;
        }

        return _url;
    };

    onPortletBeforeLoad = function (e) {
        Ti.API.debug("onPortletBeforeLoad() in PortletWindowController" + webView.url);

        if (Device.isAndroid()) {
            webView.hide();
        }
        //We want to make sure we don't need to re-establish a session.
        if (webView.url !== getQualifiedURL(webView.url)) {
            webView.stopLoading();
            webView.url = getQualifiedURL(webView.url);
        }
        else if (e.url.indexOf('http://m.youtube.com') === 0 && Device.isAndroid()) {
        	var _URLToOpen = e.url, _params = e.url.split('?')[1].split('&');
        	webView.stopLoading();

        	for (var i=0, iLength = _params.length; i<iLength; i++) {
        		Ti.API.info("iterating through url params");
        		if (_params[i].indexOf('desktop_uri') > -1) {
        			Ti.API.info("Found the desktop URI:" + _params[i]);
        			_URLToOpen = _params[i].split('=', 2)[1];
        			_URLToOpen = decodeURIComponent(_URLToOpen);
        			break;
        		}
        	}
        	Ti.API.info("Opening: " + _URLToOpen);
        	if (_URLToOpen !== _lastVideoOpened) {
        	    webView.stopLoading();
                Ti.Platform.openURL(_URLToOpen);
                //Set the last video to this, so that it doesn't try to broadcast the intent twice.
                _lastVideoOpened = _URLToOpen;
        	}
        }
        activityIndicator.setLoadingMessage(LocalDictionary.loading);
        activityIndicator.show();
    };

    onBackBtnPress = function (e) {
        webView.goBack();
    };

    onPortletLoad = function (e) {
        var portalIndex = e.url.indexOf(UPM.BASE_PORTAL_URL);
        Ti.API.debug("onPortletLoad() in PortletWindowController, index: " + portalIndex);
        webView.show();

        activityIndicator.hide();

        if (portalIndex >= 0) {
            Ti.API.debug("Visiting a portal link");
            Ti.App.fireEvent('SessionActivity', {context: LoginProxy.sessionTimeContexts.WEBVIEW});
            //We want to be able to open any video now, so we'll clear the YouTube workaround variable
            _lastVideoOpened = '';
            webView.externalModule = false;
            navBar.visible = false;
            webView.top = titleBar.height;
            webView.height = win.height - titleBar.height;
            // webView.setTop(Styles.titleBar.height);
            // Login.updateSessionTimeout(Login.sessionTimeContexts.WEBVIEW);
        }
        else {
            Ti.API.debug("Visiting an external link. Webview.url = " + webView.url + " & e.url = " + e.url);
            webView.externalModule = true;
            if (webView.canGoBack() && e.url !== _homeURL) {
                navBar.visible = true;
                webView.top = titleBar.height + navBar.height;
                webView.height = win.height - titleBar.height - navBar.height;
            }
            else {
                Ti.API.info("Webview can't go back");
                webView.top = titleBar.height;
                webView.height = win.height - titleBar.height;
            }
            Ti.API.debug("WebView height is: " + webView.height);
            // webView.setTop(Styles.titleBar.height + navBar.height);
        }
        webView.show();
        activityIndicator.hide();
    };

    if (!initialized) {
        init();
    }
};<|endoftext|>


<commit_before>(function () {
  var Pos = CodeMirror.Pos;

  function forEach(arr, f) {
    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
  }

  function arrayContains(arr, item) {
    if (!Array.prototype.indexOf) {
      var i = arr.length;
      while (i--) {
        if (arr[i] === item) {
          return true;
        }
      }
      return false;
    }
    return arr.indexOf(item) != -1;
  }

  function scriptHint(editor, keywords, getToken, options) {
    // Find the token at the cursor
    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
    // Fix for htmlmixed mode
    token.state=CodeMirror.innerMode(editor.getMode(),editor.getTokenAt(editor.getCursor()).state).state;
    // If it's not a 'word-style' token, ignore the token.
		if (!/^[\w$_]*$/.test(token.string)) {
      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
                       type: token.string == "." ? "property" : null};
    }
    // If it is a property, find out what it is a property of.
    while (tprop.type == "property") {
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (tprop.string != ".") return;
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (tprop.string == ')') {
        var level = 1;
        do {
          tprop = getToken(editor, Pos(cur.line, tprop.start));
          switch (tprop.string) {
          case ')': level++; break;
          case '(': level--; break;
          default: break;
          }
        } while (level > 0);
        tprop = getToken(editor, Pos(cur.line, tprop.start));
	if (tprop.type.indexOf("variable") === 0)
	  tprop.type = "function";
	else return; // no clue
      }
      if (!context) var context = [];
      context.push(tprop);
    }
    return {list: getCompletions(token, context, keywords, options),
            from: Pos(cur.line, token.start),
            to: Pos(cur.line, token.end)};
  }

  CodeMirror.javascriptHint = function(editor, options) {
    return scriptHint(editor, javascriptKeywords,
                      function (e, cur) {return e.getTokenAt(cur);},
                      options);
  };

  function getCoffeeScriptToken(editor, cur) {
  // This getToken, it is for coffeescript, imitates the behavior of
  // getTokenAt method in javascript.js, that is, returning "property"
  // type and treat "." as indepenent token.
    var token = editor.getTokenAt(cur);
    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
      token.end = token.start;
      token.string = '.';
      token.type = "property";
    }
    else if (/^\.[\w$_]*$/.test(token.string)) {
      token.type = "property";
      token.start++;
      token.string = token.string.replace(/\./, '');
    }
    return token;
  }

  CodeMirror.coffeescriptHint = function(editor, options) {
    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
  };

  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
                     "toUpperCase toLowerCase split concat match replace search").split(" ");
  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
  var funcProps = "prototype apply call bind".split(" ");
  var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
                  "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");

  function getCompletions(token, context, keywords, options) {
    var found = [], start = token.string;
    function maybeAdd(str) {
      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
    }
    function gatherCompletions(obj) {
      if (typeof obj == "string") forEach(stringProps, maybeAdd);
      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
      for (var name in obj) maybeAdd(name);
    }

    if (context) {
      // If this is a property, see if it belongs to some object we can
      // find in the current environment.
      var obj = context.pop(), base;
      if (obj.type.indexOf("variable") === 0) {
        if (options && options.additionalContext)
          base = options.additionalContext[obj.string];
        base = base || window[obj.string];
      } else if (obj.type == "string") {
        base = "";
      } else if (obj.type == "atom") {
        base = 1;
      } else if (obj.type == "function") {
        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
            (typeof window.jQuery == 'function'))
          base = window.jQuery();
        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
          base = window._();
      }
      while (base != null && context.length)
        base = base[context.pop().string];
      if (base != null) gatherCompletions(base);
    }
    else {
      // If not, just look in the window object and any local scope
      // (reading into JS mode internals to get at the local and global variables)
      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
      gatherCompletions(window);
      forEach(keywords, maybeAdd);
    }
    return found;
  }
})();
<commit_msg>Clean up patch 39cc371f986dbf693bbf98931274c82c5afb5411<commit_after>(function () {
  var Pos = CodeMirror.Pos;

  function forEach(arr, f) {
    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
  }

  function arrayContains(arr, item) {
    if (!Array.prototype.indexOf) {
      var i = arr.length;
      while (i--) {
        if (arr[i] === item) {
          return true;
        }
      }
      return false;
    }
    return arr.indexOf(item) != -1;
  }

  function scriptHint(editor, keywords, getToken, options) {
    // Find the token at the cursor
    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
    token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;

    // If it's not a 'word-style' token, ignore the token.
    if (!/^[\w$_]*$/.test(token.string)) {
      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: state,
                       type: token.string == "." ? "property" : null};
    }
    // If it is a property, find out what it is a property of.
    while (tprop.type == "property") {
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (tprop.string != ".") return;
      tprop = getToken(editor, Pos(cur.line, tprop.start));
      if (tprop.string == ')') {
        var level = 1;
        do {
          tprop = getToken(editor, Pos(cur.line, tprop.start));
          switch (tprop.string) {
          case ')': level++; break;
          case '(': level--; break;
          default: break;
          }
        } while (level > 0);
        tprop = getToken(editor, Pos(cur.line, tprop.start));
	if (tprop.type.indexOf("variable") === 0)
	  tprop.type = "function";
	else return; // no clue
      }
      if (!context) var context = [];
      context.push(tprop);
    }
    return {list: getCompletions(token, context, keywords, options),
            from: Pos(cur.line, token.start),
            to: Pos(cur.line, token.end)};
  }

  CodeMirror.javascriptHint = function(editor, options) {
    return scriptHint(editor, javascriptKeywords,
                      function (e, cur) {return e.getTokenAt(cur);},
                      options);
  };

  function getCoffeeScriptToken(editor, cur) {
  // This getToken, it is for coffeescript, imitates the behavior of
  // getTokenAt method in javascript.js, that is, returning "property"
  // type and treat "." as indepenent token.
    var token = editor.getTokenAt(cur);
    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
      token.end = token.start;
      token.string = '.';
      token.type = "property";
    }
    else if (/^\.[\w$_]*$/.test(token.string)) {
      token.type = "property";
      token.start++;
      token.string = token.string.replace(/\./, '');
    }
    return token;
  }

  CodeMirror.coffeescriptHint = function(editor, options) {
    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
  };

  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
                     "toUpperCase toLowerCase split concat match replace search").split(" ");
  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
  var funcProps = "prototype apply call bind".split(" ");
  var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
                  "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");

  function getCompletions(token, context, keywords, options) {
    var found = [], start = token.string;
    function maybeAdd(str) {
      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
    }
    function gatherCompletions(obj) {
      if (typeof obj == "string") forEach(stringProps, maybeAdd);
      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
      for (var name in obj) maybeAdd(name);
    }

    if (context) {
      // If this is a property, see if it belongs to some object we can
      // find in the current environment.
      var obj = context.pop(), base;
      if (obj.type.indexOf("variable") === 0) {
        if (options && options.additionalContext)
          base = options.additionalContext[obj.string];
        base = base || window[obj.string];
      } else if (obj.type == "string") {
        base = "";
      } else if (obj.type == "atom") {
        base = 1;
      } else if (obj.type == "function") {
        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
            (typeof window.jQuery == 'function'))
          base = window.jQuery();
        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
          base = window._();
      }
      while (base != null && context.length)
        base = base[context.pop().string];
      if (base != null) gatherCompletions(base);
    }
    else {
      // If not, just look in the window object and any local scope
      // (reading into JS mode internals to get at the local and global variables)
      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
      gatherCompletions(window);
      forEach(keywords, maybeAdd);
    }
    return found;
  }
})();
<|endoftext|>


<commit_before>// Print out subscribers by special country (China, Brazil)

// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>

var countries = ['brazil', 'china'];
countries.forEach(function(country) {
  print('---' + country.toUpperCase() + '---');
  var cursor = db.users.find({country: country, stripe: {$exists: true}}, {email: 1, country: 1, stripe: 1, name: 1});
  var users = [];
  var inactiveUsers = [];
  while (cursor.hasNext()) {
    var user = cursor.next();
    if (!user.stripe.planID) {
      inactiveUsers.push(user);
      continue;
    }
    users.push(user);
    print([user._id, user.country, user.email, user.name, JSON.stringify(user.stripe, null, 0)].join('\t'));
  }
  print('Had', users.length, 'active subscribers and', inactiveUsers.length, 'possible former subscribers in', country, '\n');
});
<commit_msg>Look at subscriptionID instead of planID for checking active subscribers<commit_after>// Print out subscribers by special country (China, Brazil)

// Usage:
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>

var countries = ['brazil', 'china'];
countries.forEach(function(country) {
  print('---' + country.toUpperCase() + '---');
  var cursor = db.users.find({country: country, stripe: {$exists: true}}, {email: 1, country: 1, stripe: 1, name: 1});
  var users = [];
  var inactiveUsers = [];
  while (cursor.hasNext()) {
    var user = cursor.next();
    if (!user.stripe.subscriptionID) {
      inactiveUsers.push(user);
      continue;
    }
    users.push(user);
    print([user._id, user.country, user.email, user.name, JSON.stringify(user.stripe, null, 0)].join('\t'));
  }
  print('Had', users.length, 'active subscribers and', inactiveUsers.length, 'possible former subscribers in', country, '\n');
});
<|endoftext|>


<commit_before>var searchFilter = $('#search-filter').val();
var pageLen = $('#page-len').val();

function createBuildsTable(data) {
    'use strict';

    var localData = data.result,
        table = null;

    table = $('#defconfstable').dataTable({
        'dom': '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"' +
            '<"length-menu"l>>' +
            '<"col-xs-12 col-sm-12 col-md-4 col-lg-4 col-lg-offset-2"f>r' +
            '<"col-xs-12 col-sm-12 col-md-12 col-lg-12"t>>' +
            '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"i>' +
            '<"col-xs-12 col-sm-12 col-md-6 col-lg-6"p>>',
        'language': {
            'lengthMenu': '_MENU_ builds per page',
            'zeroRecords': '
No builds to display.
',
            'search': '
' +
                '' +
                '_INPUT_
'
        },
        'initComplete': function(settings, data) {
            $('#table-loading').remove();
            $('#table-div').fadeIn('slow', 'linear');

            var api = this.api();

            pageLen = Number(pageLen);
            if (isNaN(pageLen)) {
                pageLen = 25;
            }

            api.page.len(pageLen).draw();

            if (searchFilter !== null && searchFilter.length > 0) {
                api.search(searchFilter, true).draw();
            }
        },
        'lengthMenu': [25, 50, 75, 100],
        'deferRender': true,
        'ordering': true,
        'processing': true,
        'stateDuration': -1,
        'stateSave': true,
        'order': [5, 'desc'],
        'search': {
            'regex': true
        },
        'data': localData,
        'columns': [
            {
                'data': '_id',
                'visible': false,
                'searchable': false,
                'orderable': false
            },
            {
                'data': 'job',
                'title': 'Tree ‐ Branch',
                'render': function(data, type, object) {
                    var display = '' + data;

                    if (object.git_branch !== null) {
                        display += ' ‐ ' +
                            object.git_branch + '';
                    }
                    return display + '';
                }
            },
            {
                'data': 'kernel',
                'title': 'Kernel'
            },
            {
                'data': 'defconfig_full',
                'title': 'Defconfig',
                'render': function(data, type, object) {
                    var display = data;
                    if (data.length > 33) {
                        display = '' +
                            data.slice(0, 33) + '…' +
                            '';
                    }
                    return display;
                }
            },
            {
                'data': 'arch',
                'title': 'Arch.'
            },
            {
                'data': 'created_on',
                'title': 'Date',
                'type': 'date',
                'className': 'pull-center',
                'render': function(data) {
                    var created = new Date(data['$date']);
                    return created.getCustomISODate();
                }
            },
            {
                'data': 'status',
                'title': 'Status',
                'type': 'string',
                'className': 'pull-center',
                'render': function(data) {
                    var displ;
                    switch (data) {
                        case 'PASS':
                            displ = '' +
                                '' +
                                '

';
                            break;
                        case 'FAIL':
                            displ = '' +
                                '' +
                                '
' +
                                '
';
                            break;
                        default:
                            displ = '' +
                                '' +
                                '
' +
                                '
';
                            break;
                    }
                    return displ;
                }
            },
            {
                'data': 'job',
                'title': '',
                'orderable': false,
                'searchable': false,
                'className': 'pull-center',
                'render': function(data, type, object) {
                    return '' +
                        '' +
                        '';
                }
            }
        ]
    });

    $(document).on('click', '#defconfstable tbody tr', function() {
        var tableData = table.fnGetData(this),
            location = '#';
        if (tableData) {
            location = '/build/' + tableData.job +
                '/kernel/' + tableData.kernel + '/defconfig/' +
                tableData.defconfig_full;
            if (tableData._id !== null) {
                location += '?_id=' + tableData._id['$oid'];
            }

            window.location = location;
        }
    });

    $('#search-area > .input-sm').attr('placeholder', 'Filter the results');
    $('.input-sm').keyup(function(key) {
        // Remove focus from input when Esc is pressed.
        if (key.keyCode === 27) {
            $(this).blur();
        }
    });
}

function failedAjaxCall() {
    'use strict';
    $('#table-loading').remove();
}

$(document).ready(function() {
    'use strict';

    $('#li-build').addClass('active');

    $('body').tooltip({
        'selector': '[rel=tooltip]',
        'placement': 'auto'
    });

    $('#table-div').hide();

    var ajaxCall = null,
        errorReason = '';

    errorReason = 'Defconfig data call failed.';
    ajaxCall = $.ajax({
        'url': '/_ajax/defconf',
        'traditional': true,
        'cache': true,
        'dataType': 'json',
        'data': {
            'sort': 'created_on',
            'sort_order': -1,
            'date_range': $('#date-range').val(),
            'field': [
                '_id', 'job', 'kernel', 'status',
                'arch', 'created_on', 'git_branch', 'defconfig_full'
            ]
        },
        'timeout': 6000,
        'beforeSend': function(jqXHR) {
            setXhrHeader(jqXHR);
        },
        'error': function() {
            failedAjaxCall();
        },
        'statusCode': {
            403: function() {
                setErrorAlert('build-403-error', 403, errorReason);
            },
            404: function() {
                setErrorAlert('build-404-error', 404, errorReason);
            },
            408: function() {
                errorReason = 'Defconfig data call failed: timeout.';
                setErrorAlert('build-408-error', 408, errorReason);
            },
            500: function() {
                setErrorAlert('build-500-error', 500, errorReason);
            }
        }
    });

    $.when(ajaxCall).then(createBuildsTable, failedAjaxCall);
});
<commit_msg>Refactor using new base lib.<commit_after>var searchFilter = $('#search-filter').val();
var pageLen = $('#page-len').val();

function createBuildsTable(data) {
    'use strict';

    var localData = data.result,
        table = null;

    table = $('#defconfstable').dataTable({
        'dom': '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"' +
            '<"length-menu"l>>' +
            '<"col-xs-12 col-sm-12 col-md-4 col-lg-4 col-lg-offset-2"f>r' +
            '<"col-xs-12 col-sm-12 col-md-12 col-lg-12"t>>' +
            '<"row"<"col-xs-12 col-sm-12 col-md-6 col-lg-6"i>' +
            '<"col-xs-12 col-sm-12 col-md-6 col-lg-6"p>>',
        'language': {
            'lengthMenu': '_MENU_ builds per page',
            'zeroRecords': '
No builds to display.
',
            'search': '
' +
                '' +
                '_INPUT_
'
        },
        'initComplete': function(settings, data) {
            $('#table-loading').remove();
            $('#table-div').fadeIn('slow', 'linear');

            var api = this.api();

            pageLen = Number(pageLen);
            if (isNaN(pageLen)) {
                pageLen = 25;
            }

            api.page.len(pageLen).draw();

            if (searchFilter !== null && searchFilter.length > 0) {
                api.search(searchFilter, true).draw();
            }
        },
        'lengthMenu': [25, 50, 75, 100],
        'deferRender': true,
        'ordering': true,
        'processing': true,
        'stateDuration': -1,
        'stateSave': true,
        'order': [5, 'desc'],
        'search': {
            'regex': true
        },
        'data': localData,
        'columns': [
            {
                'data': '_id',
                'visible': false,
                'searchable': false,
                'orderable': false
            },
            {
                'data': 'job',
                'title': 'Tree ‐ Branch',
                'render': function(data, type, object) {
                    var display = '' + data;

                    if (object.git_branch !== null) {
                        display += ' ‐ ' +
                            object.git_branch + '';
                    }
                    return display + '';
                }
            },
            {
                'data': 'kernel',
                'title': 'Kernel'
            },
            {
                'data': 'defconfig_full',
                'title': 'Defconfig',
                'render': function(data, type, object) {
                    var display = data;
                    if (data.length > 33) {
                        display = '' +
                            data.slice(0, 33) + '…' +
                            '';
                    }
                    return display;
                }
            },
            {
                'data': 'arch',
                'title': 'Arch.'
            },
            {
                'data': 'created_on',
                'title': 'Date',
                'type': 'date',
                'className': 'pull-center',
                'render': function(data) {
                    var created = new Date(data.$date);
                    return created.getCustomISODate();
                }
            },
            {
                'data': 'status',
                'title': 'Status',
                'type': 'string',
                'className': 'pull-center',
                'render': function(data) {
                    var displ;
                    switch (data) {
                        case 'PASS':
                            displ = '' +
                                '' +
                                '

';
                            break;
                        case 'FAIL':
                            displ = '' +
                                '' +
                                '
' +
                                '
';
                            break;
                        default:
                            displ = '' +
                                '' +
                                '
' +
                                '
';
                            break;
                    }
                    return displ;
                }
            },
            {
                'data': 'job',
                'title': '',
                'orderable': false,
                'searchable': false,
                'className': 'pull-center',
                'render': function(data, type, object) {
                    return '' +
                        '' +
                        '';
                }
            }
        ]
    });

    $(document).on('click', '#defconfstable tbody tr', function() {
        var tableData = table.fnGetData(this),
            location = '#';
        if (tableData) {
            location = '/build/' + tableData.job +
                '/kernel/' + tableData.kernel + '/defconfig/' +
                tableData.defconfig_full;
            if (tableData._id !== null) {
                location += '?_id=' + tableData._id.$oid;
            }

            window.location = location;
        }
    });

    $('#search-area > .input-sm').attr('placeholder', 'Filter the results');
    $('.input-sm').keyup(function(key) {
        // Remove focus from input when Esc is pressed.
        if (key.keyCode === 27) {
            $(this).blur();
        }
    });
}

function failedAjaxCall() {
    'use strict';
    $('#table-loading').remove();
}

$(document).ready(function() {
    'use strict';

    $('#li-build').addClass('active');
    $('#table-div').hide();

    var ajaxDeferredCall = null,
        ajaxData = null,
        errorReason = '';

    errorReason = 'Defconfig data call failed';
    ajaxData = {
        'sort': 'created_on',
        'sort_order': -1,
        'date_range': $('#date-range').val(),
        'field': [
            '_id', 'job', 'kernel', 'status',
            'arch', 'created_on', 'git_branch', 'defconfig_full'
        ]
    };
    ajaxDeferredCall = JSBase.createDeferredCall(
        '/_ajax/defconf',
        'GET',
        ajaxData,
        null,
        failedAjaxCall,
        errorReason
    );

    $.when(ajaxDeferredCall).done(createBuildsTable);
});
<|endoftext|>


<commit_before>function patch (gl) {
  function getEnumName (e) {
    for (var name in gl) {
      if (gl[name] === e) return name
    }
    return 'UNDEFINED-' + e
  }

  if (!gl.getContextAttributes) {
    gl.getSupportedExtensions = function () {
      return [
        'EXT_blend_minmax,EXT_sRGB,EXT_frag_depth',
        'OES_texture_float,OES_texture_float_linear',
        'OES_texture_half_float,OES_texture_half_float_linear',
        'OES_standard_derivatives,EXT_shader_texture_lod',
        'EXT_texture_filter_anisotropic,OES_vertex_array_object',
        'OES_element_index_uint',
        'WEBGL_depth_texture,WEBGL_draw_buffers,ANGLE_instanced_arrays',
        'WEBGL_debug_renderer_info'
      ].map(function (s) { return s.toLowerCase() })
    }
    var glPixelStorei = gl.pixelStorei
    var premultAlphaWarnings = 0;
    gl.pixelStorei = function () {
      // this is not supported in Plask so let's print the warning once
      if (arguments[0] === gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL) {
        if (premultAlphaWarnings++ > 0) return
      }
      try {
        glPixelStorei.apply(gl, arguments)
      } catch (e) {
        // console.log(e)
      }
    }
    var glDrawingBufferWidth = gl.drawingBufferWidth
    gl.drawingBufferWidth = function () {
      console.log('drawingBufferWidth', arguments)
      glDrawingBufferWidth.apply(gl, arguments)
    }
    gl.getExtension = function (name) {
      name = name.toLowerCase()
      if (name === 'angle_instanced_arrays') {
        return {
          vertexAttribDivisorANGLE: function () { gl.vertexAttribDivisor.apply(gl, arguments) },
          drawElementsInstancedANGLE: function () { gl.drawElementsInstanced.apply(gl, arguments) }
        }
      }
      if (name === 'oes_texture_float') return {}
      if (name === 'oes_texture_float_linear') return {}
      if (name === 'oes_texture_half_float') {
        return {
          HALF_FLOAT_OES: isBrowser ? 0x8D61 : gl.HALF_FLOAT
        }
      }
      if (name === 'oes_texture_half_float_linear') return {}
      if (name === 'webgl_depth_texture') return {}
      if (name === 'webgl_draw_buffers') {
        return {
          drawBuffersWEBGL: function () {
            // FIXME: first call is throwing error for some reason
            try {
              gl.drawBuffers.apply(gl, arguments)
            } catch (e) {
            }
          }
        }
      }
    }
    var GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3
    var glGetParameter = gl.getParameter
    gl.getParameter = function (name) {
      if (name === GL_COMPRESSED_TEXTURE_FORMATS) return []
      if (name === gl.MAX_TEXTURE_SIZE) return 16384
      if (name === gl.MAX_CUBE_MAP_TEXTURE_SIZE) return 16384
      if (name === gl.MAX_DRAW_BUFFERS) return 4
      if (name === gl.RED_BITS) return 8
      if (name === gl.GREEN_BITS) return 8
      if (name === gl.BLUE_BITS) return 8
      if (name === gl.ALPHA_BITS) return 8
      if (name === gl.DEPTH_BITS) return 24
      if (name === gl.STENCIL_BITS) return 8
      if (name === gl.MAX_COLOR_ATTACHMENTS) return 4
      if (name === gl.MAX_RENDERBUFFER_SIZE) return 4096
      if (name === gl.SUBPIXEL_BITS) return 8
      if (name === gl.ALIASED_POINT_SIZE_RANGE) return 0
      if (name === gl.ALIASED_LINE_WIDTH_RANGE) return 0
      if (name === gl.MAX_VIEWPORT_DIMS) return [ 16384, 16384]
      if (name === gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_VERTEX_ATTRIBS) return 16
      if (name === gl.MAX_VERTEX_UNIFORM_VECTORS) return 16
      if (name === gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_VARYING_VECTORS) return 16
      if (name === gl.MAX_FRAGMENT_UNIFORM_VECTORS) return 16
      if (name === gl.VENDOR) return 'Plask'
      if (name === gl.RENDERER) return 'Plask GL Bindings'
      if (name === gl.VERSION) return 'WebGL 1.0'
      if (name === gl.SHADING_LANGUAGE_VERSION) return 'WebGL 1.0'
      console.log(getEnumName(name) + ' not supported')
      glGetParameter.apply(gl, arguments)
    }
    gl.getContextAttributes = function () {
      return {
        alpha: true,
        antialias: true,
        depth: true,
        failIfMajorPerformanceCaveat: false,
        premultipliedAlpha: true,
        preserveDrawingBuffer: false,
        stencil: false
      }
    }
    gl.isContextLost = function () {
      return false
    }
  }

  gl.getEnumName = getEnumName
  return gl
}

module.exports = patch
<commit_msg>Remove unused overwrite<commit_after>function patch (gl) {
  function getEnumName (e) {
    for (var name in gl) {
      if (gl[name] === e) return name
    }
    return 'UNDEFINED-' + e
  }

  if (!gl.getContextAttributes) {
    gl.getSupportedExtensions = function () {
      return [
        'EXT_blend_minmax,EXT_sRGB,EXT_frag_depth',
        'OES_texture_float,OES_texture_float_linear',
        'OES_texture_half_float,OES_texture_half_float_linear',
        'OES_standard_derivatives,EXT_shader_texture_lod',
        'EXT_texture_filter_anisotropic,OES_vertex_array_object',
        'OES_element_index_uint',
        'WEBGL_depth_texture,WEBGL_draw_buffers,ANGLE_instanced_arrays',
        'WEBGL_debug_renderer_info'
      ].map(function (s) { return s.toLowerCase() })
    }
    var glPixelStorei = gl.pixelStorei
    var premultAlphaWarnings = 0;
    gl.pixelStorei = function () {
      // this is not supported in Plask so let's print the warning once
      if (arguments[0] === gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL) {
        if (premultAlphaWarnings++ > 0) return
      }
      try {
        glPixelStorei.apply(gl, arguments)
      } catch (e) {
        // console.log(e)
      }
    }
    gl.getExtension = function (name) {
      name = name.toLowerCase()
      if (name === 'angle_instanced_arrays') {
        return {
          vertexAttribDivisorANGLE: function () { gl.vertexAttribDivisor.apply(gl, arguments) },
          drawElementsInstancedANGLE: function () { gl.drawElementsInstanced.apply(gl, arguments) }
        }
      }
      if (name === 'oes_texture_float') return {}
      if (name === 'oes_texture_float_linear') return {}
      if (name === 'oes_texture_half_float') {
        return {
          HALF_FLOAT_OES: isBrowser ? 0x8D61 : gl.HALF_FLOAT
        }
      }
      if (name === 'oes_texture_half_float_linear') return {}
      if (name === 'webgl_depth_texture') return {}
      if (name === 'webgl_draw_buffers') {
        return {
          drawBuffersWEBGL: function () {
            // FIXME: first call is throwing error for some reason
            try {
              gl.drawBuffers.apply(gl, arguments)
            } catch (e) {
            }
          }
        }
      }
    }
    var GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3
    var glGetParameter = gl.getParameter
    gl.getParameter = function (name) {
      if (name === GL_COMPRESSED_TEXTURE_FORMATS) return []
      if (name === gl.MAX_TEXTURE_SIZE) return 16384
      if (name === gl.MAX_CUBE_MAP_TEXTURE_SIZE) return 16384
      if (name === gl.MAX_DRAW_BUFFERS) return 4
      if (name === gl.RED_BITS) return 8
      if (name === gl.GREEN_BITS) return 8
      if (name === gl.BLUE_BITS) return 8
      if (name === gl.ALPHA_BITS) return 8
      if (name === gl.DEPTH_BITS) return 24
      if (name === gl.STENCIL_BITS) return 8
      if (name === gl.MAX_COLOR_ATTACHMENTS) return 4
      if (name === gl.MAX_RENDERBUFFER_SIZE) return 4096
      if (name === gl.SUBPIXEL_BITS) return 8
      if (name === gl.ALIASED_POINT_SIZE_RANGE) return 0
      if (name === gl.ALIASED_LINE_WIDTH_RANGE) return 0
      if (name === gl.MAX_VIEWPORT_DIMS) return [ 16384, 16384]
      if (name === gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_VERTEX_ATTRIBS) return 16
      if (name === gl.MAX_VERTEX_UNIFORM_VECTORS) return 16
      if (name === gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) return 16
      if (name === gl.MAX_VARYING_VECTORS) return 16
      if (name === gl.MAX_FRAGMENT_UNIFORM_VECTORS) return 16
      if (name === gl.VENDOR) return 'Plask'
      if (name === gl.RENDERER) return 'Plask GL Bindings'
      if (name === gl.VERSION) return 'WebGL 1.0'
      if (name === gl.SHADING_LANGUAGE_VERSION) return 'WebGL 1.0'
      console.log(getEnumName(name) + ' not supported')
      glGetParameter.apply(gl, arguments)
    }
    gl.getContextAttributes = function () {
      return {
        alpha: true,
        antialias: true,
        depth: true,
        failIfMajorPerformanceCaveat: false,
        premultipliedAlpha: true,
        preserveDrawingBuffer: false,
        stencil: false
      }
    }
    gl.isContextLost = function () {
      return false
    }
  }

  gl.getEnumName = getEnumName
  return gl
}

module.exports = patch
<|endoftext|>


<commit_before>// DO NOT DELETE OR MODIFY THIS FILE :)

import angular from 'angular';

const app = angular.module('app', []);

// Lazy loading hotness
const addModules = function(_modules) {

  let modules = _modules;
  if (!Array.isArray(modules)) {
    modules = [modules];
  }

  modules.forEach(module => {
    const contains = app.requires.includes(module);
    if (!contains) {
      app.requires.push(module);
    }
  });

  return app;

};

app.addModule = addModules;
app.addModules = addModules;

export default app;
<commit_msg>fix IE Angualr modules<commit_after>// DO NOT DELETE OR MODIFY THIS FILE :)

import angular from 'angular';

const app = angular.module('app', []);

// Lazy loading hotness
const addModules = function(_modules) {

  let modules = _modules;
  if (!Array.isArray(modules)) {
    modules = [modules];
  }

  angular.forEach(modules, module => {
    const index = app.requires.indexOf(module);
    if (index === -1) {
      app.requires.push(module);
    }
  });

  return app;

};

app.addModule = addModules;
app.addModules = addModules;

export default app;
<|endoftext|>


<commit_before>/**
 * Created by msyk on 15/10/09.
 */

var slideNumber = 0;
var currentId = 0;
var slideNodes = null;

window.onload = function () {
    var nodeSlide = document.getElementsByClassName("slide")[0];
    slideNodes = nodeSlide.cloneNode(true);
    nodeSlide.parentNode.removeChild(nodeSlide);

    if (document.location.hash.length > 0) {
        slideNumber = parseInt(document.location.hash.substr(1)) - 1;
    }

    var headNode = document.getElementsByTagName("HEAD")[0];
    var linkNode = document.createElement("LINK");
    linkNode.setAttribute("rel", "stylesheet");
    linkNode.setAttribute("href", "projector/themes/" + config.theme + "/slidestyle.css");
    headNode.appendChild(linkNode);
    setTimeout(function () {
        adjust(loadSlide(true, document.body));
    }, 100);

    window.onclick = function (e) {
        if (e.clientX > window.innerWidth / 2) {
            moveSlide(+1);
        } else {
            moveSlide(-1);
        }
    };

    document.body.onkeydown = function (e) {
        var url;
        switch (e.keyCode) {
            case 37:
            case 38:
                if (!e.shiftKey) {
                    moveSlide(-1);
                } else {
                    moveSlide(0, true);
                }                break;
            case 39:
            case 40:
                if (!e.shiftKey) {
                    moveSlide(+1);
                } else {
                    moveSlide(-1, true);
                }                break;
            case 49:    // keyCode = 49 for "1"
                moveSlide(0, true);
                break;
            case 69:    // keyCode = 69 for "E"
                moveSlide(-1, true);
                break;
            case 70:    // keyCode = 70 for "F"
                var element = document.documentElement;
                if (element.requestFullscreen) {
                    element.requestFullscreen();
                } else if (element.msRequestFullscreen) {
                    element.msRequestFullscreen();
                } else if (element.mozRequestFullScreen) {
                    element.mozRequestFullScreen();
                } else if (element.webkitRequestFullscreen) {
                    element.webkitRequestFullscreen(element.ALLOW_KEYBOARD_INPUT);
                }
                break;
            case 82:    // keyCode = 82 for "R"
            case 13:    // keyCode = 13 for Return
                url = document.URL;
                if (url.match(/#/)) {
                    url = url.split(/#/)[0];
                }
                document.location.href = url + "#" + (slideNumber + 1);
                location.reload();
                break;
        }
        e.preventDefault();
    };

    window.onresize = function () {
        adjust();
    };

    window.onhashchange = function () {
		location.reload();
    };

}
function moveSlide(bias, absolute) {
    var slide;
    if (absolute)   {
        slideNumber = bias;
        if (bias < 0)   {
            slideNumber = contents.length + bias;
        }
    }
    else {
        slideNumber += bias;
    }
    if (slideNumber >= contents.length) {
        slideNumber = contents.length - 1;
    } else if (slideNumber < 0) {
        slideNumber = 0;
    } else {
        slide = loadSlide(false, document.body);

        url = document.URL;
        if (url.match(/#/)) {
            url = url.split(/#/)[0];
        }
        document.location.href = url + "#" + (slideNumber + 1);

        setTimeout(function () {
            adjust(slide);
        }, 100);
    }
}
<commit_msg>Refactoring<commit_after>/**
 * Created by msyk on 15/10/09.
 */

var slideNumber = 0;
var currentId = 0;
var slideNodes = null;

window.onload = function () {
    var nodeSlide, headNode, linkNode;

    nodeSlide = document.getElementsByClassName("slide")[0];
    slideNodes = nodeSlide.cloneNode(true);
    nodeSlide.parentNode.removeChild(nodeSlide);

    if (document.location.hash.length > 0) {
        slideNumber = parseInt(document.location.hash.substr(1)) - 1;
    }

    headNode = document.getElementsByTagName("HEAD")[0];
    linkNode = document.createElement("LINK");
    linkNode.setAttribute("rel", "stylesheet");
    linkNode.setAttribute("href", "projector/themes/" + config.theme + "/slidestyle.css");
    headNode.appendChild(linkNode);

    setTimeout(function () {
        adjust(loadSlide(true, document.body));
    }, 100);
};

window.onclick = function (e) {
    if (e.clientX > window.innerWidth / 2) {
        moveSlide(+1);
    } else {
        moveSlide(-1);
    }
};

window.onkeydown = function (e) {
    switch (e.keyCode) {
        case 37:
        case 38:
            if (!e.shiftKey) {
                moveSlide(-1);
            } else {
                moveSlide(0, true);
            }
            break;
        case 39:
        case 40:
            if (!e.shiftKey) {
                moveSlide(+1);
            } else {
                moveSlide(-1, true);
            }
            break;
        case 49:    // keyCode = 49 for "1"
            moveSlide(0, true);
            break;
        case 69:    // keyCode = 69 for "E"
            moveSlide(-1, true);
            break;
        case 70:    // keyCode = 70 for "F"
            fullScreen();
            break;
        case 82:    // keyCode = 82 for "R"
        case 13:    // keyCode = 13 for Return
            setupURL();
            location.reload();
            break;
    }
    e.preventDefault();
};

window.onresize = function () {
    adjust();
};

window.onhashchange = function () {
    location.reload();
};

function moveSlide(bias, absolute) {
    var slide;
    if (absolute) {
        slideNumber = bias;
        if (bias < 0) {
            slideNumber = contents.length + bias;
        }
    }
    else {
        slideNumber += bias;
    }
    if (slideNumber >= contents.length) {
        slideNumber = contents.length - 1;
    } else if (slideNumber < 0) {
        slideNumber = 0;
    } else {
        slide = loadSlide(false, document.body);
        setupURL();

        setTimeout(function () {
            adjust(slide);
        }, 100);
    }
}

function setupURL() {
    var url;

    url = document.URL;
    if (url.match(/#/)) {
        url = url.split(/#/)[0];
    }
    document.location.href = url + "#" + (slideNumber + 1);
}

function fullScreen() {
    console.log("#");
    var element = document.documentElement;
    if (element.requestFullscreen) {
        element.requestFullscreen();
    } else if (element.msRequestFullscreen) {
        element.msRequestFullscreen();
    } else if (element.mozRequestFullScreen) {
        element.mozRequestFullScreen();
    } else if (element.webkitRequestFullscreen) {
        element.webkitRequestFullscreen(element.ALLOW_KEYBOARD_INPUT);
    }
}<|endoftext|>


<commit_before>//
// angular-file-uploader factory
// ===========================
//
// Factory to support file upload with progress status on Angular.js.
//
// You can upload files with `$http.post` and a `FormData` object but
// don't get progress update on it.
//
(function() {
  'use strict';

  angular.module('angular-file-uploader')
    .factory('angular-file-uploader', [
      '$q',
      '$rootScope',
      function($q, $rootScope) {
        return {
          //
          // Upload a file
          // -------------
          //
          // ### Arguments
          //
          // + `url` - `String` with the URI end-point.
          // + `files` - HTML file object, can be an `Array`
          // + `data` - (Optional) `JSON` with extra data to be sent
          // + `callback` - (Optional) `Callback` function that returns
          // the progress.
          //
          // ### E.g
          //
          // ```
          // $upload.send('/end-point', fileArray);
          // ```
          //
          send: function(url, files, data, callback) {

            var deferred = $q.defer();

            var xhr = new XMLHttpRequest();

            xhr.upload.onprogress = function (event) {
              $rootScope.apply(function () {
                var percent;

                if (event.lengthComputable) {
                  percent = Math.round(event.loaded / (event.total * 100));

                  if (callback != null) {
                    callback(percent);
                  } else if (deferred.notify) {
                    deferred.notify(percent);
                  }
                }
              });
            };

            xhr.onload = function () {
              $rootScope.apply(function () {
                deferred.resolve({
                  files: files,
                  data: angular.fromJson(xhr.responseText)
                });
              });
            };

            xhr.upload.onerror = function () {
              var msg = xhr.responseText ? xhr.responseText : 'Error occurred uploading to ' + url;

              $rootScope.apply(function () {
                deferred.reject(msg);
              });
            };

            var fd = new FormData();

            //
            // If there are no files then `$http.post` should
            // be used instead.
            //
            if (files != null || !files.length) {
              deferred.reject('There\'s no files to upload');
              return;
            }

            //
            // There's data to be added to the payload on this upload?
            //
            if (data) {
              Object.keys(data).forEach(function (element) {
                fd.append(element, data[element]);
              });
            }

            //
            // Load the files in the formData object
            //
            for (var i = 0; i < files.length; i++) {
              fd.append(files[i].name, files[i]);
            }

            //
            // Send the files
            //
            xhr.open('POST', url);

            xhr.send(fd);

            return deferred.promise;

          }
        };
      }
    ]);
}).call(this);
<commit_msg>refactor so it actually loads the module<commit_after>//
// angular-file-uploader factory
// ===========================
//
// Factory to support file upload with progress status on Angular.js.
//
// You can upload files with `$http.post` and a `FormData` object but
// don't get progress update on it.
//
(function() {
  'use strict';

  angular.module('angular-file-uploader', [])

    .factory('angularFileUploader', [
      '$q',
      '$rootScope',
      function($q, $rootScope) {
        return {
          //
          // Upload a file
          // -------------
          //
          // ### Arguments
          //
          // + `url` - `String` with the URI end-point.
          // + `files` - HTML file object, can be an `Array`
          // + `data` - (Optional) `JSON` with extra data to be sent
          // + `callback` - (Optional) `Callback` function that returns
          // the progress.
          //
          // ### E.g
          //
          // ```
          // $upload.send('/end-point', fileArray);
          // ```
          //
          send: function(url, files, data, callback) {

            var deferred = $q.defer();

            var xhr = new XMLHttpRequest();

            xhr.upload.onprogress = function (event) {
              $rootScope.apply(function () {
                var percent;

                if (event.lengthComputable) {
                  percent = Math.round(event.loaded / (event.total * 100));

                  if (callback != null) {
                    callback(percent);
                  } else if (deferred.notify) {
                    deferred.notify(percent);
                  }
                }
              });
            };

            xhr.onload = function () {
              $rootScope.apply(function () {
                deferred.resolve({
                  files: files,
                  data: angular.fromJson(xhr.responseText)
                });
              });
            };

            xhr.upload.onerror = function () {
              var msg = xhr.responseText ? xhr.responseText : 'Error occurred uploading to ' + url;

              $rootScope.apply(function () {
                deferred.reject(msg);
              });
            };

            var fd = new FormData();

            //
            // If there are no files then `$http.post` should
            // be used instead.
            //
            if (files != null || !files.length) {
              deferred.reject('There\'s no files to upload');
              return;
            }

            //
            // There's data to be added to the payload on this upload?
            //
            if (data) {
              Object.keys(data).forEach(function (element) {
                fd.append(element, data[element]);
              });
            }

            //
            // Load the files in the formData object
            //
            for (var i = 0; i < files.length; i++) {
              fd.append(files[i].name, files[i]);
            }

            //
            // Send the files
            //
            xhr.open('POST', url);

            xhr.send(fd);

            return deferred.promise;

          }
        };
      }
    ]);
}).call(this);
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2010 Evin Grano
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

sc_require('models/record');
sc_require('models/record_attribute');

/** @class

  ChildAttribute is a subclass of RecordAttribute and handles to-one
  relationship for child record

  When setting ( .set() ) the value of a toMany attribute, make sure
  to pass in an array of SC.Record objects.

  There are many ways you can configure a ManyAttribute:

  {{{
    contacts: SC.ChildAttribute.attr('SC.Child');
  }}}

  @extends SC.RecordAttribute
  @since SproutCore 1.0
*/
SC.ChildAttribute = SC.RecordAttribute.extend(
  /** @scope SC.ChildAttribute.prototype */ {

  isNestedRecordTransform: YES,

  // ..........................................................
  // LOW-LEVEL METHODS
  //

  /**  @private - adapted for to one relationship */
  toType: function(record, key, value) {
    var ret   = null, rel,
        recordType  = this.get('typeClass');

    if (!record) {
      throw 'SC.Child: Error during transform: Unable to retrieve parent record.';
    }
    if (!SC.none(value)) ret = record.registerNestedRecord(value, key);

    return ret;
  },

  // Default fromType is just returning itself
  fromType: function(record, key, value){
    var sk, store, ret;
    if (record){
      ret = record.registerNestedRecord(value, key);
      if (ret) {
        sk = ret.get('storeKey');
        store = ret.get('store');
        record.writeAttribute(key, store.readDataHash(sk));
      }
      else if (value) {
        record.writeAttribute(key, value);
      }
    }

    return ret;
  },

  /**
    The core handler.  Called from the property.
    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value if called as a setter
    @returns {Object} property value
  */
  call: function(record, key, value) {
    var attrKey = this.get('key') || key, cRef,
        cacheKey = SC.keyFor('__kid__', SC.guidFor(this));
    if (value !== undefined) {
      // this.orphan(record, cacheKey, value);
      value = this.fromType(record, key, value) ; // convert to attribute.
      // record[cacheKey] = value;
    } else {
      value = record.readAttribute(attrKey);
      if (SC.none(value) && (value = this.get('defaultValue'))) {
        if (typeof value === SC.T_FUNCTION) {
          value = this.defaultValue(record, key, this);
          // write default value so it doesn't have to be executed again
          if(record.attributes()) record.writeAttribute(attrKey, value, true);
        }
      } else value = this.toType(record, key, value);
    }

    return value ;
  }
});

<commit_msg>Fixed bug preventing null values from being set on SC.ChildAttributes.<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2010 Evin Grano
// License:   Licensed under MIT license (see license.js)
// ==========================================================================

sc_require('models/record');
sc_require('models/record_attribute');

/** @class

  ChildAttribute is a subclass of RecordAttribute and handles to-one
  relationship for child record

  When setting ( .set() ) the value of a toMany attribute, make sure
  to pass in an array of SC.Record objects.

  There are many ways you can configure a ManyAttribute:

  {{{
    contacts: SC.ChildAttribute.attr('SC.Child');
  }}}

  @extends SC.RecordAttribute
  @since SproutCore 1.0
*/
SC.ChildAttribute = SC.RecordAttribute.extend(
  /** @scope SC.ChildAttribute.prototype */ {

  isNestedRecordTransform: YES,

  // ..........................................................
  // LOW-LEVEL METHODS
  //

  /**  @private - adapted for to one relationship */
  toType: function(record, key, value) {
    var ret   = null, rel,
        recordType  = this.get('typeClass');

    if (!record) {
      throw 'SC.Child: Error during transform: Unable to retrieve parent record.';
    }
    if (!SC.none(value)) ret = record.registerNestedRecord(value, key);

    return ret;
  },

  // Default fromType is just returning itself
  fromType: function(record, key, value) {
    var sk, store, ret;

    if (record) {
      if (SC.none(value)) {
        // Handle null value.
        record.writeAttribute(key, value);
        ret = value;
      } else {
        // Register the nested record with this record (the parent).
        ret = record.registerNestedRecord(value, key);

        if (ret) {
          // Write the data hash of the nested record to the store.
          sk = ret.get('storeKey');
          store = ret.get('store');
          record.writeAttribute(key, store.readDataHash(sk));
        } else if (value) {
          // If registration failed, just write the value.
          record.writeAttribute(key, value);
        }
      }
    }

    return ret;
  },

  /**
    The core handler.  Called from the property.
    @param {SC.Record} record the record instance
    @param {String} key the key used to access this attribute on the record
    @param {Object} value the property value if called as a setter
    @returns {Object} property value
  */
  call: function(record, key, value) {
    var attrKey = this.get('key') || key, cRef,
        cacheKey = SC.keyFor('__kid__', SC.guidFor(this));
    if (value !== undefined) {
      // this.orphan(record, cacheKey, value);
      value = this.fromType(record, key, value) ; // convert to attribute.
      // record[cacheKey] = value;
    } else {
      value = record.readAttribute(attrKey);
      if (SC.none(value) && (value = this.get('defaultValue'))) {
        if (typeof value === SC.T_FUNCTION) {
          value = this.defaultValue(record, key, this);
          // write default value so it doesn't have to be executed again
          if(record.attributes()) record.writeAttribute(attrKey, value, true);
        }
      } else value = this.toType(record, key, value);
    }

    return value ;
  }
});

<|endoftext|>


<commit_before>core.Class("unify.ui.container.VirtualScroll", {
	include : [unify.ui.container.Scroll],

	construct : function(widgetFactory, widgetUpdater, prerenderRows) {
		unify.ui.container.Scroll.call(this, new unify.ui.layout.Basic());

		this.addListener("scroll", this.__onVirtualScroll, this);

		this.__widgetFactory = widgetFactory;
		this.__widgetUpdater = widgetUpdater;

		this.__prerenderRows = prerenderRows || 0;

		this.__widgetPool = [];
		this.__widgetMap = [];

		this.__debouncedReflow = core.Function.debounce(this.__reflow, 10);

		this.addListener("resize", function() {
			this.__debouncedReflow();
		}, this);
	},

	properties : {
		model : {
			init: null,
			nullable: true,
			apply: function(value) { this.__applyModel(value); },
			fire: "changeModel"
		},

		columns : {
			init: 1,
			type: "Integer",
			apply: function(value) { this.__applyColumns(value); }
		},

		offsetX : {
			init: 0,
			apply : function() { this.__debouncedReflow(); }
		},

		offsetY : {
			init: 0,
			apply : function() { this.__debouncedReflow(); }
		},

		elementWidth : {
			init: 50,
			apply: function() { this.__debouncedReflow(); }
		},

		elementHeight : {
			init: 100,
			apply: function() { this.__debouncedReflow(); }
		}
	},

	events : {
		changeModel: core.event.Simple
	},

	members : {
		__prerenderRows : 0,
		__widgetPool : null,
		__widgetFactory : null,
		__widgetUpdater : null,
		__widgetMap : null,
		__maximalRows : 0,
		__isInit : null,

		getCurrentWidgets : function() {
			return this.__widgetMap;
		},

		__applyModel : function(model) {
			var rows = Math.ceil(model.length / this.getColumns());
			this.getChildrenContainer().setHeight(rows * this.getElementHeight());
			this.__debouncedReflow();
		},

		__applyColumns : function(columns) {
			var model = this.getModel();
			if (model && columns > 0) {
				var rows = Math.ceil(model.length / columns);
				this.getChildrenContainer().setHeight(rows * this.getElementHeight());
				this.__debouncedReflow();
			}
		},

		__debouncedReflow : function() { this.__reflow(); },

		__reflow : function() {
			var model = this.getModel();
			if (!model) {
				return;
			}
			this.__isInit = true;

			this.__maximalRows = Math.ceil(model.length / this.getColumns());

			var widgetMap = this.__widgetMap;
			for (var row in widgetMap) {
				var widgetRow = widgetMap[row];
				for (var col in widgetRow) {
					this.__pushWidgetToPool(widgetRow[col]);
				}
			}
			this.__widgetMap = [];

			this.__onVirtualScroll();
		},

		__oldOffsetX : null,
		__oldOffsetY : null,

		__onVirtualScroll : function() {
			if (!this.__isInit) {
				return;
			}

			var currentPos = this.getScrollTop();
			if(currentPos < 0 || currentPos > this.getContentHeight()){
				return; // no need to do anything on overshoot
			}

			var currentHeight = this._getClientHeight();

			var widgetMap = this.__widgetMap;

			var maxElementHeight = this.getElementHeight();
			var startRow = Math.floor(currentPos / maxElementHeight) - this.__prerenderRows;
			var endRow = Math.floor((currentPos + currentHeight) / maxElementHeight) + this.__prerenderRows;

			if (startRow < 0) {
				startRow = 0;
			}
			var maxEndRow = this.__maximalRows;
			if (endRow >= maxEndRow) {
				endRow = maxEndRow-1;
			}

			// Move all widgets not shown anymore to widget pool
			for (var row in widgetMap) {
				if (row < startRow || row > endRow) {
					var widgets = widgetMap[row];
					delete widgetMap[row];
					for (var col in widgets) {
						this.__pushWidgetToPool(widgets[col]);
					}
				}
			}

			var offsetX = this.getOffsetX();
			var offsetY = this.getOffsetY();
			var changedOffset = false;

			if ((offsetX != this.__oldOffsetX) || (offsetY != this.__oldOffsetY)) {
				this.__oldOffsetX = offsetX;
				this.__oldOffsetY = offsetY;

				changedOffset = true;
			}

			var elementWidth = this.getElementWidth();
			var elementHeight = this.getElementHeight();

			// If new rows appears that have no rendered widgets, do it
			for (var row = startRow; row <= endRow; row++) {
				var columns = this.getColumns();
				for (var i=0; i<columns; i++) {
					if ((!widgetMap[row]) || (!widgetMap[row][i])) {
						this.__createWidget(i, row, offsetX, offsetY);
					} else if (changedOffset) {
						var widget = widgetMap[row][i];
						var left = i*elementWidth+offsetX;
						var top = row*elementHeight+offsetY;
						widget.overrideLayoutTransform(left, top);
					}
				}
			}

		},

		__createWidget : function(column, row, offsetX, offsetY) {
			var widgetConfig = this.__getWidgetFromPool();
			var widget = widgetConfig.widget;

			if (!widgetConfig.fromPool) {
				this.add(widget);
				widget.forceAccelerated();
			}

			var left = column*this.getElementWidth()+offsetX;
			var top = row*this.getElementHeight()+offsetY;
			widget.overrideLayoutTransform(left, top);

			if (!this.__widgetMap[row]) {
				this.__widgetMap[row] = {};
			}
			this.__widgetMap[row][column] = widget;

			var pos = row * this.getColumns() + column;
			var model = this.getModel();

			if (pos >= model.length) {
				//widget.hide();
				widget.overrideLayoutTransform(0, -100000);
			} else {
				//widget.show();
				//this.__widgetUpdater.lazy(this, widget, column, row, model[pos]);
				var widgetUpdater = lowland.Function.curryBind(this.__widgetUpdater, this, widget, column, row, model[pos]);
				core.Function.immediate(widgetUpdater);
			}
		},

		__getWidgetFromPool : function() {
			var widgetPool = this.__widgetPool;
			if (widgetPool.length > 0) {
				return {
					widget: widgetPool.pop(),
					fromPool: true
				};
			} else {
				return {
					widget: this.__widgetFactory(),
					fromPool: false
				};
			}
		},

		__pushWidgetToPool : function(widget) {
			widget.overrideLayoutTransform(0, -100000);
			this.__widgetPool.push(widget);
			this.__widgetUpdater(widget, null, null, false);
		}
	}
});
<commit_msg>added lookup for existing widget/model combinations to speed up layout process while scrolling<commit_after>core.Class("unify.ui.container.VirtualScroll", {
	include : [unify.ui.container.Scroll],

	construct : function(widgetFactory, widgetUpdater, prerenderRows) {
		unify.ui.container.Scroll.call(this, new unify.ui.layout.Basic());

		this.addListener("scroll", this.__onVirtualScroll, this);

		this.__widgetFactory = widgetFactory;
		this.__widgetUpdater = widgetUpdater;

		this.__prerenderRows = prerenderRows || 0;

		this.__widgetPool = [];
		this.__widgetMap = [];
		this.__modelHashes = {};

		this.__debouncedReflow = core.Function.debounce(this.__reflow, 10);

		this.addListener("resize", function() {
			this.__debouncedReflow();
		}, this);
	},

	properties : {
		model : {
			init: null,
			nullable: true,
			apply: function(value) { this.__applyModel(value); },
			fire: "changeModel"
		},

		columns : {
			init: 1,
			type: "Integer",
			apply: function(value) { this.__applyColumns(value); }
		},

		offsetX : {
			init: 0,
			apply : function() { this.__debouncedReflow(); }
		},

		offsetY : {
			init: 0,
			apply : function() { this.__debouncedReflow(); }
		},

		elementWidth : {
			init: 50,
			apply: function() { this.__debouncedReflow(); }
		},

		elementHeight : {
			init: 100,
			apply: function() { this.__debouncedReflow(); }
		}
	},

	events : {
		changeModel: core.event.Simple
	},

	members : {
		__prerenderRows : 0,
		__widgetPool : null,
		__widgetFactory : null,
		__widgetUpdater : null,
		__widgetMap : null,
		__maximalRows : 0,
		__isInit : null,

		/** Map used to find requested model/widget combinations to speed up model layout */
		__modelHashes : null,

		getCurrentWidgets : function() {
			return this.__widgetMap;
		},

		__applyModel : function(model) {
			var rows = Math.ceil(model.length / this.getColumns());
			this.getChildrenContainer().setHeight(rows * this.getElementHeight());
			this.__debouncedReflow();
		},

		__applyColumns : function(columns) {
			var model = this.getModel();
			if (model && columns > 0) {
				var rows = Math.ceil(model.length / columns);
				this.getChildrenContainer().setHeight(rows * this.getElementHeight());
				this.__debouncedReflow();
			}
		},

		__debouncedReflow : function() { this.__reflow(); },

		__reflow : function() {
			var model = this.getModel();
			if (!model) {
				return;
			}
			this.__isInit = true;

			this.__maximalRows = Math.ceil(model.length / this.getColumns());

			var widgetMap = this.__widgetMap;
			for (var row in widgetMap) {
				var widgetRow = widgetMap[row];
				for (var col in widgetRow) {
					this.__pushWidgetToPool(widgetRow[col]);
				}
			}
			this.__widgetMap = [];

			this.__onVirtualScroll();
		},

		__oldOffsetX : null,
		__oldOffsetY : null,

		__onVirtualScroll : function() {
			if (!this.__isInit) {
				return;
			}
			var row;
			var currentPos = this.getScrollTop();
			if(currentPos < 0 || currentPos > this.getContentHeight()){
				return; // no need to do anything on overshoot
			}

			var currentHeight = this._getClientHeight();

			var widgetMap = this.__widgetMap;

			var maxElementHeight = this.getElementHeight();
			var startRow = Math.floor(currentPos / maxElementHeight) - this.__prerenderRows;
			var endRow = Math.floor((currentPos + currentHeight) / maxElementHeight) + this.__prerenderRows;

			if (startRow < 0) {
				startRow = 0;
			}
			var maxEndRow = this.__maximalRows;
			if (endRow >= maxEndRow) {
				endRow = maxEndRow-1;
			}

			// Move all widgets not shown anymore to widget pool
			for (row in widgetMap) {
				if (row < startRow || row > endRow) {
					var widgets = widgetMap[row];
					delete widgetMap[row];
					for (var col in widgets) {
						this.__pushWidgetToPool(widgets[col]);
					}
				}
			}

			var offsetX = this.getOffsetX();
			var offsetY = this.getOffsetY();
			var changedOffset = false;

			if ((offsetX != this.__oldOffsetX) || (offsetY != this.__oldOffsetY)) {
				this.__oldOffsetX = offsetX;
				this.__oldOffsetY = offsetY;

				changedOffset = true;
			}

			var elementWidth = this.getElementWidth();
			var elementHeight = this.getElementHeight();

			// If new rows appears that have no rendered widgets, do it
			for (row = startRow; row <= endRow; row++) {
				var columns = this.getColumns();
				for (var i=0; i<columns; i++) {
					if ((!widgetMap[row]) || (!widgetMap[row][i])) {
						this.__createWidget(i, row, offsetX, offsetY);
					} else if (changedOffset) {
						var widget = widgetMap[row][i];
						var left = i*elementWidth+offsetX;
						var top = row*elementHeight+offsetY;
						widget.overrideLayoutTransform(left, top);
					}
				}
			}

		},

		__createWidget : function(column, row, offsetX, offsetY) {
			var pos = row * this.getColumns() + column;
			var model = this.getModel();
			var widgetConfig = this.__getWidgetFromPool(model[pos]);
			var widget = widgetConfig.widget;
			if (!widgetConfig.fromPool) {
				this.add(widget);
				widget.forceAccelerated();
			}

			var left = column*this.getElementWidth()+offsetX;
			var top = row*this.getElementHeight()+offsetY;
			widget.overrideLayoutTransform(left, top);

			if (!this.__widgetMap[row]) {
				this.__widgetMap[row] = {};
			}
			this.__widgetMap[row][column] = widget;

			if (pos >= model.length) {
				//widget.hide();
				widget.overrideLayoutTransform(0, -100000);
			} else {
				//widget.show();
				//this.__widgetUpdater.lazy(this, widget, column, row, model[pos]);
				var widgetUpdater = lowland.Function.curryBind(this.__widgetUpdater, this, widget, column, row, model[pos]);
				core.Function.immediate(widgetUpdater);
			}
		},

		__getWidgetFromPool : function(model) {
			var widgetPool = this.__widgetPool;
			var adler32 = core.crypt.Adler32;
			var modelHash = adler32.checksum(JSON.stringify(model));
			var hashMap = this.__modelHashes;
			var widget;
			var widgetHash;

			if (widgetPool.length > 0) {

				//Here we check if there is a widget already defined with this model
				if(hashMap[modelHash]){

					//now get the corresponding widget from the array...urgs
					for(var i = 0; i<widgetPool.length; i++){
						if(lowland.ObjectManager.getHash(widgetPool[i]) === hashMap[modelHash]){
							return {
								//get widget and remove item from array
								widget: widgetPool.splice(i,1)[0],
								fromPool: true
							};
						}
					}
					//Iterated but no luck ? Return any widget and(!) delete entry from hashmap
					hashMap[modelHash] = null;

					//Of course, save the hash for this one...might come handy later
					widget = widgetPool.pop();
					widgetHash = lowland.ObjectManager.getHash(widget);
					hashMap[modelHash] = widgetHash;
					return {
						widget: widget,
						fromPool: true
					};

				//No widget with this model. Fine, take any widget!
				} else {

					//Save the hash for later use
					widget = widgetPool.pop();
					widgetHash = lowland.ObjectManager.getHash(widget);
					hashMap[modelHash] = widgetHash;

					return {
						widget: widget,
						fromPool: true
					};
				}

			//There is no widget ? Create one !
			} else {
				widget = this.__widgetFactory();
				widgetHash = lowland.ObjectManager.getHash(widget);
				//Store the modelhash as key for the widgethash which already has this model
				hashMap[modelHash] = widgetHash;
				return {
					widget: widget,
					fromPool: false
				};
			}
		},

		__pushWidgetToPool : function(widget) {
			widget.overrideLayoutTransform(0, -100000);
			this.__widgetPool.push(widget);
			this.__widgetUpdater(widget, null, null, false);
		}
	}
});
<|endoftext|>