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

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'UserAndGroupAuthService',
  flags: ['java'],

  implements: [
    'foam.nanos.auth.AuthService',
    'foam.nanos.NanoService'
  ],

  imports: [
    'localGroupDAO',
    'localSessionDAO',
    'localUserDAO'
  ],

  javaImports: [
    'foam.dao.DAO',
    'foam.nanos.logger.Logger',
    'foam.nanos.session.Session',
    'foam.util.Email',
    'foam.util.Password',
    'foam.util.SafetyUtil',

    'java.util.Calendar',
    'java.util.regex.Pattern',
    'javax.security.auth.AuthPermission',

    'static foam.mlang.MLang.AND',
    'static foam.mlang.MLang.EQ'
  ],

  constants: [
    {
      name: 'PASSWORD_VALIDATE_REGEX',
      type: 'String',
      value: '^.{6,}$'
    },
    {
      name: 'PASSWORD_VALIDATION_ERROR_MESSAGE',
      type: 'String',
      value: 'Password must be at least 6 characters long.'
    },
    {
      name: 'CHECK_USER_PERMISSION',
      type: 'String',
      value: 'service.auth.checkUser'
    }
  ],

  methods: [
    {
      name: 'start',
      javaCode: '// nothing here'
    },
    {
      name: 'getCurrentUser',
      javaCode: `
        Session session = x.get(Session.class);

        // fetch context and check if not null or user id is 0
        if ( session == null || session.getUserId() == 0 ) {
          throw new AuthenticationException("Not logged in");
        }

        // get user from session id
        User user = (User) ((DAO) getLocalUserDAO()).find(session.getUserId());
        if ( user == null ) {
          throw new AuthenticationException("User not found: " + session.getUserId());
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = getCurrentGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        // check for two-factor authentication
        if ( user.getTwoFactorEnabled() && ! session.getContext().getBoolean("twoFactorSuccess") ) {
          throw new AuthenticationException("User requires two-factor authentication");
        }

        return user;
      `
    },
    {
      name: 'loginHelper',
      documentation: `Helper function to reduce duplicated code.`,
      type: 'foam.nanos.auth.User',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'user',
          type: 'foam.nanos.auth.User'
        },
        {
          name: 'password',
          type: 'String'
        }
      ],
      javaThrows: ['foam.nanos.auth.AuthenticationException'],
      javaCode: `
        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = user.findGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        if ( ! Password.verify(password, user.getPassword()) ) {
          throw new AuthenticationException("Invalid Password");
        }

        // Freeze user
        user = (User) user.fclone();
        user.freeze();

        Session session = x.get(Session.class);
        session.setUserId(user.getId());
        ((DAO) getLocalSessionDAO()).put(session);
        // QUESTION: can we applyTo current context instead session context as below?
        session.setContext(session.applyTo(x));

        return user;
      `
    },
    {
      name: 'login',
      documentation: `Login a user by the id provided, validate the password and
        return the user in the context`,
      javaCode: `
        if ( userId < 1 || SafetyUtil.isEmpty(password) ) {
          throw new AuthenticationException("Invalid Parameters");
        }

        return loginHelper(x, (User) ((DAO) getLocalUserDAO()).find(userId), password);
      `
    },
    {
      name: 'loginByEmail',
      javaCode: `
        User user = (User) ((DAO) getLocalUserDAO()).find(
          AND(
            EQ(User.EMAIL, email.toLowerCase()),
            EQ(User.LOGIN_ENABLED, true)
          )
        );

        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }
        return loginHelper(x, user, password);
      `
    },
    {
      name: 'checkUserPermission',
      documentation: `Checks if the user passed into the method has the passed
        in permission attributed to it by checking their group. No check on User
        and group enabled flags.`,
      javaCode: `
        // check whether user has permission to check user permissions
        if ( ! check(x, CHECK_USER_PERMISSION) ) throw new AuthorizationException();

        if ( user == null || permission == null ) return false;

        try {
          String groupId = (String) user.getGroup();

          while ( ! SafetyUtil.isEmpty(groupId) ) {
            Group group = (Group) ((DAO) getLocalGroupDAO()).find(groupId);

            // if group is null break
            if ( group == null ) break;

            // check permission
            if ( group.implies(x, permission) ) return true;

            // check parent group
            groupId = group.getParent();
          }
        } catch (Throwable t) {
        }

        return false;
      `
    },
    {
      name: 'check',
      documentation: `Check if the user in the context supplied has the right
        permission.`,
      javaCode: `
        if ( x == null || permission == null ) return false;

        java.security.Permission p = new AuthPermission(permission);

        try {
          Group group = getCurrentGroup(x);

          while ( group != null ) {

            // check permission
            if ( group.implies(x, p) ) return true;

            // check parent group
            group = (Group) ((DAO) getLocalGroupDAO()).find(group.getParent());
          }
        } catch (IllegalArgumentException e) {
          Logger logger = (Logger) x.get("logger");
          logger.error("check", p, e);
        } catch (Throwable t) {
        }

        return false;
      `
    },
    {
      name: 'validatePassword',
      javaCode: `
        if ( SafetyUtil.isEmpty(potentialPassword) || ! (Pattern.compile(PASSWORD_VALIDATE_REGEX)).matcher(potentialPassword).matches() ) {
          throw new RuntimeException(PASSWORD_VALIDATION_ERROR_MESSAGE);
        }
      `
    },
    {
      name: 'checkUser',
      javaCode: `
        return checkUserPermission(x, user, new AuthPermission(permission));
      `
    },
    {
      name: 'updatePassword',
      documentation: `Given a context with a user, validate the password to be
        updated and return a context with the updated user information.`,
      javaCode: `
        if ( x == null || SafetyUtil.isEmpty(oldPassword) || SafetyUtil.isEmpty(newPassword) ) {
          throw new RuntimeException("Password fields cannot be blank");
        }

        Session session = x.get(Session.class);
        if ( session == null || session.getUserId() == 0 ) {
          throw new AuthenticationException("User not found");
        }

        User user = (User) ((DAO) getLocalUserDAO()).find(session.getUserId());
        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = user.findGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        // check if password is valid per validatePassword method
        validatePassword(newPassword);

        // old password does not match
        if ( ! Password.verify(oldPassword, user.getPassword()) ) {
          throw new RuntimeException("Old password is incorrect");
        }

        // new password is the same
        if ( Password.verify(newPassword, user.getPassword()) ) {
          throw new RuntimeException("New password must be different");
        }

        // store new password in DAO and put in context
        user = (User) user.fclone();
        user.setPasswordLastModified(Calendar.getInstance().getTime());
        user.setPreviousPassword(user.getPassword());
        user.setPassword(Password.hash(newPassword));
        // TODO: modify line to allow actual setting of password expiry in cases where users are required to periodically update their passwords
        user.setPasswordExpiry(null);
        user = (User) ((DAO) getLocalUserDAO()).put(user);
        session.setContext(session.getContext().put("user", user).put("group", group));
        return user;
      `
    },
    {
      name: 'validateUser',
      documentation: `Used to validate properties of a user. This will be called
        on registration of users. Will mainly be used as a veto method. Users
        should have id, email, first name, last name, password for registration`,
      javaCode: `
        if ( user == null ) {
          throw new AuthenticationException("Invalid User");
        }

        if ( SafetyUtil.isEmpty(user.getEmail()) ) {
          throw new AuthenticationException("Email is required for creating a user");
        }

        if ( ! Email.isValid(user.getEmail()) ) {
          throw new AuthenticationException("Email format is invalid");
        }

        if ( SafetyUtil.isEmpty(user.getFirstName()) ) {
          throw new AuthenticationException("First Name is required for creating a user");
        }

        if ( SafetyUtil.isEmpty(user.getLastName()) ) {
          throw new AuthenticationException("Last Name is required for creating a user");
        }

        if ( SafetyUtil.isEmpty(user.getPassword()) ) {
          throw new AuthenticationException("Password is required for creating a user");
        }

        validatePassword(user.getPassword());
      `
    },
    {
      name: 'logout',
      documentation: `Just return a null user for now. Not sure how to handle
        the cleanup of the current context.`,
      javaCode: `
        Session session = x.get(Session.class);
        if ( session != null && session.getUserId() != 0 ) {
          ((DAO) getLocalSessionDAO()).remove(session);
        }
      `
    },
    {
      name: 'getCurrentGroup',
      documentation: `Gets the effective group from a context.`,
      javaCode: `
        // Highest precedence: Just return the group from the context if it's already
        // been set.
        Group group = (Group) x.get("group");

        if ( group != null ) return group;

        User user = (User) x.get("user");
        User agent = (User) x.get("agent");

        // Second highest precedence: If one user is acting as another, return the
        // group on the junction between them.
        if ( user != null ) {
          if ( agent != null ) {
            DAO agentJunctionDAO = (DAO) x.get("agentJunctionDAO");
            UserUserJunction junction = (UserUserJunction) agentJunctionDAO.find(
              AND(
                EQ(UserUserJunction.SOURCE_ID, agent.getId()),
                EQ(UserUserJunction.TARGET_ID, user.getId())
              )
            );

            if ( junction == null ) {
              throw new RuntimeException("There was a user and an agent in the context, but a junction between then was not found.");
            }

            return (Group) ((DAO) getLocalGroupDAO()).inX(x).find(junction.getGroup());
          }

          // Third highest precedence: If a user is logged in but not acting as
          // another user, return their group.
          return (Group) ((DAO) getLocalGroupDAO()).inX(x).find(user.getGroup());
        }

        // If none of the cases above match, return null.
        // TODO: Should this throw an error instead?
        return null;
      `
    }
  ]
});
<commit_msg>Revert Fix session apply to context<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'UserAndGroupAuthService',
  flags: ['java'],

  implements: [
    'foam.nanos.auth.AuthService',
    'foam.nanos.NanoService'
  ],

  imports: [
    'localGroupDAO',
    'localSessionDAO',
    'localUserDAO'
  ],

  javaImports: [
    'foam.dao.DAO',
    'foam.nanos.logger.Logger',
    'foam.nanos.session.Session',
    'foam.util.Email',
    'foam.util.Password',
    'foam.util.SafetyUtil',

    'java.util.Calendar',
    'java.util.regex.Pattern',
    'javax.security.auth.AuthPermission',

    'static foam.mlang.MLang.AND',
    'static foam.mlang.MLang.EQ'
  ],

  constants: [
    {
      name: 'PASSWORD_VALIDATE_REGEX',
      type: 'String',
      value: '^.{6,}$'
    },
    {
      name: 'PASSWORD_VALIDATION_ERROR_MESSAGE',
      type: 'String',
      value: 'Password must be at least 6 characters long.'
    },
    {
      name: 'CHECK_USER_PERMISSION',
      type: 'String',
      value: 'service.auth.checkUser'
    }
  ],

  methods: [
    {
      name: 'start',
      javaCode: '// nothing here'
    },
    {
      name: 'getCurrentUser',
      javaCode: `
        Session session = x.get(Session.class);

        // fetch context and check if not null or user id is 0
        if ( session == null || session.getUserId() == 0 ) {
          throw new AuthenticationException("Not logged in");
        }

        // get user from session id
        User user = (User) ((DAO) getLocalUserDAO()).find(session.getUserId());
        if ( user == null ) {
          throw new AuthenticationException("User not found: " + session.getUserId());
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = getCurrentGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        // check for two-factor authentication
        if ( user.getTwoFactorEnabled() && ! session.getContext().getBoolean("twoFactorSuccess") ) {
          throw new AuthenticationException("User requires two-factor authentication");
        }

        return user;
      `
    },
    {
      name: 'loginHelper',
      documentation: `Helper function to reduce duplicated code.`,
      type: 'foam.nanos.auth.User',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'user',
          type: 'foam.nanos.auth.User'
        },
        {
          name: 'password',
          type: 'String'
        }
      ],
      javaThrows: ['foam.nanos.auth.AuthenticationException'],
      javaCode: `
        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = user.findGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        if ( ! Password.verify(password, user.getPassword()) ) {
          throw new AuthenticationException("Invalid Password");
        }

        // Freeze user
        user = (User) user.fclone();
        user.freeze();

        Session session = x.get(Session.class);
        session.setUserId(user.getId());
        ((DAO) getLocalSessionDAO()).put(session);
        session.setContext(session.applyTo(session.getContext()));

        return user;
      `
    },
    {
      name: 'login',
      documentation: `Login a user by the id provided, validate the password and
        return the user in the context`,
      javaCode: `
        if ( userId < 1 || SafetyUtil.isEmpty(password) ) {
          throw new AuthenticationException("Invalid Parameters");
        }

        return loginHelper(x, (User) ((DAO) getLocalUserDAO()).find(userId), password);
      `
    },
    {
      name: 'loginByEmail',
      javaCode: `
        User user = (User) ((DAO) getLocalUserDAO()).find(
          AND(
            EQ(User.EMAIL, email.toLowerCase()),
            EQ(User.LOGIN_ENABLED, true)
          )
        );

        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }
        return loginHelper(x, user, password);
      `
    },
    {
      name: 'checkUserPermission',
      documentation: `Checks if the user passed into the method has the passed
        in permission attributed to it by checking their group. No check on User
        and group enabled flags.`,
      javaCode: `
        // check whether user has permission to check user permissions
        if ( ! check(x, CHECK_USER_PERMISSION) ) throw new AuthorizationException();

        if ( user == null || permission == null ) return false;

        try {
          String groupId = (String) user.getGroup();

          while ( ! SafetyUtil.isEmpty(groupId) ) {
            Group group = (Group) ((DAO) getLocalGroupDAO()).find(groupId);

            // if group is null break
            if ( group == null ) break;

            // check permission
            if ( group.implies(x, permission) ) return true;

            // check parent group
            groupId = group.getParent();
          }
        } catch (Throwable t) {
        }

        return false;
      `
    },
    {
      name: 'check',
      documentation: `Check if the user in the context supplied has the right
        permission.`,
      javaCode: `
        if ( x == null || permission == null ) return false;

        java.security.Permission p = new AuthPermission(permission);

        try {
          Group group = getCurrentGroup(x);

          while ( group != null ) {

            // check permission
            if ( group.implies(x, p) ) return true;

            // check parent group
            group = (Group) ((DAO) getLocalGroupDAO()).find(group.getParent());
          }
        } catch (IllegalArgumentException e) {
          Logger logger = (Logger) x.get("logger");
          logger.error("check", p, e);
        } catch (Throwable t) {
        }

        return false;
      `
    },
    {
      name: 'validatePassword',
      javaCode: `
        if ( SafetyUtil.isEmpty(potentialPassword) || ! (Pattern.compile(PASSWORD_VALIDATE_REGEX)).matcher(potentialPassword).matches() ) {
          throw new RuntimeException(PASSWORD_VALIDATION_ERROR_MESSAGE);
        }
      `
    },
    {
      name: 'checkUser',
      javaCode: `
        return checkUserPermission(x, user, new AuthPermission(permission));
      `
    },
    {
      name: 'updatePassword',
      documentation: `Given a context with a user, validate the password to be
        updated and return a context with the updated user information.`,
      javaCode: `
        if ( x == null || SafetyUtil.isEmpty(oldPassword) || SafetyUtil.isEmpty(newPassword) ) {
          throw new RuntimeException("Password fields cannot be blank");
        }

        Session session = x.get(Session.class);
        if ( session == null || session.getUserId() == 0 ) {
          throw new AuthenticationException("User not found");
        }

        User user = (User) ((DAO) getLocalUserDAO()).find(session.getUserId());
        if ( user == null ) {
          throw new AuthenticationException("User not found");
        }

        // check if user enabled
        if ( ! user.getEnabled() ) {
          throw new AuthenticationException("User disabled");
        }

        // check if user login enabled
        if ( ! user.getLoginEnabled() ) {
          throw new AuthenticationException("Login disabled");
        }

        // check if group enabled
        Group group = user.findGroup(x);
        if ( group != null && ! group.getEnabled() ) {
          throw new AuthenticationException("Group disabled");
        }

        // check if password is valid per validatePassword method
        validatePassword(newPassword);

        // old password does not match
        if ( ! Password.verify(oldPassword, user.getPassword()) ) {
          throw new RuntimeException("Old password is incorrect");
        }

        // new password is the same
        if ( Password.verify(newPassword, user.getPassword()) ) {
          throw new RuntimeException("New password must be different");
        }

        // store new password in DAO and put in context
        user = (User) user.fclone();
        user.setPasswordLastModified(Calendar.getInstance().getTime());
        user.setPreviousPassword(user.getPassword());
        user.setPassword(Password.hash(newPassword));
        // TODO: modify line to allow actual setting of password expiry in cases where users are required to periodically update their passwords
        user.setPasswordExpiry(null);
        user = (User) ((DAO) getLocalUserDAO()).put(user);
        session.setContext(session.getContext().put("user", user).put("group", group));
        return user;
      `
    },
    {
      name: 'validateUser',
      documentation: `Used to validate properties of a user. This will be called
        on registration of users. Will mainly be used as a veto method. Users
        should have id, email, first name, last name, password for registration`,
      javaCode: `
        if ( user == null ) {
          throw new AuthenticationException("Invalid User");
        }

        if ( SafetyUtil.isEmpty(user.getEmail()) ) {
          throw new AuthenticationException("Email is required for creating a user");
        }

        if ( ! Email.isValid(user.getEmail()) ) {
          throw new AuthenticationException("Email format is invalid");
        }

        if ( SafetyUtil.isEmpty(user.getFirstName()) ) {
          throw new AuthenticationException("First Name is required for creating a user");
        }

        if ( SafetyUtil.isEmpty(user.getLastName()) ) {
          throw new AuthenticationException("Last Name is required for creating a user");
        }

        if ( SafetyUtil.isEmpty(user.getPassword()) ) {
          throw new AuthenticationException("Password is required for creating a user");
        }

        validatePassword(user.getPassword());
      `
    },
    {
      name: 'logout',
      documentation: `Just return a null user for now. Not sure how to handle
        the cleanup of the current context.`,
      javaCode: `
        Session session = x.get(Session.class);
        if ( session != null && session.getUserId() != 0 ) {
          ((DAO) getLocalSessionDAO()).remove(session);
        }
      `
    },
    {
      name: 'getCurrentGroup',
      documentation: `Gets the effective group from a context.`,
      javaCode: `
        // Highest precedence: Just return the group from the context if it's already
        // been set.
        Group group = (Group) x.get("group");

        if ( group != null ) return group;

        User user = (User) x.get("user");
        User agent = (User) x.get("agent");

        // Second highest precedence: If one user is acting as another, return the
        // group on the junction between them.
        if ( user != null ) {
          if ( agent != null ) {
            DAO agentJunctionDAO = (DAO) x.get("agentJunctionDAO");
            UserUserJunction junction = (UserUserJunction) agentJunctionDAO.find(
              AND(
                EQ(UserUserJunction.SOURCE_ID, agent.getId()),
                EQ(UserUserJunction.TARGET_ID, user.getId())
              )
            );

            if ( junction == null ) {
              throw new RuntimeException("There was a user and an agent in the context, but a junction between then was not found.");
            }

            return (Group) ((DAO) getLocalGroupDAO()).inX(x).find(junction.getGroup());
          }

          // Third highest precedence: If a user is logged in but not acting as
          // another user, return their group.
          return (Group) ((DAO) getLocalGroupDAO()).inX(x).find(user.getGroup());
        }

        // If none of the cases above match, return null.
        // TODO: Should this throw an error instead?
        return null;
      `
    }
  ]
});
<|endoftext|>


<commit_before>/*
 * Copyright 2005-2013 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.
 */
(function($) {
	$.fn.selectTab = function(options){
		return this.each(function(){
			options = options || {};
			//default setting
			options = $.extend({
				selectPage: ""
			}, options);

			if(options.selectPage){
                var oldTab = $(this).find(".active");
                if(oldTab.length){
                    oldTab.removeClass("active");
                }

				var currentTab = $(this).find("a[name='" + options.selectPage + "']");
				if(currentTab.length){
					currentTab.parent().addClass("active");
				}
			}
		});
	}

	$.fn.tabMenu = function(options){
		return this.each(function(){
			options = options || {};
			//default setting
			options = $.extend({
				defaultSelectFirst: true,
				currentPage: ""
			}, options);

			//element id strings
			var id = $(this).parent().attr('id');
			var list_elements = "#" + id + " li";
			var link_elements = list_elements + " a";

			//Styling
			$(this).parent().addClass("");
			$(this).addClass("uif-tabMenu");
			$(list_elements).addClass("");
			if(options.currentPage){
				var currentTab = $(this).find("a[name='" + options.currentPage + "']");
				if(currentTab){
					currentTab.closest("li").addClass("active");
				}
			}
			//Handlers and animation
			$(document).ready(function()
			{
					$(link_elements).each(function(i)
					{
						if(i == 0 && options.defaultSelectFirst && !options.currentPage){
							$(this).closest("li").addClass("active");
						}
						$(this).focus(
						function()
						{
							$(this).closest("li").addClass("");
						});

                        $(this).click(
						function()
						{
							$(link_elements).each(function(){$(this).closest("li").removeClass("active")});
							$(this).closest("li").addClass("active");
						});

						$(this).hover(
						function()
						{
							$(this).closest("li").addClass("");
						},
						function()
						{
							$(this).closest("li").removeClass("");
						});

						$(this).blur(
						function()
						{
							$(this).closest("li").removeClass("");
						});
					});
			});
		});
	}
})(jQuery);<commit_msg>UXI-86 Swapping out JQuery UI styling with Bootstrap classes & styling<commit_after>/*
 * Copyright 2005-2013 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.
 */
(function($) {
	$.fn.selectTab = function(options){
		return this.each(function(){
			options = options || {};
			//default setting
			options = $.extend({
				selectPage: ""
			}, options);

			if(options.selectPage){
                var oldTab = $(this).find(".active");
                if(oldTab.length){
                    oldTab.removeClass("active");
                }

				var currentTab = $(this).find("a[name='" + options.selectPage + "']");
				if(currentTab.length){
					currentTab.parent().addClass("active");
				}
			}
		});
	}

	$.fn.tabMenu = function(options){
		return this.each(function(){
			options = options || {};
			//default setting
			options = $.extend({
				defaultSelectFirst: true,
				currentPage: ""
			}, options);

			//element id strings
			var id = $(this).parent().attr('id');
			var list_elements = "#" + id + " li";
			var link_elements = list_elements + " a";

			//Styling
			$(this).parent().addClass("");
			$(this).addClass("nav nav-tabs");
			$(list_elements).addClass("");
			if(options.currentPage){
				var currentTab = $(this).find("a[name='" + options.currentPage + "']");
				if(currentTab){
					currentTab.closest("li").addClass("active");
				}
			}
			//Handlers and animation
			$(document).ready(function()
			{
					$(link_elements).each(function(i)
					{
						if(i == 0 && options.defaultSelectFirst && !options.currentPage){
							$(this).closest("li").addClass("active");
						}
						$(this).focus(
						function()
						{
							$(this).closest("li").addClass("");
						});

                        $(this).click(
						function()
						{
							$(link_elements).each(function(){$(this).closest("li").removeClass("active")});
							$(this).closest("li").addClass("active");
						});

						$(this).hover(
						function()
						{
							$(this).closest("li").addClass("");
						},
						function()
						{
							$(this).closest("li").removeClass("");
						});

						$(this).blur(
						function()
						{
							$(this).closest("li").removeClass("");
						});
					});
			});
		});
	}
})(jQuery);<|endoftext|>


<commit_before>angular.module('blockweltapp').controller("MainController", function ($http, importService, projectionService) {

    function processData(rawData) {
        var grid = {
            longitude: 13,
            latitude: 52,
            width: 0.1,
            height: 0.1,
            numLongitude: 10,
            numLatitude: 10
        };

        var projection = projectionService.project(grid, rawData);
        var features = projectionService.convertToFeatures(projection);
        vectorSource.addFeatures(features);
    }

    this.upload = function (path) {
        $http({
            method: 'GET',
            url: path,
        }).success(function (data) {
            processData(data)
        }).error(function () {
            alert("error");
        });
    };

    function processData(locations) {
        var model = importService.importData(locations);
        var grid = {
            longitude: 13,
            latitude: 52,
            width: 0.1,
            height: 0.1,
            numLongitude: 10,
            numLatitude: 10
        };
        var projection = projectionService.project(grid, model);
        var features = projectionService.convertToFeatures(projection);
        vectorSource.addFeatures(features);
    }

    this.visualize = function () {
        var f = document.getElementById('file').files[0],
            r = new FileReader();
        r.onloadend = function (e) {
            var data = e.target.result;
            var locations = angular.fromJson(data);
            processData(locations);
        }
        r.readAsBinaryString(f);
    };

    var map = new ol.Map({
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM()
            })
        ],
        target: 'map',
        view: new ol.View({
            center: [1520000, 6880000],
            zoom: 6
        })
    });

    var vectorSource = new ol.source.Vector();
    map.addLayer(new ol.layer.Vector({
        source: vectorSource
    }));
    var graticule = new ol.Graticule();
    graticule.setMap(map);

    this.with_data = function () {
        this.upload('locations.json');
    }
})<commit_msg>Add hook for current frame<commit_after>angular.module('blockweltapp').controller("MainController", function ($http, importService, projectionService) {

    this.upload = function (path) {
        $http({
            method: 'GET',
            url: path,
        }).success(function (data) {
            processData(data)
        }).error(function () {
            alert("error");
        });
    };

    function processData(locations) {
        var model = importService.importData(locations);
        var grid = {
            longitude: 13,
            latitude: 52,
            width: 0.1,
            height: 0.1,
            numLongitude: 10,
            numLatitude: 10
        };
        var projection = projectionService.project(grid, model);
        var features = projectionService.convertToFeatures(projection);
        vectorSource.addFeatures(features);
    }

    function onMoveEnd(evt) {
        var map = evt.map;
        var extent = map.getView().calculateExtent(map.getSize());
        console.log(extent);
    }

    this.visualize = function () {
        var f = document.getElementById('file').files[0],
            r = new FileReader();
        r.onloadend = function (e) {
            var data = e.target.result;
            var locations = angular.fromJson(data);
            processData(locations);
        };
        r.readAsBinaryString(f);
    };

    var map = new ol.Map({
        layers: [
            new ol.layer.Tile({
                source: new ol.source.OSM()
            })
        ],
        target: 'map',
        view: new ol.View({
            center: [1520000, 6880000],
            zoom: 6
        })
    });

    map.on('moveend', onMoveEnd);

    var vectorSource = new ol.source.Vector();
    map.addLayer(new ol.layer.Vector({
        source: vectorSource
    }));
    var graticule = new ol.Graticule();
    graticule.setMap(map);

    this.with_data = function () {
        this.upload('locations.json');
    }
})<|endoftext|>


<commit_before>var module = angular.module('blockweltapp');

module.factory('projectionService', function () {

    var mapProjection = ol.proj.get("EPSG:3857");
    var sourceProjection = ol.proj.get("EPSG:4326");

    function createBlock(longitude, latitude, grid) {
        var block = {
            longitude: longitude,
            latitude: latitude,
            width: grid.width,
            height: grid.height,
            count: 0
        };
        return block;
    }

    function getMaxCount(projection) {
        var m = 0;
        for (var x = 0; x < projection.length; x++) {
            for (var y = 0; y < projection[0].length; y++) {
                m = projection[x][y].count > m ? projection[x][y].count : m;
            }
        }
        return m;
    }

    function createColor(block, maxCount) {
        var c = 255 - 255 * Math.log(1 + block.count / maxCount);
        var color = [255, Math.floor(c), 0, .6];
        color = block.count == 0 ? [0, 0, 0, 0.2] : color;
        return color;
    }

    function createFeatureFromBlock(block, maxCount) {
        var color = createColor(block, maxCount);
        var style = new ol.style.Style({
            fill: new ol.style.Fill({
                color: color
            }),
            stroke: new ol.style.Stroke({
                color: 'black',
                width: .1
            })
        });
        var extent = ol.proj.transformExtent([
            block.longitude,
            block.latitude,
            block.longitude + block.height,
            block.latitude + block.width
        ], sourceProjection, mapProjection);
        var feature = new ol.Feature({
            geometry: ol.geom.Polygon.fromExtent(extent)
        });
        feature.setStyle(style);
        return feature;

    }

    var service = {

        project: function (grid, data) {

            var projection = [];

            for (var y = 0; y < grid.numLongitude; y++) {
                projection.push([]);
                for (var x = 0; x < grid.numLatitude; x++) {
                    projection[y].push(createBlock(grid.longitude + y * grid.height, grid.latitude + x * grid.width, grid));
                }
            }

            angular.forEach(data, function (coordinate) {
                var x = ((coordinate.latitude - grid.latitude) / grid.width) | 0;
                var y = ((coordinate.longitude - grid.longitude) / grid.height) | 0;

                if (x >= 0 && x < grid.numLatitude && y >= 0 && y < grid.numLongitude) {
                    projection[y][x].count++
                }
            });

            return projection;
        },

        convertToFeatures: function (projection) {
            var maxCount = getMaxCount(projection);
            var features = [];
            for (var x = 0; x < projection.length; x++) {
                for (var y = 0; y < projection[0].length; y++) {
                    features.push(createFeatureFromBlock(projection[x][y], maxCount));
                }
            }
            return features;
        }

    };

    return service;

});<commit_msg>Change color to logarithmic scale<commit_after>var module = angular.module('blockweltapp');

module.factory('projectionService', function () {

    var mapProjection = ol.proj.get("EPSG:3857");
    var sourceProjection = ol.proj.get("EPSG:4326");

    function createBlock(longitude, latitude, grid) {
        var block = {
            longitude: longitude,
            latitude: latitude,
            width: grid.width,
            height: grid.height,
            count: 0
        };
        return block;
    }

    function getMaxCount(projection) {
        var m = 0;
        for (var x = 0; x < projection.length; x++) {
            for (var y = 0; y < projection[0].length; y++) {
                m = projection[x][y].count > m ? projection[x][y].count : m;
            }
        }
        return m;
    }

    function createColor(block, maxCount) {
        var c = 255 - 255 * Math.log(1 + block.count) / Math.log(maxCount);
        var color = [255, Math.floor(c), 0, .6];
        color = block.count == 0 ? [0, 0, 0, 0.2] : color;
        return color;
    }

    function createFeatureFromBlock(block, maxCount) {
        var color = createColor(block, maxCount);
        var style = new ol.style.Style({
            fill: new ol.style.Fill({
                color: color
            }),
            stroke: new ol.style.Stroke({
                color: 'black',
                width: .1
            })
        });
        var extent = ol.proj.transformExtent([
            block.longitude,
            block.latitude,
            block.longitude + block.height,
            block.latitude + block.width
        ], sourceProjection, mapProjection);
        var feature = new ol.Feature({
            geometry: ol.geom.Polygon.fromExtent(extent)
        });
        feature.setStyle(style);
        return feature;

    }

    var service = {

        project: function (grid, data) {

            var projection = [];

            for (var y = 0; y < grid.numLongitude; y++) {
                projection.push([]);
                for (var x = 0; x < grid.numLatitude; x++) {
                    projection[y].push(createBlock(grid.longitude + y * grid.height, grid.latitude + x * grid.width, grid));
                }
            }

            angular.forEach(data, function (coordinate) {
                var x = ((coordinate.latitude - grid.latitude) / grid.width) | 0;
                var y = ((coordinate.longitude - grid.longitude) / grid.height) | 0;

                if (x >= 0 && x < grid.numLatitude && y >= 0 && y < grid.numLongitude) {
                    projection[y][x].count++
                }
            });

            return projection;
        },

        convertToFeatures: function (projection) {
            var maxCount = getMaxCount(projection);
            var features = [];
            for (var x = 0; x < projection.length; x++) {
                for (var y = 0; y < projection[0].length; y++) {
                    features.push(createFeatureFromBlock(projection[x][y], maxCount));
                }
            }
            return features;
        }

    };

    return service;

});<|endoftext|>


<commit_before>(function() {
  'use strict'

  function queryLink() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'visualization': '=?'
      },
      template: '{{query.name}}',
      link: function(scope, element) {
        var hash = null;
        if (scope.visualization) {
          if (scope.visualization.type === 'TABLE') {
            // link to hard-coded table tab instead of the (hidden) visualization tab
            hash = 'table';
          } else {
            hash = scope.visualization.id;
          }
        }
        scope.link = scope.query.getUrl(false, hash);
      }
    }
  }

  function querySourceLink($location) {
    return {
      restrict: 'E',
      template: '\
                    Show Source\
                    \
                    Hide Source\
                    \
                '
    }
  }

  function queryResultLink() {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {

        var fileType = attrs.fileType ? attrs.fileType : "csv";
        scope.$watch('queryResult && queryResult.getData()', function(data) {
          if (!data) {
            return;
          }

          if (scope.queryResult.getId() == null) {
            element.attr('href', '');
          } else {
            element.attr('href', 'api/queries/' + scope.query.id + '/results/' + scope.queryResult.getId() + '.' + fileType);
            element.attr('download', scope.query.name.replace(" ", "_") + moment(scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") + "." + fileType);
          }
        });
      }
    }
  }

  // By default Ace will try to load snippet files for the different modes and fail. We don't need them, so we use these
  // placeholders until we define our own.
  function defineDummySnippets(mode) {
    ace.define("ace/snippets/" + mode, ["require", "exports", "module"], function(require, exports, module) {
      "use strict";

      exports.snippetText = "";
      exports.scope = mode;
    });
  };

  defineDummySnippets("python");
  defineDummySnippets("sql");
  defineDummySnippets("json");

  var editorScope;
  var editorInstance;

  function queryEditor(QuerySnippet) {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'lock': '=',
        'schema': '=',
        'syntax': '='
      },
      template: '

',
      link: {
        pre: function ($scope, element) {
          $scope.syntax = $scope.syntax || 'sql';

          $scope.editorOptions = {
            theme: 'solarized_dark',
            mode: 'json',
            require: ['ace/ext/language_tools'],
            advanced: {
              behavioursEnabled: true,
              enableSnippets: true,
              enableBasicAutocompletion: true,
              enableLiveAutocompletion: true,
              autoScrollEditorIntoView: true,
            },
            onLoad: function(editor) {
              QuerySnippet.query(function(snippets) {
                var snippetManager = ace.require("ace/snippets").snippetManager;
                var m = {
                  snippetText: ''
                };
                m.snippets = snippetManager.parseSnippetFile(m.snippetText);
                _.each(snippets, function(snippet) {
                  m.snippets.push(snippet.getSnippet());
                });

                snippetManager.register(m.snippets || [], m.scope);
              });

              editor.$blockScrolling = Infinity;
              editor.getSession().setUseWrapMode(true);
              editor.setShowPrintMargin(false);

              $scope.$watch('syntax', function(syntax) {
                var newMode = 'ace/mode/' + syntax;
                editor.getSession().setMode(newMode);
              });

              $scope.$watch('schema', function(newSchema, oldSchema) {
                if (newSchema !== oldSchema) {
                  var tokensCount = _.reduce(newSchema, function(totalLength, table) { return totalLength + table.columns.length }, 0);
                  // If there are too many tokens we disable live autocomplete, as it makes typing slower.
                  if (tokensCount > 5000) {
                    editor.setOption('enableLiveAutocompletion', false);
                  } else {
                    editor.setOption('enableLiveAutocompletion', true);
                  }
                }

                $scope.fullScreenEditor = function fullScreenEditor(editor, resize_height){

                    var fullScreen = dom.toggleCssClass(document.body, "fullScreen")
                    angular.element('.ace_editor.fullScreen').css('height', '');
                    dom.setCssClass(editor.container, "fullScreen", fullScreen)
                    editor.setAutoScrollEditorIntoView(!fullScreen)
                    document.querySelector('.navbar').classList.toggle("hide")

                    if (angular.element('.ace_editor.fullScreen')){
                        angular.element('.ace_editor.fullScreen').css('height', $scope.resize_height);
                    }

                    editor.resize();
                    editor.focus();
                }

                var resize_height;
                editorScope = $scope;
                editorInstance = editor;
                var dom = ace.require("ace/lib/dom");

                editor.commands.addCommand({
                    name: "Toggle Fullscreen",
                    bindKey: {win: "Esc", mac: "Esc"},
                    exec: function(editor) {
                        $scope.fullScreenEditor(editor, resize_height)
                    }
                })

              });

              $scope.$parent.$on("angular-resizable.resizing", function (event, args) {

                editor.resize();

                $scope.resize_height = (448 + args.height - 300).toString();
                angular.element('.ace_editor.fullScreen').css('height', $scope.resize_height);

              });
              $scope.query.editor = editor;

              editor.focus();
            }
          };

          var langTools = ace.require("ace/ext/language_tools");

          var schemaCompleter = {
            getCompletions: function(state, session, pos, prefix, callback) {
              if (prefix.length === 0 || !$scope.schema) {
                callback(null, []);
                return;
              }

              if (!$scope.schema.keywords) {
                var keywords = {};

                _.each($scope.schema, function (table) {
                  keywords[table.name] = 'Table';

                  _.each(table.columns, function (c) {
                    keywords[c] = 'Column';
                    keywords[table.name + "." + c] = 'Column';
                  });
                });

                $scope.schema.keywords = _.map(keywords, function(v, k) {
                  return {
                    name: k,
                    value: k,
                    score: 0,
                    meta: v
                  };
                });
              }
              callback(null, $scope.schema.keywords);
            }
          };

          langTools.addCompleter(schemaCompleter);
        }
      }
    };
  }

  function queryFormatter($http, growl) {
    return {
      restrict: 'E',
      // don't create new scope to avoid ui-codemirror bug
      // seehttps://github.com/angular-ui/ui-codemirror/pull/37
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="formatQuery()">\
                    \
                     Format Query\
                </button>',
      link: function($scope) {
        $scope.formatQuery = function formatQuery() {
          if ($scope.dataSource.syntax == 'json') {
            try {
              $scope.query.query = JSON.stringify(JSON.parse($scope.query.query), ' ', 4);
            } catch(err) {
              growl.addErrorMessage(err);
            }
          } else if ($scope.dataSource.syntax =='sql') {

            $scope.queryFormatting = true;
            $http.post('api/queries/format', {
              'query': $scope.query.query
            }).success(function (response) {
              $scope.query.query = response;
            }).finally(function () {
              $scope.queryFormatting = false;
            });
          } else {
            growl.addInfoMessage("Query formatting is not supported for your data source syntax.");
          }
        };
      }
    }
  }

  function fullScreenEditor() {
    return {
      restrict: 'E',
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="fullScreenEditor()">\
                    \
                     Full Screen\
                </button>',
      link: function($scope) {
        $scope.fullScreenEditor = function fullScreenEditor() {
          editorScope.fullScreenEditor(editorInstance)
        };
      }
    }
  }

  function schemaBrowser() {
    return {
      restrict: 'E',
      scope: {
        schema: '='
      },
      templateUrl: '/views/directives/schema_browser.html',
      link: function ($scope) {
        $scope.showTable = function(table) {
          table.collapsed = !table.collapsed;
          $scope.$broadcast('vsRepeatTrigger');
        }

        $scope.getKeys = function(colDict) {
          return Object.keys(colDict)
        }

        $scope.getSize = function(table) {

          var size = 18;

          if (!table.collapsed) {
            size += 18 * table.columns.length;
          }

          return size;
        }
      }
    }
  }

  function queryTimePicker() {
    return {
      restrict: 'E',
      template: '<select ng-disabled="refreshType != \'daily\'" ng-model="hour" ng-change="updateSchedule()" ng-options="c as c for c in hourOptions"></select> :\
                 <select ng-disabled="refreshType != \'daily\'" ng-model="minute" ng-change="updateSchedule()" ng-options="c as c for c in minuteOptions"></select>',
      link: function($scope) {
        var padWithZeros = function(size, v) {
          v = String(v);
          if (v.length < size) {
            v = "0" + v;
          }
          return v;
        };

        $scope.hourOptions = _.map(_.range(0, 24), _.partial(padWithZeros, 2));
        $scope.minuteOptions = _.map(_.range(0, 60, 5), _.partial(padWithZeros, 2));

        if ($scope.query.hasDailySchedule()) {
          var parts = $scope.query.scheduleInLocalTime().split(':');
          $scope.minute = parts[1];
          $scope.hour = parts[0];
        } else {
          $scope.minute = "15";
          $scope.hour = "00";
        }

        $scope.updateSchedule = function() {
          var newSchedule = moment().hour($scope.hour).minute($scope.minute).utc().format('HH:mm');
          if (newSchedule != $scope.query.schedule) {
            $scope.query.schedule = newSchedule;
            $scope.saveQuery();
          }
        };

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'daily') {
            $scope.updateSchedule();
          }
        });
      }
    }
  }

  function queryRefreshSelect() {
    return {
      restrict: 'E',
      template: '<select\
                  ng-disabled="refreshType != \'periodic\'"\
                  ng-model="query.schedule"\
                  ng-change="saveQuery()"\
                  ng-options="c.value as c.name for c in refreshOptions">\
                  <option value="">No Refresh</option>\
                  </select>',
      link: function($scope) {
        $scope.refreshOptions = [
            {
                value: "60",
                name: 'Every minute'
            }
        ];

        _.each([5, 10, 15, 30], function(i) {
          $scope.refreshOptions.push({
            value: String(i*60),
            name: "Every " + i + " minutes"
          })
        });

        _.each(_.range(1, 13), function (i) {
            $scope.refreshOptions.push({
                value: String(i * 3600),
                name: 'Every ' + i + 'h'
            });
        })

        $scope.refreshOptions.push({
            value: String(24 * 3600),
            name: 'Every 24h'
        });
        $scope.refreshOptions.push({
            value: String(7 * 24 * 3600),
            name: 'Every 7 days'
        });
        $scope.refreshOptions.push({
          value: String(14 * 24 * 3600),
          name: 'Every 14 days'
        });
        $scope.refreshOptions.push({
            value: String(30 * 24 * 3600),
            name: 'Every 30 days'
        });

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'periodic') {
            if ($scope.query.hasDailySchedule()) {
              $scope.query.schedule = null;
              $scope.saveQuery();
            }
          }
        });
      }

    }
  }

  angular.module('redash.directives')
  .directive('queryLink', queryLink)
  .directive('querySourceLink', ['$location', querySourceLink])
  .directive('queryResultLink', queryResultLink)
  .directive('queryEditor', ['QuerySnippet', queryEditor])
  .directive('queryRefreshSelect', queryRefreshSelect)
  .directive('queryTimePicker', queryTimePicker)
  .directive('schemaBrowser', schemaBrowser)
  .directive('queryFormatter', ['$http', 'growl', queryFormatter])
  .directive('fullScreenEditor', fullScreenEditor);
})();
<commit_msg>enlarge no fullscreen<commit_after>(function() {
  'use strict'

  function queryLink() {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'visualization': '=?'
      },
      template: '{{query.name}}',
      link: function(scope, element) {
        var hash = null;
        if (scope.visualization) {
          if (scope.visualization.type === 'TABLE') {
            // link to hard-coded table tab instead of the (hidden) visualization tab
            hash = 'table';
          } else {
            hash = scope.visualization.id;
          }
        }
        scope.link = scope.query.getUrl(false, hash);
      }
    }
  }

  function querySourceLink($location) {
    return {
      restrict: 'E',
      template: '\
                    Show Source\
                    \
                    Hide Source\
                    \
                '
    }
  }

  function queryResultLink() {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {

        var fileType = attrs.fileType ? attrs.fileType : "csv";
        scope.$watch('queryResult && queryResult.getData()', function(data) {
          if (!data) {
            return;
          }

          if (scope.queryResult.getId() == null) {
            element.attr('href', '');
          } else {
            element.attr('href', 'api/queries/' + scope.query.id + '/results/' + scope.queryResult.getId() + '.' + fileType);
            element.attr('download', scope.query.name.replace(" ", "_") + moment(scope.queryResult.getUpdatedAt()).format("_YYYY_MM_DD") + "." + fileType);
          }
        });
      }
    }
  }

  // By default Ace will try to load snippet files for the different modes and fail. We don't need them, so we use these
  // placeholders until we define our own.
  function defineDummySnippets(mode) {
    ace.define("ace/snippets/" + mode, ["require", "exports", "module"], function(require, exports, module) {
      "use strict";

      exports.snippetText = "";
      exports.scope = mode;
    });
  };

  defineDummySnippets("python");
  defineDummySnippets("sql");
  defineDummySnippets("json");

  var editorScope;
  var editorInstance;

  function queryEditor(QuerySnippet) {
    return {
      restrict: 'E',
      scope: {
        'query': '=',
        'lock': '=',
        'schema': '=',
        'syntax': '='
      },
      template: '

',
      link: {
        pre: function ($scope, element) {
          $scope.syntax = $scope.syntax || 'sql';

          $scope.editorOptions = {
            theme: 'solarized_dark',
            mode: 'json',
            require: ['ace/ext/language_tools'],
            advanced: {
              behavioursEnabled: true,
              enableSnippets: true,
              enableBasicAutocompletion: true,
              enableLiveAutocompletion: true,
              autoScrollEditorIntoView: true,
            },
            onLoad: function(editor) {
              QuerySnippet.query(function(snippets) {
                var snippetManager = ace.require("ace/snippets").snippetManager;
                var m = {
                  snippetText: ''
                };
                m.snippets = snippetManager.parseSnippetFile(m.snippetText);
                _.each(snippets, function(snippet) {
                  m.snippets.push(snippet.getSnippet());
                });

                snippetManager.register(m.snippets || [], m.scope);
              });

              editor.$blockScrolling = Infinity;
              editor.getSession().setUseWrapMode(true);
              editor.setShowPrintMargin(false);

              $scope.$watch('syntax', function(syntax) {
                var newMode = 'ace/mode/' + syntax;
                editor.getSession().setMode(newMode);
              });

              $scope.$watch('schema', function(newSchema, oldSchema) {
                if (newSchema !== oldSchema) {
                  var tokensCount = _.reduce(newSchema, function(totalLength, table) { return totalLength + table.columns.length }, 0);
                  // If there are too many tokens we disable live autocomplete, as it makes typing slower.
                  if (tokensCount > 5000) {
                    editor.setOption('enableLiveAutocompletion', false);
                  } else {
                    editor.setOption('enableLiveAutocompletion', true);
                  }
                }

                $scope.fullScreenEditor = function fullScreenEditor(editor){

                    document.querySelector('.navbar').classList.toggle("hide")
                    document.querySelector('.container .row.bg-white.m-t-10').classList.toggle("hide")
                    document.querySelector('.schema-container').classList.toggle("hide")
                    angular.element('.pace-done').toggleClass('remove-t-padding')
                    angular.element('.container .editor-box').removeClass('col-md-9')
                    angular.element('.container .editor-box').toggleClass('col-md-12')

                    editor.resize();
                    editor.focus();
                }

                editorScope = $scope;
                editorInstance = editor;
                var dom = ace.require("ace/lib/dom");

                editor.commands.addCommand({
                    name: "Toggle Fullscreen",
                    bindKey: {win: "Esc", mac: "Esc"},
                    exec: function(editor) {
                        $scope.fullScreenEditor(editor)
                    }
                })

              });

              $scope.$parent.$on("angular-resizable.resizing", function (event, args) {
                editor.resize();
              });
              $scope.query.editor = editor;

              editor.focus();
            }
          };\

          var langTools = ace.require("ace/ext/language_tools");

          var schemaCompleter = {
            getCompletions: function(state, session, pos, prefix, callback) {
              if (prefix.length === 0 || !$scope.schema) {
                callback(null, []);
                return;
              }

              if (!$scope.schema.keywords) {
                var keywords = {};

                _.each($scope.schema, function (table) {
                  keywords[table.name] = 'Table';

                  _.each(table.columns, function (c) {
                    keywords[c] = 'Column';
                    keywords[table.name + "." + c] = 'Column';
                  });
                });

                $scope.schema.keywords = _.map(keywords, function(v, k) {
                  return {
                    name: k,
                    value: k,
                    score: 0,
                    meta: v
                  };
                });
              }
              callback(null, $scope.schema.keywords);
            }
          };

          langTools.addCompleter(schemaCompleter);
        }
      }
    };
  }

  function queryFormatter($http, growl) {
    return {
      restrict: 'E',
      // don't create new scope to avoid ui-codemirror bug
      // seehttps://github.com/angular-ui/ui-codemirror/pull/37
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="formatQuery()">\
                    \
                     Format Query\
                </button>',
      link: function($scope) {
        $scope.formatQuery = function formatQuery() {
          if ($scope.dataSource.syntax == 'json') {
            try {
              $scope.query.query = JSON.stringify(JSON.parse($scope.query.query), ' ', 4);
            } catch(err) {
              growl.addErrorMessage(err);
            }
          } else if ($scope.dataSource.syntax =='sql') {

            $scope.queryFormatting = true;
            $http.post('api/queries/format', {
              'query': $scope.query.query
            }).success(function (response) {
              $scope.query.query = response;
            }).finally(function () {
              $scope.queryFormatting = false;
            });
          } else {
            growl.addInfoMessage("Query formatting is not supported for your data source syntax.");
          }
        };
      }
    }
  }

  function fullScreenEditor() {
    return {
      restrict: 'E',
      scope: false,
      template: '<button type="button" class="btn btn-default btn-s"\
                   ng-click="fullScreenEditor()">\
                    \
                     Full Screen\
                </button>',
      link: function($scope) {
        $scope.fullScreenEditor = function fullScreenEditor() {
          editorScope.fullScreenEditor(editorInstance)
        };
      }
    }
  }

  function schemaBrowser() {
    return {
      restrict: 'E',
      scope: {
        schema: '='
      },
      templateUrl: '/views/directives/schema_browser.html',
      link: function ($scope) {
        $scope.showTable = function(table) {
          table.collapsed = !table.collapsed;
          $scope.$broadcast('vsRepeatTrigger');
        }

        $scope.getKeys = function(colDict) {
          return Object.keys(colDict)
        }

        $scope.getSize = function(table) {

          var size = 18;

          if (!table.collapsed) {
            size += 18 * table.columns.length;
          }

          return size;
        }
      }
    }
  }

  function queryTimePicker() {
    return {
      restrict: 'E',
      template: '<select ng-disabled="refreshType != \'daily\'" ng-model="hour" ng-change="updateSchedule()" ng-options="c as c for c in hourOptions"></select> :\
                 <select ng-disabled="refreshType != \'daily\'" ng-model="minute" ng-change="updateSchedule()" ng-options="c as c for c in minuteOptions"></select>',
      link: function($scope) {
        var padWithZeros = function(size, v) {
          v = String(v);
          if (v.length < size) {
            v = "0" + v;
          }
          return v;
        };

        $scope.hourOptions = _.map(_.range(0, 24), _.partial(padWithZeros, 2));
        $scope.minuteOptions = _.map(_.range(0, 60, 5), _.partial(padWithZeros, 2));

        if ($scope.query.hasDailySchedule()) {
          var parts = $scope.query.scheduleInLocalTime().split(':');
          $scope.minute = parts[1];
          $scope.hour = parts[0];
        } else {
          $scope.minute = "15";
          $scope.hour = "00";
        }

        $scope.updateSchedule = function() {
          var newSchedule = moment().hour($scope.hour).minute($scope.minute).utc().format('HH:mm');
          if (newSchedule != $scope.query.schedule) {
            $scope.query.schedule = newSchedule;
            $scope.saveQuery();
          }
        };

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'daily') {
            $scope.updateSchedule();
          }
        });
      }
    }
  }

  function queryRefreshSelect() {
    return {
      restrict: 'E',
      template: '<select\
                  ng-disabled="refreshType != \'periodic\'"\
                  ng-model="query.schedule"\
                  ng-change="saveQuery()"\
                  ng-options="c.value as c.name for c in refreshOptions">\
                  <option value="">No Refresh</option>\
                  </select>',
      link: function($scope) {
        $scope.refreshOptions = [
            {
                value: "60",
                name: 'Every minute'
            }
        ];

        _.each([5, 10, 15, 30], function(i) {
          $scope.refreshOptions.push({
            value: String(i*60),
            name: "Every " + i + " minutes"
          })
        });

        _.each(_.range(1, 13), function (i) {
            $scope.refreshOptions.push({
                value: String(i * 3600),
                name: 'Every ' + i + 'h'
            });
        })

        $scope.refreshOptions.push({
            value: String(24 * 3600),
            name: 'Every 24h'
        });
        $scope.refreshOptions.push({
            value: String(7 * 24 * 3600),
            name: 'Every 7 days'
        });
        $scope.refreshOptions.push({
          value: String(14 * 24 * 3600),
          name: 'Every 14 days'
        });
        $scope.refreshOptions.push({
            value: String(30 * 24 * 3600),
            name: 'Every 30 days'
        });

        $scope.$watch('refreshType', function() {
          if ($scope.refreshType == 'periodic') {
            if ($scope.query.hasDailySchedule()) {
              $scope.query.schedule = null;
              $scope.saveQuery();
            }
          }
        });
      }

    }
  }

  angular.module('redash.directives')
  .directive('queryLink', queryLink)
  .directive('querySourceLink', ['$location', querySourceLink])
  .directive('queryResultLink', queryResultLink)
  .directive('queryEditor', ['QuerySnippet', queryEditor])
  .directive('queryRefreshSelect', queryRefreshSelect)
  .directive('queryTimePicker', queryTimePicker)
  .directive('schemaBrowser', schemaBrowser)
  .directive('queryFormatter', ['$http', 'growl', queryFormatter])
  .directive('fullScreenEditor', fullScreenEditor);
})();
<|endoftext|>