<commit_before>(function() {

  angular
    .module('app.lesson')
    .controller('LessonController', LessonController);

  function LessonController($q, $scope, $routeParams, $location, $firebaseObject, $sce, navBarService, commonService) {

    console.log("LessonController");

	var ref = firebase.database().ref();
    var user = firebase.auth().currentUser;
	var chapter = $routeParams.chapter;
	var qns = $routeParams.qns;
    var qid = $routeParams.qid;

	navBarService.updateNavBar();

	$scope.answer = "";

    //Load Question
    var question = $firebaseObject(ref.child('course/questions/' + qid));
    question.$loaded().then(function(){
        //update user last attempt
        user = firebase.auth().currentUser;
        ref.child('userProfiles').child(user.uid).child('lastAttempt').set(qid);

        //retrieve qns details
        $scope.qnsTitle = question.qnsTitle;
		$scope.qnsInstruction = question.qnsInstruction;
		$scope.qnsDescription = question.qnsDescription;
        $scope.qnsHint = question.hint;
        var qnsType = question.qnsType;

        //Video type question
        if(qnsType == 'video'){
            $scope.srclink = $sce.trustAsResourceUrl(question.link);
        }

        //Slides type question
        if(qnsType == 'slides'){
           var slides = question.slides;

            $scope.currentSlide = 1;
            $scope.totalSlide = slides.length

            $scope.changeSlide = function(changeBy) {
                $scope.currentSlide += changeBy;
                var currentSlide = slides[$scope.currentSlide - 1];
                $scope.srclink = $sce.trustAsResourceUrl(currentSlide.imageLink);
                $scope.explanation = currentSlide.explanation;
            }
            //initial run
            $scope.changeSlide(0);
        }

        //MCQ type question
        if(qnsType == 'mcq') {
            $scope.questions = question.mcq;
            $scope.currentScore = 0;
            $scope.totalScore = $scope.questions.length;
        }

        //Excel type question
        if (qnsType == 'excel') {
            //load qns sheet ID
            $scope.sheetID = question.sheetID;

            //load admin
            var adminID = $firebaseObject(ref.child('auth/admin/admin'));
            adminID.$loaded().then(function(){
                //load admin spreadsheetId
                var adminUser = $firebaseObject(ref.child('auth/users/' + adminID.$value));
                adminUser.$loaded().then(function(){
                    $scope.eduExcelID = adminUser.eduSheet;

                    //load user spreadsheetId
                    var currentUser = $firebaseObject(ref.child('auth/users/' + user.uid));
                    currentUser.$loaded().then(function(){

                        $scope.userExcelID = currentUser.driveExcel;
                        //$scope.userSheetID = currentUser.sheetID; //Currently not in use
                        $scope.token = currentUser.access_token;

                        gapi.auth.setToken({
                            access_token: $scope.token
                        });

                        var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
                        //Delete all sheet except for instruction

                        gapi.client.load(discoveryUrl).then(function() {
                            getAllSheets().then(function(result) {
                                removeAllSheets(result).then(function(){
                                    copyQnsFromEdu().then(function(result) {
                                        updateSheetTitle().then(function(result) {
                                            var excelLink = "https://docs.google.com/spreadsheets/d/" + $scope.userExcelID + "/edit#gid=" + $scope.sheetId1;
                                            $scope.srclink = $sce.trustAsResourceUrl(excelLink);
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        }

        //Codebox type question
        if(qnsType == 'code') {
            var editor = ace.edit("editor");

            editor.setTheme("ace/theme/chrome");
            editor.getSession().setMode("ace/mode/javascript");
            editor.setOption("maxLines", 30);
            editor.setOption("minLines", 10);

            //insert code to codebox from firebase
            editor.insert(question.initialCode);

            /* Bind to commands
            editor.commands.addCommand({
                name: 'myCommand',
                bindKey: {win: 'Ctrl-Enter',  mac: 'Command-Enter'},
                exec: function(editor) {
                    alert("you have just press CTRL-ENTER")
                },
                readOnly: true // false if this command should not apply in readOnly mode
            });
            */
        }

        //Submit answer and go next qns if correct
        $scope.submit = function() {
            $scope.checkingAns = true;
            //Load answer key of the question
            var answerKey = $firebaseObject(ref.child('answerKey/' + qid));
            answerKey.$loaded().then(function(){

                //video and slides question type
                if (qnsType == 'video' || qnsType == 'slides'){
                    commonService.showSimpleToast("Time to applied what you have learnt!");
                    nextQns(chapter,qns);
                }

                //mcq question type
                if (qnsType == 'mcq'){
                    $scope.checked = true;
                    $scope.currentScore = 0;
                    for (i = 0; i < $scope.totalScore; i++) {
                        var result = $scope.questions[i].qnsID == answerKey.answer[i];
                        $scope.questions[i].qnsID = result;
                        //increase score if correct
                        if(result) {
                            $scope.currentScore += 1;
                        }
                    }
                    //all correct, go to next qns
                    if($scope.currentScore == $scope.totalScore) {
                        nextQns(chapter,qns);
                        commonService.showSimpleToast("Excellent!! You have completed the MCQ");
                    }
                }

                //excel question type
                if (qnsType == 'excel') {

                    gapi.auth.setToken({
                        access_token: $scope.token
                    });
                    $scope.range = answerKey.range;
                    $scope.formulaAnswer = answerKey.formulaAnswer;
                    $scope.valueAnswer = answerKey.valueAnswer;

                    var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
                    gapi.client.load(discoveryUrl).then(function() {
                        checkCellFormula().then(function(result) {
                            if(result.indexOf(false) === -1 && result.indexOf('false') === -1) {
                                checkCellValue().then(function(result){
                                    if (result.indexOf(false) === -1 && result.indexOf('false') === -1) {
                                        deleteCurrentSheet().then(function() {
                                            nextQns(chapter,qns);
                                            commonService.showSimpleToast("AWESOME!! You have completed the EXCEL Question");
                                        });

                                    } else {
                                        $scope.incorrect = true;
                                        $scope.checkingAns = false;
                                    }
                                });
                            } else {
                                $scope.incorrect = true;
                                $scope.checkingAns = false;
                            }
                        });
                    });
                }

                //code question type
                if (qnsType == 'code') {
                    // Check for syntax error
                    var editor = ace.edit("editor");
                    var annot = editor.getSession().getAnnotations();
                    if (annot.length == 0) {
                        var code = editor.getValue();
                        console.log(code);
                        $scope.codeResult = [];
                        var promises = []
                        var totalTestNum = answerKey.testcases.length;
                        //var totalTestNum = $scope.testCase.length;
                        for (i = 0; i < totalTestNum; i++) {
                            var test =  answerKey.testcases[i];//var test =  $scope.testCase[i];
                            //Run Test case
                            runTestcase(test, code).then(function(result) {
                                $scope.codeResult.push(result);
                                //When end of test case
                                if($scope.codeResult.length === totalTestNum){
                                    if ($scope.codeResult.indexOf(false) === -1) {
                                        nextQns(chapter,qns);
                                        commonService.showSimpleToast("FANTASTIC!! You have completed the code Question");
                                    } else {
                                        $scope.incorrect = true;
                                    }
                                }
                            });
                        }

                    } else {
                        $scope.incorrect = true;
                        $scope.errMsg = "Error with the syntax. Please check your answer again."
                    }
                }
            });
        }
    });

    function getAllSheets() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.get({
          spreadsheetId: $scope.userExcelID
        }).then(function(response) {
          var sheets = response.result.sheets;
          var sheetsToBeDelete = [];

          for (i = 0; i < sheets.length; i++) {
            var sheetId = sheets[i].properties.sheetId;
            if ( sheetId != 0) {
                sheetsToBeDelete.push(sheetId);
            }

            if(i == (sheets.length - 1)) {
                deferred.resolve(sheetsToBeDelete);
            }
          }
        });
        return deferred.promise;
    }

    function removeAllSheets(sheetsToBeDelete) {
        var deferred = $q.defer();
        var sheets = sheetsToBeDelete;

        for (i = 0; i < sheets.length; i++) {
          var sheetId = sheets[i];
          if ( sheetId != 0) {
            gapi.client.sheets.spreadsheets.batchUpdate({
                spreadsheetId: $scope.userExcelID,
                requests: [
                  {
                    deleteSheet:{
                      sheetId: sheetId
                    }
                  }
                ]
            }).then(function(response) {
                console.log("Sheet Deleted");
            });
          }
          if(i == (sheets.length - 1)) {
              deferred.resolve(true);
          }
        }
        if(sheets.length === 0) {
              deferred.resolve(true);
        }

        return deferred.promise;
    }

    function copyQnsFromEdu() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.sheets.copyTo({
          spreadsheetId: $scope.eduExcelID,
          sheetId: $scope.sheetID,
          destinationSpreadsheetId: $scope.userExcelID,
        }).then(function(response) {

          $scope.sheetId1 = response.result.sheetId;
          //update user sheetID - Currently not in use
          //ref.child("/auth/users/" + user.uid).update({ sheetID: $scope.sheetId1 });
          deferred.resolve(true);

        });
        return deferred.promise;
    }

    function updateSheetTitle() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.batchUpdate({
            spreadsheetId: $scope.userExcelID,
            requests: [
              {
                updateSheetProperties:{
                  properties:{
                    title: $scope.qnsTitle,
                    sheetId: $scope.sheetId1
                  },
                  fields: "title"
                }
              }
            ]
          }).then(function(response) {
            deferred.resolve(true);
        });
        return deferred.promise;
    }

    function checkCellFormula() {
      var deferred = $q.defer();
      gapi.client.sheets.spreadsheets.values.get({
        spreadsheetId: $scope.userExcelID,
        range: $scope.qnsTitle + "!" + $scope.range,
        valueRenderOption:"FORMULA"
      }).then(function(response) {

        $scope.formulaResult = []
        if($scope.formulaAnswer) {
            var totalTestNum =  $scope.formulaAnswer.length;
            for (i = 0; i < totalTestNum; i++) {
                var cell = $scope.formulaAnswer[i].cell;
                var functionName = $scope.formulaAnswer[i].functionName;

                var col = sheetColMapping(cell.charAt(0).toUpperCase()) - 1;
                var row = parseInt(cell.substring(1)) - 1;

                var formula = String(response.result.values[parseInt(row)][parseInt(col)]);

                $scope.formulaResult.push(formula.toUpperCase().indexOf(functionName.toUpperCase()) !== -1);
            }
        }
        deferred.resolve($scope.formulaResult);

      });
      return deferred.promise;
    }

    function checkCellValue() {
      var deferred = $q.defer();
      gapi.client.sheets.spreadsheets.values.get({
        spreadsheetId: $scope.userExcelID,
        range: $scope.qnsTitle + "!" + $scope.range
      }).then(function(response) {

        $scope.valueResult = []
        if($scope.formulaAnswer) {
            var totalTestNum =  $scope.valueAnswer.length;
            for (i = 0; i < totalTestNum; i++) {
                var cell = $scope.valueAnswer[i].cell;
                var answer = $scope.valueAnswer[i].value;

                var col = sheetColMapping(cell.charAt(0).toUpperCase()) - 1;
                var row = parseInt(cell.substring(1)) - 1;
                var valueRow = response.result.values[parseInt(row)];
                if(valueRow) {
                    value = valueRow[parseInt(col)];
                    $scope.valueResult.push(value === answer);
                } else {
                    $scope.valueResult.push(false);
                }

            }
        }
        deferred.resolve($scope.valueResult);
      });

      return deferred.promise;
    }

    function deleteCurrentSheet() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.batchUpdate({
            spreadsheetId: $scope.userExcelID,
            requests: [
              {
                deleteSheet:{
                    sheetId: $scope.sheetId1
                }
              }
            ]
        }).then(function(response) {
            deferred.resolve(true);

        });
      return deferred.promise;
    }

    function runTestcase(test, code) {

        var deferred = $q.defer();
        var ww = new Worker(getInlineJSandTest(test, code));
        //Send any message to worker
        ww.postMessage("and message");
        ww.onmessage = function (e) {
            var msg = e.data;
            //check if there failed result
            deferred.resolve(msg);
        };
        return deferred.promise;
    }

    function getInlineJSandTest (test, code) {
		var top = 'onmessage = function(msg){';
		var bottom = 'postMessage(result);};';

		var all = code +"\n\n"+top+"\n"+test+"\n"+bottom+"\n"
    console.log(all);
		var blob = new Blob([all], {"type": "text\/plain"});
		return URL.createObjectURL(blob);
	}

    function nextQns(chapter, question){

        //update course progress in firebase db
        var currentDateTime = new Date().toLocaleString("en-US");
        ref.child('userProfiles').child(user.uid).child('courseProgress').child(qid).set(currentDateTime);

        chapter = parseInt(chapter) - 1;
        question = parseInt(question);

        var courseSeq = $firebaseObject(ref.child('courseSequence'));
        courseSeq.$loaded().then(function() {
            var nextQns = courseSeq[chapter].qns[question];
            if(nextQns) {
				$location.path('/lesson/' + nextQns.qnsType + '/' + (chapter + 1) + '/' + (question + 1)+ '/' + nextQns.qid);
			} else {
				//Complete current chapter, go to next chapter
				nextQns = courseSeq[chapter+1];
				if(nextQns && courseSeq[chapter+1].qns) {
                    nextQns = courseSeq[chapter+1].qns[0];
					$location.path('/lesson/' + nextQns.qnsType + '/' + (chapter + 2) + '/1/'+ nextQns.qid );
				} else {
					//update last attemp in firebase db
					ref.child('userProfiles').child(user.uid).child('lastAttempt').set("completed");
					//Complete whole course
					var userRef = $firebaseObject(ref.child('auth/users/' + user.uid));
					userRef.$loaded().then(function(){
						$location.path('/profile/' + userRef.profileLink);
					});
				}
			}

        });
    }

    //map the col alphabet to number
    function sheetColMapping(col) {
        mapping = {
            "A" : 1,
            "B" : 2,
            "C" : 3,
            "D" : 4,
            "E" : 5,
            "F" : 6,
            "G" : 7,
            "H" : 8,
            "I" : 9,
            "J" : 10,
            "K" : 11,
            "L" : 12,
            "M" : 13,
            "N" : 14,
            "O" : 15,
            "P" : 16,
            "Q" : 17,
            "R" : 18,
            "S" : 19,
            "T" : 20,
            "U" : 21,
            "V" : 22,
            "W" : 23,
            "X" : 24,
            "Y" : 25,
            "Z" : 26,
        }
        return mapping[col.toUpperCase()]
    }

  };
})();
<commit_msg>update progress log<commit_after>(function() {

  angular
    .module('app.lesson')
    .controller('LessonController', LessonController);

  function LessonController($q, $scope, $routeParams, $location, $firebaseObject, $sce, navBarService, commonService) {

    console.log("LessonController");

	var ref = firebase.database().ref();
    var user = firebase.auth().currentUser;
	var chapter = $routeParams.chapter;
	var qns = $routeParams.qns;
    var qid = $routeParams.qid;

	navBarService.updateNavBar();

	$scope.answer = "";

    //Load Question
    var question = $firebaseObject(ref.child('course/questions/' + qid));
    question.$loaded().then(function(){
        //update user last attempt
        user = firebase.auth().currentUser;
        ref.child('userProfiles').child(user.uid).child('lastAttempt').set(qid);

        //retrieve qns details
        $scope.qnsTitle = question.qnsTitle;
		$scope.qnsInstruction = question.qnsInstruction;
		$scope.qnsDescription = question.qnsDescription;
        $scope.qnsHint = question.hint;
        var qnsType = question.qnsType;

        //Video type question
        if(qnsType == 'video'){
            $scope.srclink = $sce.trustAsResourceUrl(question.link);
        }

        //Slides type question
        if(qnsType == 'slides'){
           var slides = question.slides;

            $scope.currentSlide = 1;
            $scope.totalSlide = slides.length

            $scope.changeSlide = function(changeBy) {
                $scope.currentSlide += changeBy;
                var currentSlide = slides[$scope.currentSlide - 1];
                $scope.srclink = $sce.trustAsResourceUrl(currentSlide.imageLink);
                $scope.explanation = currentSlide.explanation;
            }
            //initial run
            $scope.changeSlide(0);
        }

        //MCQ type question
        if(qnsType == 'mcq') {
            $scope.questions = question.mcq;
            $scope.currentScore = 0;
            $scope.totalScore = $scope.questions.length;
        }

        //Excel type question
        if (qnsType == 'excel') {
            //load qns sheet ID
            $scope.sheetID = question.sheetID;

            //load admin
            var adminID = $firebaseObject(ref.child('auth/admin/admin'));
            adminID.$loaded().then(function(){
                //load admin spreadsheetId
                var adminUser = $firebaseObject(ref.child('auth/users/' + adminID.$value));
                adminUser.$loaded().then(function(){
                    $scope.eduExcelID = adminUser.eduSheet;

                    //load user spreadsheetId
                    var currentUser = $firebaseObject(ref.child('auth/users/' + user.uid));
                    currentUser.$loaded().then(function(){

                        $scope.userExcelID = currentUser.driveExcel;
                        //$scope.userSheetID = currentUser.sheetID; //Currently not in use
                        $scope.token = currentUser.access_token;

                        gapi.auth.setToken({
                            access_token: $scope.token
                        });

                        var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
                        //Delete all sheet except for instruction

                        gapi.client.load(discoveryUrl).then(function() {
                            getAllSheets().then(function(result) {
                                removeAllSheets(result).then(function(){
                                    copyQnsFromEdu().then(function(result) {
                                        updateSheetTitle().then(function(result) {
                                            var excelLink = "https://docs.google.com/spreadsheets/d/" + $scope.userExcelID + "/edit#gid=" + $scope.sheetId1;
                                            $scope.srclink = $sce.trustAsResourceUrl(excelLink);
                                        });
                                    });
                                });
                            });
                        });
                    });
                });
            });
        }

        //Codebox type question
        if(qnsType == 'code') {
            var editor = ace.edit("editor");

            editor.setTheme("ace/theme/chrome");
            editor.getSession().setMode("ace/mode/javascript");
            editor.setOption("maxLines", 30);
            editor.setOption("minLines", 10);

            //insert code to codebox from firebase
            editor.insert(question.initialCode);

            /* Bind to commands
            editor.commands.addCommand({
                name: 'myCommand',
                bindKey: {win: 'Ctrl-Enter',  mac: 'Command-Enter'},
                exec: function(editor) {
                    alert("you have just press CTRL-ENTER")
                },
                readOnly: true // false if this command should not apply in readOnly mode
            });
            */
        }

        //Submit answer and go next qns if correct
        $scope.submit = function() {
            $scope.checkingAns = true;
            //Load answer key of the question
            var answerKey = $firebaseObject(ref.child('answerKey/' + qid));
            answerKey.$loaded().then(function(){

                //video and slides question type
                if (qnsType == 'video' || qnsType == 'slides'){
                    commonService.showSimpleToast("Time to applied what you have learnt!");
                    nextQns(chapter,qns);
                }

                //mcq question type
                if (qnsType == 'mcq'){
                    $scope.checked = true;
                    $scope.currentScore = 0;
                    for (i = 0; i < $scope.totalScore; i++) {
                        var result = $scope.questions[i].qnsID == answerKey.answer[i];
                        $scope.questions[i].qnsID = result;
                        //increase score if correct
                        if(result) {
                            $scope.currentScore += 1;
                        }
                    }
                    //all correct, go to next qns
                    if($scope.currentScore == $scope.totalScore) {
                        nextQns(chapter,qns);
                        commonService.showSimpleToast("Excellent!! You have completed the MCQ");
                    }
                }

                //excel question type
                if (qnsType == 'excel') {

                    gapi.auth.setToken({
                        access_token: $scope.token
                    });
                    $scope.range = answerKey.range;
                    $scope.formulaAnswer = answerKey.formulaAnswer;
                    $scope.valueAnswer = answerKey.valueAnswer;

                    var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
                    gapi.client.load(discoveryUrl).then(function() {
                        checkCellFormula().then(function(result) {
                            if(result.indexOf(false) === -1 && result.indexOf('false') === -1) {
                                checkCellValue().then(function(result){
                                    if (result.indexOf(false) === -1 && result.indexOf('false') === -1) {
                                        deleteCurrentSheet().then(function() {
                                            nextQns(chapter,qns);
                                            commonService.showSimpleToast("AWESOME!! You have completed the EXCEL Question");
                                        });

                                    } else {
                                        $scope.incorrect = true;
                                        $scope.checkingAns = false;
                                    }
                                });
                            } else {
                                $scope.incorrect = true;
                                $scope.checkingAns = false;
                            }
                        });
                    });
                }

                //code question type
                if (qnsType == 'code') {
                    // Check for syntax error
                    var editor = ace.edit("editor");
                    var annot = editor.getSession().getAnnotations();
                    if (annot.length == 0) {
                        var code = editor.getValue();
                        console.log(code);
                        $scope.codeResult = [];
                        var promises = []
                        var totalTestNum = answerKey.testcases.length;
                        //var totalTestNum = $scope.testCase.length;
                        for (i = 0; i < totalTestNum; i++) {
                            var test =  answerKey.testcases[i];//var test =  $scope.testCase[i];
                            //Run Test case
                            runTestcase(test, code).then(function(result) {
                                $scope.codeResult.push(result);
                                //When end of test case
                                if($scope.codeResult.length === totalTestNum){
                                    if ($scope.codeResult.indexOf(false) === -1) {
                                        nextQns(chapter,qns);
                                        commonService.showSimpleToast("FANTASTIC!! You have completed the code Question");
                                    } else {
                                        $scope.incorrect = true;
                                    }
                                }
                            });
                        }

                    } else {
                        $scope.incorrect = true;
                        $scope.errMsg = "Error with the syntax. Please check your answer again."
                    }
                }
            });
        }
    });

    function getAllSheets() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.get({
          spreadsheetId: $scope.userExcelID
        }).then(function(response) {
          var sheets = response.result.sheets;
          var sheetsToBeDelete = [];

          for (i = 0; i < sheets.length; i++) {
            var sheetId = sheets[i].properties.sheetId;
            if ( sheetId != 0) {
                sheetsToBeDelete.push(sheetId);
            }

            if(i == (sheets.length - 1)) {
                deferred.resolve(sheetsToBeDelete);
            }
          }
        });
        return deferred.promise;
    }

    function removeAllSheets(sheetsToBeDelete) {
        var deferred = $q.defer();
        var sheets = sheetsToBeDelete;

        for (i = 0; i < sheets.length; i++) {
          var sheetId = sheets[i];
          if ( sheetId != 0) {
            gapi.client.sheets.spreadsheets.batchUpdate({
                spreadsheetId: $scope.userExcelID,
                requests: [
                  {
                    deleteSheet:{
                      sheetId: sheetId
                    }
                  }
                ]
            }).then(function(response) {
                console.log("Sheet Deleted");
            });
          }
          if(i == (sheets.length - 1)) {
              deferred.resolve(true);
          }
        }
        if(sheets.length === 0) {
              deferred.resolve(true);
        }

        return deferred.promise;
    }

    function copyQnsFromEdu() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.sheets.copyTo({
          spreadsheetId: $scope.eduExcelID,
          sheetId: $scope.sheetID,
          destinationSpreadsheetId: $scope.userExcelID,
        }).then(function(response) {

          $scope.sheetId1 = response.result.sheetId;
          //update user sheetID - Currently not in use
          //ref.child("/auth/users/" + user.uid).update({ sheetID: $scope.sheetId1 });
          deferred.resolve(true);

        });
        return deferred.promise;
    }

    function updateSheetTitle() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.batchUpdate({
            spreadsheetId: $scope.userExcelID,
            requests: [
              {
                updateSheetProperties:{
                  properties:{
                    title: $scope.qnsTitle,
                    sheetId: $scope.sheetId1
                  },
                  fields: "title"
                }
              }
            ]
          }).then(function(response) {
            deferred.resolve(true);
        });
        return deferred.promise;
    }

    function checkCellFormula() {
      var deferred = $q.defer();
      gapi.client.sheets.spreadsheets.values.get({
        spreadsheetId: $scope.userExcelID,
        range: $scope.qnsTitle + "!" + $scope.range,
        valueRenderOption:"FORMULA"
      }).then(function(response) {

        $scope.formulaResult = []
        if($scope.formulaAnswer) {
            var totalTestNum =  $scope.formulaAnswer.length;
            for (i = 0; i < totalTestNum; i++) {
                var cell = $scope.formulaAnswer[i].cell;
                var functionName = $scope.formulaAnswer[i].functionName;

                var col = sheetColMapping(cell.charAt(0).toUpperCase()) - 1;
                var row = parseInt(cell.substring(1)) - 1;

                var formula = String(response.result.values[parseInt(row)][parseInt(col)]);

                $scope.formulaResult.push(formula.toUpperCase().indexOf(functionName.toUpperCase()) !== -1);
            }
        }
        deferred.resolve($scope.formulaResult);

      });
      return deferred.promise;
    }

    function checkCellValue() {
      var deferred = $q.defer();
      gapi.client.sheets.spreadsheets.values.get({
        spreadsheetId: $scope.userExcelID,
        range: $scope.qnsTitle + "!" + $scope.range
      }).then(function(response) {

        $scope.valueResult = []
        if($scope.formulaAnswer) {
            var totalTestNum =  $scope.valueAnswer.length;
            for (i = 0; i < totalTestNum; i++) {
                var cell = $scope.valueAnswer[i].cell;
                var answer = $scope.valueAnswer[i].value;

                var col = sheetColMapping(cell.charAt(0).toUpperCase()) - 1;
                var row = parseInt(cell.substring(1)) - 1;
                var valueRow = response.result.values[parseInt(row)];
                if(valueRow) {
                    value = valueRow[parseInt(col)];
                    $scope.valueResult.push(value === answer);
                } else {
                    $scope.valueResult.push(false);
                }

            }
        }
        deferred.resolve($scope.valueResult);
      });

      return deferred.promise;
    }

    function deleteCurrentSheet() {
        var deferred = $q.defer();
        gapi.client.sheets.spreadsheets.batchUpdate({
            spreadsheetId: $scope.userExcelID,
            requests: [
              {
                deleteSheet:{
                    sheetId: $scope.sheetId1
                }
              }
            ]
        }).then(function(response) {
            deferred.resolve(true);

        });
      return deferred.promise;
    }

    function runTestcase(test, code) {

        var deferred = $q.defer();
        var ww = new Worker(getInlineJSandTest(test, code));
        //Send any message to worker
        ww.postMessage("and message");
        ww.onmessage = function (e) {
            var msg = e.data;
            //check if there failed result
            deferred.resolve(msg);
        };
        return deferred.promise;
    }

    function getInlineJSandTest (test, code) {
		var top = 'onmessage = function(msg){';
		var bottom = 'postMessage(result);};';

		var all = code +"\n\n"+top+"\n"+test+"\n"+bottom+"\n"
    console.log(all);
		var blob = new Blob([all], {"type": "text\/plain"});
		return URL.createObjectURL(blob);
	}

    function nextQns(chapter, question){

        //update course progress in firebase db
        var dateTimeNow = new Date().toISOString().slice(0,10);
        ref.child('userProfiles').child(user.uid).child('courseProgress').child(qid).set(dateTimeNow);

        chapter = parseInt(chapter) - 1;
        question = parseInt(question);

        var courseSeq = $firebaseObject(ref.child('courseSequence'));
        courseSeq.$loaded().then(function() {
            var nextQns = courseSeq[chapter].qns[question];
            if(nextQns) {
				$location.path('/lesson/' + nextQns.qnsType + '/' + (chapter + 1) + '/' + (question + 1)+ '/' + nextQns.qid);
			} else {
				//Complete current chapter, go to next chapter
				nextQns = courseSeq[chapter+1];
				if(nextQns && courseSeq[chapter+1].qns) {
                    nextQns = courseSeq[chapter+1].qns[0];
					$location.path('/lesson/' + nextQns.qnsType + '/' + (chapter + 2) + '/1/'+ nextQns.qid );
				} else {
					//update last attemp in firebase db
					ref.child('userProfiles').child(user.uid).child('lastAttempt').set("completed");
					//Complete whole course
					var userRef = $firebaseObject(ref.child('auth/users/' + user.uid));
					userRef.$loaded().then(function(){
						$location.path('/profile/' + userRef.profileLink);
					});
				}
			}

        });
    }

    //map the col alphabet to number
    function sheetColMapping(col) {
        mapping = {
            "A" : 1,
            "B" : 2,
            "C" : 3,
            "D" : 4,
            "E" : 5,
            "F" : 6,
            "G" : 7,
            "H" : 8,
            "I" : 9,
            "J" : 10,
            "K" : 11,
            "L" : 12,
            "M" : 13,
            "N" : 14,
            "O" : 15,
            "P" : 16,
            "Q" : 17,
            "R" : 18,
            "S" : 19,
            "T" : 20,
            "U" : 21,
            "V" : 22,
            "W" : 23,
            "X" : 24,
            "Y" : 25,
            "Z" : 26,
        }
        return mapping[col.toUpperCase()]
    }

  };
})();
<|endoftext|>


<commit_before>( function($) {

	/*
	$.post(wp_ajax.ajaxurl, { action: 'ACTION' }, function( data ) {
		// results come back in JSON, format data.property
	}, 'json' );
	*/

} ( jQuery ) );
<commit_msg>change .no-js class to .js on page load<commit_after>// Replace html.no-js class with .js
document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/g, 'js');

(function($) {

	/*
	$.post(wp_ajax.ajaxurl, {action: 'ACTION'}, function(data) {
		// results come back in JSON, format data.property
	}, 'json');
	*/

}(jQuery));
<|endoftext|>


<commit_before>// Scriptino Tabs
// Copyright 2010 Michael Geary - http://mg.to/
// Use under any Open Source license:
// http://www.opensource.org/licenses/

(function( S, $ ) {

	S.Tabs = function( a ) {
		var tabs = {
			select: select
		};
		var selectedClass = a.selectedClass || 'selected';
		var items = [];
		for( var id in a.tabs ) items.push( S(
			'
',
				'',
					'',
						a.tabs[id],
					'',
				'',
			'
'
		) );
		var $list = $.S( '
', items.join(''), '
' )
			.delegate( 'a', 'mouseenter', function( event ) {
				$(this).addClass('hover');
			})
			.delegate( 'a', 'mouseleave', function( event ) {
				$(this).removeClass('hover');
			})
			.delegate( 'a', 'click', function( event ) {
				event.preventDefault();
				select( this.parentNode.id, true );
			})
			.appendTo( a.parent );
		$('
').appendTo( a.parent );  // must be a better way
		function select( id ) {
			tabs.selected = id;
			$list.find('li').removeClass(selectedClass);
			$('#'+id).addClass(selectedClass);
			a.click && a.click( id );
			$(a.panels).children().hide();
			$('#'+id+'-panel').show();
		}
		return tabs;
	};

})( Scriptino, jQuery );
<commit_msg>Add subst property so two tabs can share the same content<commit_after>// Scriptino Tabs
// Copyright 2010 Michael Geary - http://mg.to/
// Use under any Open Source license:
// http://www.opensource.org/licenses/

(function( S, $ ) {

	S.Tabs = function( a ) {
		var tabs = {
			select: select
		};
		var selectedClass = a.selectedClass || 'selected';
		var items = [];
		for( var id in a.tabs ) items.push( S(
			'
',
				'',
					'',
						a.tabs[id],
					'',
				'',
			'
'
		) );
		var $list = $.S( '
', items.join(''), '
' )
			.delegate( 'a', 'mouseenter', function( event ) {
				$(this).addClass('hover');
			})
			.delegate( 'a', 'mouseleave', function( event ) {
				$(this).removeClass('hover');
			})
			.delegate( 'a', 'click', function( event ) {
				event.preventDefault();
				select( this.parentNode.id, true );
			})
			.appendTo( a.parent );
		$('
').appendTo( a.parent );  // must be a better way
		function select( id ) {
			tabs.selected = id;
			$list.find('li').removeClass(selectedClass);
			$('#'+id).addClass(selectedClass);
			a.click && a.click( id );
			$(a.panels).children().hide();
			id = a.subst[id] || id;
			$('#'+id+'-panel').show();
		}
		return tabs;
	};

})( Scriptino, jQuery );
<|endoftext|>


<commit_before>/*
  Copyright 2011-2016 Marvell Semiconductor, Inc.

  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.
*/

let blackSkin = new Skin({ fill: 'black'});

class SlideShowPictureBehavior extends Behavior{
  onCreate(container, data, startOnLoad){
    this.data = data;
    this.container = container;
    this.startOnLoad = startOnLoad;
    this.startedTransition = false;
    this.endedTransition = false;

    this.transition = { totalDuration: 8000, fadeInDuration: 1500, fadeOutDuration: 1500, scaleFactor: 0.5 };

    this.transitionCount = 10;
    this.transition.index = Math.floor( Math.random() * this.transitionCount );
  }
  onLoaded(picture){
    if ( !picture.ready ) {
			application.distribute( "onSlideShowPictureFailed", picture );
			return;
		}
		this.originalSize = { width: picture.width, height: picture.height };
		this.containerSize = { width: this.container.width, height: this.container.height };
		this.scaledSize = this.getScaledSize( picture );
		this.scale = this.scaledSize.scale;

		picture.visible = false;
		picture.opacity = 0;
		picture.subPixel = true;
		picture.aspect = 'draw';

		application.distribute( "onSlideShowPictureLoaded", picture );
  }
  startTransition(picture){
    picture.visible = true;
		picture.duration = this.transition.totalDuration;
		picture.start();
  }
  onTimeChanged(picture){
    var time = picture.time;
		var fraction = picture.fraction / 2;
		var scale = this.scale;
		var scaledSize = this.scaledSize;
		var containerSize = this.containerSize;
		var tx, ty = 0;

		if ( !this.startedTransition ) {
			this.startedTransition = true;
			application.distribute( "onSlideShowPictureStarting", picture );
			if ( 0 == this.transition.index || 4 == this.transition.index ) {	// The transition index tested here matches the pan transitions below
				var scaleBump = 1.1;
				// Adjust the scale for pan transitions to ensure that there's horizontal space to pan
				if ( this.scaledSize.width < (scaleBump * this.containerSize.width) ) {
					this.containerSize.width *= scaleBump;
					this.containerSize.height *= scaleBump;
					this.scaledSize = this.getScaledSize( picture );
					this.containerSize.width /= scaleBump;
					this.containerSize.height /= scaleBump;
					this.scale = scale = this.scaledSize.scale;
				}
			}
		}

		if ( time <= this.transition.fadeInDuration ) picture.opacity = time / this.transition.fadeInDuration
		else if ( time >= ( this.transition.totalDuration - this.transition.fadeOutDuration ) ) {
			picture.opacity = 1 - ( ( time - ( this.transition.totalDuration - this.transition.fadeOutDuration ) ) / this.transition.fadeOutDuration );
			if ( !this.endedTransition ) {
				this.endedTransition = true;
				application.distribute( "onSlideShowPictureEnding", picture );
			}
		}
		else
			picture.opacity = 1;

		switch( this.transition.index ) {
			case 0:	// Picture anchored at bottom/right, pan right
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { right: 0, bottom: 0 };
				picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
				tx = ( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 1:	// Picture anchored at bottom/right, zoom in, pan left
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { right: 0, bottom: 0 };
				picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
				break;
			case 2:	// Picture anchored at bottom/right, zoom in
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { right: 0, bottom: 0 };
				picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
				tx = ( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 3:	// Picture anchored at bottom/right, zoom out
				fraction = 1 - fraction;
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { right: 0, bottom: 0 };
				picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
				tx = ( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 4:	// Picture anchored at left/top, pan left
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, top: 0 };
				picture.origin = { x: 0, y: 0 };
				tx = -( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 5:	// Picture anchored at left/top, zoom in, pan right
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, top: 0 };
				picture.origin = { x: 0, y: 0 };
				break;
			case 6:	// Picture anchored at left/top, zoom in
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, top: 0 };
				picture.origin = { x: 0, y: 0 };
				tx = -( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 7:	// Picture anchored at left/top, zoom out
				fraction = 1 - fraction;
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, top: 0 };
				picture.origin = { x: 0, y: 0 };
				tx = -( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 8:	// Picture anchored at left/bottom, zoom out
				fraction = 1 - fraction;
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, bottom: 0 };
				picture.origin = { x: 0, y: this.originalSize.height };
				tx = -( ( scaledSize.width - containerSize.width ) * fraction );
				picture.translation = { x: tx, y: ty };
				break;
			case 9:	// Picture anchored at left/bottom, zoom in, pan right
				scale *= 1 + ( fraction * this.transition.scaleFactor );
				picture.scale = { x: scale, y: scale };
				picture.coordinates = { left: 0, bottom: 0 };
				picture.origin = { x: 0, y: this.originalSize.height };
				break;
		}
  }
  onFinished(picture){
    application.distribute( "onSlideShowPictureEnded", picture );
  }
  getScaledSize(picture){
    var hScale = this.containerSize.width / this.originalSize.width;
		var vScale = this.containerSize.height / this.originalSize.height;
		var scale = hScale > vScale ? hScale : vScale;
		return { width: Math.round( this.originalSize.width * scale ), height: Math.round( this.originalSize.height * scale ), scale: scale };
  }
};

let BusyPicture = Picture.template($ => ({ url: './assets/waiting.png',
  Behavior: class extends Behavior{
    onLoaded(picture){
      picture.origin = { x:picture.width >> 1, y:picture.height>>1 };
      picture.scale = { x:0.5, y:0.5 };
      picture.rotation = 0;
      picture.start();
    }
    onTimeChanged(picture){
      var rotation = picture.rotation;
      rotation -= 5;
      if (rotation < 0) rotation = 360
      picture.rotation = rotation;
    }
  }
}));

let MainContainer = Container.template($ => ({ left: 0, right: 0, top: 0, bottom: 0, skin: blackSkin,
  Behavior: class extends Behavior{
    onCreate(container, data){
      this.container = container;
      this.data = data;
      this.images = {index: 0, data: []};
      this.nextPicture = null;
      this.startedSlideShow = false;

      var uri = "https://api.flickr.com/services/feeds/groups_pool.gne?" + serializeQuery( { id: "80641914@N00", format: "json", nojsoncallback: 1 } );
      container.invoke( new Message( uri ), Message.TEXT );
    }
    onComplete(container, message, text){
      if (text) {
        var json = JSON.parse(text.replace(/\\'/g, "'"))
        var items = json.items;
        this.images.data.length = items.length;
        this.images.data.index = 0;
        for ( var i = 0, c = items.length; i < c; ++i ) {
          var uri = items[ i ].media.m.replace( "_m.jpg", "_b.jpg" );
          this.images.data[ i ] = { uri: uri, seed: Math.random() };
        }
        this.images.data.sort(function numericalOrder( a, b ) { return a.seed - b.seed });
      }
      if ( this.images.data.length ) {
        var picture = this.picture = new Picture();
        picture.behavior = new SlideShowPictureBehavior( this.container, this.data );
        picture.url = this.images.data[ this.images.index ].uri;
      }
    }
    getNextImage(){
      if ( !this.images.data.length ) return
      if ( ++this.images.index >= this.images.data.length ) this.images.index = 0
      return this.images.data[ this.images.index ];
    }
    onSlideShowPictureLoaded(container, picture){
      if ( !this.startedSlideShow || picture.behavior.startOnLoad ) {
        this.data.BUSY.visible = false;
        container.insert( picture, this.data.BUSY );
        picture.behavior.startTransition.call( picture.behavior, picture );
        this.startedSlideShow = true;
      }
    }
    onSlideShowPictureStarting(container, picture){
      var nextImage = this.getNextImage();
      if (!nextImage) return;

      this.nextPicture = new Picture();
      this.nextPicture.behavior = new SlideShowPictureBehavior( this.container, this.data );
      this.nextPicture.url = nextImage.uri;
    }
    onSlideShowPictureEnding(container, picture){
      if ( !this.nextPicture || !this.nextPicture.ready ) {
        //trace("Next picture not ready, fetching another and restarting...\n");
        if ( this.nextPicture ) this.nextPicture.url = null

        var nextImage = this.getNextImage();
        if ( !nextImage ) return;

        this.nextPicture = new Picture();
        this.nextPicture.behavior = new SlideShowPictureBehavior( container, this.data, true );
        this.nextPicture.url = nextImage.uri;
        this.picture = null;
        return;
      }
      this.container.insert( this.nextPicture, picture );
      this.nextPicture.behavior.startTransition.call( this.nextPicture.behavior, this.nextPicture );

      this.nextPicture = null;
      this.picture = null;
    }
    onSlideShowPictureEnded(container, picture){
      if ( picture && picture.container ) container.remove( picture )

      if  ( !( "transition" in container.first.behavior ) ) this.data.BUSY.visible = true

      this.picture = null;
    }
    onSlideShowPictureFailed(container, failed){
      var nextImage = this.getNextImage();
      if ( !nextImage ) return

      var picture = this.picture = new Picture();
      picture.behavior = new SlideShowPictureBehavior( container, this.data, true );
      picture.url = nextImage.uri;
      if ( failed == this.nextPicture ) this.nextPicture = picture
    }
    onUndisplayed(container){
      if ( this.nextPicture ) this.nextPicture.url = null
    }
  },
  contents: [
    BusyPicture($, { anchor:'BUSY' })
  ]
}));

application.add(new MainContainer({}));
<commit_msg>tab fixing<commit_after>/*
  Copyright 2011-2016 Marvell Semiconductor, Inc.

  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.
*/

let blackSkin = new Skin({ fill: 'black'});

class SlideShowPictureBehavior extends Behavior{
  onCreate(container, data, startOnLoad){
    this.data = data;
    this.container = container;
    this.startOnLoad = startOnLoad;
    this.startedTransition = false;
    this.endedTransition = false;

    this.transition = { totalDuration: 8000, fadeInDuration: 1500, fadeOutDuration: 1500, scaleFactor: 0.5 };

    this.transitionCount = 10;
    this.transition.index = Math.floor( Math.random() * this.transitionCount );
  }
  onLoaded(picture){
    if ( !picture.ready ) {
      application.distribute( "onSlideShowPictureFailed", picture );
      return;
    }
    this.originalSize = { width: picture.width, height: picture.height };
    this.containerSize = { width: this.container.width, height: this.container.height };
    this.scaledSize = this.getScaledSize( picture );
    this.scale = this.scaledSize.scale;

    picture.visible = false;
    picture.opacity = 0;
    picture.subPixel = true;
    picture.aspect = 'draw';

    application.distribute( "onSlideShowPictureLoaded", picture );
  }
  startTransition(picture){
    picture.visible = true;
    picture.duration = this.transition.totalDuration;
    picture.start();
  }
  onTimeChanged(picture){
    var time = picture.time;
    var fraction = picture.fraction / 2;
    var scale = this.scale;
    var scaledSize = this.scaledSize;
    var containerSize = this.containerSize;
    var tx, ty = 0;

    if ( !this.startedTransition ) {
      this.startedTransition = true;
      application.distribute( "onSlideShowPictureStarting", picture );
      if ( 0 == this.transition.index || 4 == this.transition.index ) {	// The transition index tested here matches the pan transitions below
      	var scaleBump = 1.1;
      	// Adjust the scale for pan transitions to ensure that there's horizontal space to pan
      	if ( this.scaledSize.width < (scaleBump * this.containerSize.width) ) {
          this.containerSize.width *= scaleBump;
          this.containerSize.height *= scaleBump;
          this.scaledSize = this.getScaledSize( picture );
          this.containerSize.width /= scaleBump;
          this.containerSize.height /= scaleBump;
          this.scale = scale = this.scaledSize.scale;
      	}
      }
    }

    if ( time <= this.transition.fadeInDuration ) picture.opacity = time / this.transition.fadeInDuration
    else if ( time >= ( this.transition.totalDuration - this.transition.fadeOutDuration ) ) {
      picture.opacity = 1 - ( ( time - ( this.transition.totalDuration - this.transition.fadeOutDuration ) ) / this.transition.fadeOutDuration );
      if ( !this.endedTransition ) {
        this.endedTransition = true;
        application.distribute( "onSlideShowPictureEnding", picture );
      }
    }
    else
      picture.opacity = 1;

    switch( this.transition.index ) {
      case 0:	// Picture anchored at bottom/right, pan right
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { right: 0, bottom: 0 };
        picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
        tx = ( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 1:	// Picture anchored at bottom/right, zoom in, pan left
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { right: 0, bottom: 0 };
        picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
        break;
      case 2:	// Picture anchored at bottom/right, zoom in
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { right: 0, bottom: 0 };
        picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
        tx = ( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 3:	// Picture anchored at bottom/right, zoom out
        fraction = 1 - fraction;
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { right: 0, bottom: 0 };
        picture.origin = { x: this.originalSize.width, y: this.originalSize.height };
        tx = ( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 4:	// Picture anchored at left/top, pan left
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, top: 0 };
        picture.origin = { x: 0, y: 0 };
        tx = -( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 5:	// Picture anchored at left/top, zoom in, pan right
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, top: 0 };
        picture.origin = { x: 0, y: 0 };
        break;
      case 6:	// Picture anchored at left/top, zoom in
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, top: 0 };
        picture.origin = { x: 0, y: 0 };
        tx = -( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 7:	// Picture anchored at left/top, zoom out
        fraction = 1 - fraction;
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, top: 0 };
        picture.origin = { x: 0, y: 0 };
        tx = -( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 8:	// Picture anchored at left/bottom, zoom out
        fraction = 1 - fraction;
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, bottom: 0 };
        picture.origin = { x: 0, y: this.originalSize.height };
        tx = -( ( scaledSize.width - containerSize.width ) * fraction );
        picture.translation = { x: tx, y: ty };
        break;
      case 9:	// Picture anchored at left/bottom, zoom in, pan right
        scale *= 1 + ( fraction * this.transition.scaleFactor );
        picture.scale = { x: scale, y: scale };
        picture.coordinates = { left: 0, bottom: 0 };
        picture.origin = { x: 0, y: this.originalSize.height };
        break;
    }
  }
  onFinished(picture){
    application.distribute( "onSlideShowPictureEnded", picture );
  }
  getScaledSize(picture){
    var hScale = this.containerSize.width / this.originalSize.width;
    var vScale = this.containerSize.height / this.originalSize.height;
    var scale = hScale > vScale ? hScale : vScale;
    return { width: Math.round( this.originalSize.width * scale ), height: Math.round( this.originalSize.height * scale ), scale: scale };
  }
};

let BusyPicture = Picture.template($ => ({ url: './assets/waiting.png',
  Behavior: class extends Behavior{
    onLoaded(picture){
      picture.origin = { x:picture.width >> 1, y:picture.height>>1 };
      picture.scale = { x:0.5, y:0.5 };
      picture.rotation = 0;
      picture.start();
    }
    onTimeChanged(picture){
      var rotation = picture.rotation;
      rotation -= 5;
      if (rotation < 0) rotation = 360
      picture.rotation = rotation;
    }
  }
}));

let MainContainer = Container.template($ => ({ left: 0, right: 0, top: 0, bottom: 0, skin: blackSkin,
  Behavior: class extends Behavior{
    onCreate(container, data){
      this.container = container;
      this.data = data;
      this.images = {index: 0, data: []};
      this.nextPicture = null;
      this.startedSlideShow = false;

      var uri = "https://api.flickr.com/services/feeds/groups_pool.gne?" + serializeQuery( { id: "80641914@N00", format: "json", nojsoncallback: 1 } );
      container.invoke( new Message( uri ), Message.TEXT );
    }
    onComplete(container, message, text){
      if (text) {
        var json = JSON.parse(text.replace(/\\'/g, "'"))
        var items = json.items;
        this.images.data.length = items.length;
        this.images.data.index = 0;
        for ( var i = 0, c = items.length; i < c; ++i ) {
          var uri = items[ i ].media.m.replace( "_m.jpg", "_b.jpg" );
          this.images.data[ i ] = { uri: uri, seed: Math.random() };
        }
        this.images.data.sort(function numericalOrder( a, b ) { return a.seed - b.seed });
      }
      if ( this.images.data.length ) {
        var picture = this.picture = new Picture();
        picture.behavior = new SlideShowPictureBehavior( this.container, this.data );
        picture.url = this.images.data[ this.images.index ].uri;
      }
    }
    getNextImage(){
      if ( !this.images.data.length ) return
      if ( ++this.images.index >= this.images.data.length ) this.images.index = 0
      return this.images.data[ this.images.index ];
    }
    onSlideShowPictureLoaded(container, picture){
      if ( !this.startedSlideShow || picture.behavior.startOnLoad ) {
        this.data.BUSY.visible = false;
        container.insert( picture, this.data.BUSY );
        picture.behavior.startTransition.call( picture.behavior, picture );
        this.startedSlideShow = true;
      }
    }
    onSlideShowPictureStarting(container, picture){
      var nextImage = this.getNextImage();
      if (!nextImage) return;

      this.nextPicture = new Picture();
      this.nextPicture.behavior = new SlideShowPictureBehavior( this.container, this.data );
      this.nextPicture.url = nextImage.uri;
    }
    onSlideShowPictureEnding(container, picture){
      if ( !this.nextPicture || !this.nextPicture.ready ) {
        //trace("Next picture not ready, fetching another and restarting...\n");
        if ( this.nextPicture ) this.nextPicture.url = null

        var nextImage = this.getNextImage();
        if ( !nextImage ) return;

        this.nextPicture = new Picture();
        this.nextPicture.behavior = new SlideShowPictureBehavior( container, this.data, true );
        this.nextPicture.url = nextImage.uri;
        this.picture = null;
        return;
      }
      this.container.insert( this.nextPicture, picture );
      this.nextPicture.behavior.startTransition.call( this.nextPicture.behavior, this.nextPicture );

      this.nextPicture = null;
      this.picture = null;
    }
    onSlideShowPictureEnded(container, picture){
      if ( picture && picture.container ) container.remove( picture )

      if  ( !( "transition" in container.first.behavior ) ) this.data.BUSY.visible = true

      this.picture = null;
    }
    onSlideShowPictureFailed(container, failed){
      var nextImage = this.getNextImage();
      if ( !nextImage ) return

      var picture = this.picture = new Picture();
      picture.behavior = new SlideShowPictureBehavior( container, this.data, true );
      picture.url = nextImage.uri;
      if ( failed == this.nextPicture ) this.nextPicture = picture
    }
    onUndisplayed(container){
      if ( this.nextPicture ) this.nextPicture.url = null
    }
  },
  contents: [
    BusyPicture($, { anchor:'BUSY' })
  ]
}));

application.add(new MainContainer({}));
<|endoftext|>


<commit_before>/* jshint strict: false, sub: true */
/* global print */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2016 ArangoDB GmbH, Cologne, Germany
// / Copyright 2014 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is ArangoDB GmbH, Cologne, Germany
// /
// / @author Max Neunhoeffer
// //////////////////////////////////////////////////////////////////////////////

const functionsDocumentation = {
  'http_replication': 'http replication tests',
  'http_server': 'http server tests in Ruby',
  'ssl_server': 'https server tests'
};
const optionsDocumentation = [
  '   - `skipSsl`: omit the ssl_server rspec tests.',
  '   - `rspec`: the location of rspec program',
  '   - `ruby`: the location of ruby program; if empty start rspec directly'
];

/* Modules: */
const _ = require('lodash');
const fs = require('fs');
const pu = require('@arangodb/process-utils');
const tu = require('@arangodb/test-utils');
const yaml = require('js-yaml');

// const BLUE = require('internal').COLORS.COLOR_BLUE;
// const CYAN = require('internal').COLORS.COLOR_CYAN;
// const GREEN = require('internal').COLORS.COLOR_GREEN;
const RED = require('internal').COLORS.COLOR_RED;
const RESET = require('internal').COLORS.COLOR_RESET;
// const YELLOW = require('internal').COLORS.COLOR_YELLOW;

// //////////////////////////////////////////////////////////////////////////////
// / @brief runs ruby tests using RSPEC
// //////////////////////////////////////////////////////////////////////////////

function camelize (str) {
  return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) {
    return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
  }).replace(/\s+/g, '');
}

function rubyTests (options, ssl) {
  let instanceInfo;

  if (ssl) {
    instanceInfo = pu.startInstance('ssl', options, {}, 'ssl_server');
  } else {
    instanceInfo = pu.startInstance('tcp', options, {}, 'http_server');
  }

  if (instanceInfo === false) {
    return {
      status: false,
      message: 'failed to start server!'
    };
  }

  const tmpname = fs.getTempFile() + '.rb';

  let rspecConfig = 'RSpec.configure do |c|\n' +
                    '  c.add_setting :ARANGO_SERVER\n' +
                    '  c.ARANGO_SERVER = "' +
                    instanceInfo.endpoint.substr(6) + '"\n' +
                    '  c.add_setting :ARANGO_SSL\n' +
                    '  c.ARANGO_SSL = "' + (ssl ? '1' : '0') + '"\n' +
                    '  c.add_setting :ARANGO_USER\n' +
                    '  c.ARANGO_USER = "' + options.username + '"\n' +
                    '  c.add_setting :ARANGO_PASSWORD\n' +
                    '  c.ARANGO_PASSWORD = "' + options.password + '"\n' +
                    '  c.add_setting :SKIP_TIMECRITICAL\n' +
                    '  c.SKIP_TIMECRITICAL = ' + JSON.stringify(options.skipTimeCritical) + '\n' +
                    'end\n';

  fs.write(tmpname, rspecConfig);
  if (options.extremeVerbosity === true){
      print("rspecConfig: \n" + rspecConfig);
  }
  try {
    fs.makeDirectory(pu.LOGS_DIR);
  } catch (err) {}

  let files = fs.list(fs.join('UnitTests', 'HttpInterface'));

  let continueTesting = true;
  let filtered = {};
  let result = {};

  let args;
  let command;
  let rspec;

  if (options.ruby === '') {
    command = options.rspec;
    rspec = undefined;
  } else {
    command = options.ruby;
    rspec = options.rspec;
  }

  const parseRspecJson = function (testCase, res, totalDuration) {
    let tName = camelize(testCase.description);
    let status = (testCase.status === 'passed');

    res[tName] = {
      status: status,
      message: testCase.full_description,
      duration: totalDuration // RSpec doesn't offer per testcase time...
    };

    res.total++;

    if (!status) {
      const msg = yaml.safeDump(testCase)
            .replace(/.*rspec\/core.*\n/gm, '')
            .replace(/.*rspec\\core.*\n/gm, '');
      print('RSpec test case falied: \n' + msg);
      res[tName].message += '\n' + msg;
    }
  };

  let count = 0;
  files = tu.splitBuckets(options, files);

  for (let i = 0; i < files.length; i++) {
    const te = files[i];

    if (te.substr(0, 4) === 'api-' && te.substr(-3) === '.rb') {
      let tfn = fs.join('UnitTests', 'HttpInterface', te);
      if (tu.filterTestcaseByOptions(tfn, options, filtered)) {
        count += 1;
        if (!continueTesting) {
          print('Skipping ' + te + ' server is gone.');

          result[te] = {
            status: false,
            message: instanceInfo.exitStatus
          };

          instanceInfo.exitStatus = 'server is gone.';
          break;
        }

        args = ['--color',
                '-I', fs.join('UnitTests', 'HttpInterface'),
                '--format', 'd',
                '--format', 'j',
                '--out', fs.join('out', 'UnitTests', te + '.json'),
                '--require', tmpname,
                tfn
               ];

        if (rspec !== undefined) {
          args = [rspec].concat(args);
        }

        print('\n' + Date() + ' rspec trying', tfn, '...');
        const res = pu.executeAndWait(command, args, options, 'arangosh', instanceInfo.rootDir);

        result[te] = {
          total: 0,
          status: res.status
        };

        const resultfn = fs.join('out', 'UnitTests', te + '.json');

        try {
          const jsonResult = JSON.parse(fs.read(resultfn));

          if (options.extremeVerbosity) {
            print(yaml.safeDump(jsonResult));
          }

          for (let j = 0; j < jsonResult.examples.length; ++j) {
            parseRspecJson(jsonResult.examples[j], result[te],
                           jsonResult.summary.duration);
          }

          result[te].duration = jsonResult.summary.duration;
        } catch (x) {
          print('Failed to parse rspec result: ' + x);
          result[te]['complete_' + te] = res;

          if (res.status === false) {
            options.cleanup = false;

            if (!options.force) {
              break;
            }
          }
        }

        continueTesting = pu.arangod.check.instanceAlive(instanceInfo, options);
      } else {
        if (options.extremeVerbosity) {
          print('Skipped ' + te + ' because of ' + filtered.filter);
        }
      }
    }
  }

  print('Shutting down...');

  if (count === 0) {
    result['ALLTESTS'] = {
      status: false,
      skipped: true
    };
    result.status = false;
    print(RED + 'No testcase matched the filter.' + RESET);
  }

  fs.remove(tmpname);
  pu.shutdownInstance(instanceInfo, options);
  print('done.');

  return result;
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: http_replication
// //////////////////////////////////////////////////////////////////////////////

function httpReplication (options) {
  var opts = {
    'replication': true
  };
  _.defaults(opts, options);

  return rubyTests(opts, false);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: http_server
// //////////////////////////////////////////////////////////////////////////////

function httpServer (options) {
  var opts = {
    'httpTrustedOrigin': 'http://was-erlauben-strunz.it'
  };
  _.defaults(opts, options);
  return rubyTests(opts, false);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: ssl_server
// //////////////////////////////////////////////////////////////////////////////

function sslServer (options) {
  if (options.skipSsl) {
    return {
      ssl_server: {
        status: true,
        skipped: true
      }
    };
  }
  var opts = {
    'httpTrustedOrigin': 'http://was-erlauben-strunz.it'
  };
  _.defaults(opts, options);
  return rubyTests(opts, true);
}

function setup (testFns, defaultFns, opts, fnDocs, optionsDoc) {
  testFns['http_replication'] = httpReplication;
  testFns['http_server'] = httpServer;
  testFns['ssl_server'] = sslServer;

  defaultFns.push('http_server');
  defaultFns.push('ssl_server');

  opts['skipSsl'] =  false;
  opts['rspec'] = 'rspec';
  opts['ruby'] =  '';

  for (var attrname in functionsDocumentation) { fnDocs[attrname] = functionsDocumentation[attrname]; }
  for (var i = 0; i < optionsDocumentation.length; i++) { optionsDoc.push(optionsDocumentation[i]); }
}

exports.setup = setup;
<commit_msg>make rspec backtrace more compact<commit_after>/* jshint strict: false, sub: true */
/* global print */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2016 ArangoDB GmbH, Cologne, Germany
// / Copyright 2014 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is ArangoDB GmbH, Cologne, Germany
// /
// / @author Max Neunhoeffer
// //////////////////////////////////////////////////////////////////////////////

const functionsDocumentation = {
  'http_replication': 'http replication tests',
  'http_server': 'http server tests in Ruby',
  'ssl_server': 'https server tests'
};
const optionsDocumentation = [
  '   - `skipSsl`: omit the ssl_server rspec tests.',
  '   - `rspec`: the location of rspec program',
  '   - `ruby`: the location of ruby program; if empty start rspec directly'
];

/* Modules: */
const _ = require('lodash');
const fs = require('fs');
const pu = require('@arangodb/process-utils');
const tu = require('@arangodb/test-utils');
const yaml = require('js-yaml');

// const BLUE = require('internal').COLORS.COLOR_BLUE;
// const CYAN = require('internal').COLORS.COLOR_CYAN;
// const GREEN = require('internal').COLORS.COLOR_GREEN;
const RED = require('internal').COLORS.COLOR_RED;
const RESET = require('internal').COLORS.COLOR_RESET;
// const YELLOW = require('internal').COLORS.COLOR_YELLOW;

// //////////////////////////////////////////////////////////////////////////////
// / @brief runs ruby tests using RSPEC
// //////////////////////////////////////////////////////////////////////////////

function camelize (str) {
  return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) {
    return index === 0 ? letter.toLowerCase() : letter.toUpperCase();
  }).replace(/\s+/g, '');
}

function rubyTests (options, ssl) {
  let instanceInfo;

  if (ssl) {
    instanceInfo = pu.startInstance('ssl', options, {}, 'ssl_server');
  } else {
    instanceInfo = pu.startInstance('tcp', options, {}, 'http_server');
  }

  if (instanceInfo === false) {
    return {
      status: false,
      message: 'failed to start server!'
    };
  }

  const tmpname = fs.getTempFile() + '.rb';

  let rspecConfig = 'RSpec.configure do |c|\n' +
                    '  c.add_setting :ARANGO_SERVER\n' +
                    '  c.ARANGO_SERVER = "' +
                    instanceInfo.endpoint.substr(6) + '"\n' +
                    '  c.add_setting :ARANGO_SSL\n' +
                    '  c.ARANGO_SSL = "' + (ssl ? '1' : '0') + '"\n' +
                    '  c.add_setting :ARANGO_USER\n' +
                    '  c.ARANGO_USER = "' + options.username + '"\n' +
                    '  c.add_setting :ARANGO_PASSWORD\n' +
                    '  c.ARANGO_PASSWORD = "' + options.password + '"\n' +
                    '  c.add_setting :SKIP_TIMECRITICAL\n' +
                    '  c.SKIP_TIMECRITICAL = ' + JSON.stringify(options.skipTimeCritical) + '\n' +
                    'end\n';

  fs.write(tmpname, rspecConfig);
  if (options.extremeVerbosity === true){
      print("rspecConfig: \n" + rspecConfig);
  }
  try {
    fs.makeDirectory(pu.LOGS_DIR);
  } catch (err) {}

  let files = fs.list(fs.join('UnitTests', 'HttpInterface'));

  let continueTesting = true;
  let filtered = {};
  let result = {};

  let args;
  let command;
  let rspec;

  if (options.ruby === '') {
    command = options.rspec;
    rspec = undefined;
  } else {
    command = options.ruby;
    rspec = options.rspec;
  }

  const parseRspecJson = function (testCase, res, totalDuration) {
    let tName = camelize(testCase.description);
    let status = (testCase.status === 'passed');

    res[tName] = {
      status: status,
      message: testCase.full_description,
      duration: totalDuration // RSpec doesn't offer per testcase time...
    };

    res.total++;

    if (!status) {
      const msg = yaml.safeDump(testCase)
            .replace(/.*rspec\/core.*\n/gm, '')
            .replace(/.*rspec\\core.*\n/gm, '')
            .replace(/.*- >-.*\n/gm, '')
            .replace(/\n *`/gm, ' `');
      print('RSpec test case falied: \n' + msg);
      res[tName].message += '\n' + msg;
    }
  };

  let count = 0;
  files = tu.splitBuckets(options, files);

  for (let i = 0; i < files.length; i++) {
    const te = files[i];

    if (te.substr(0, 4) === 'api-' && te.substr(-3) === '.rb') {
      let tfn = fs.join('UnitTests', 'HttpInterface', te);
      if (tu.filterTestcaseByOptions(tfn, options, filtered)) {
        count += 1;
        if (!continueTesting) {
          print('Skipping ' + te + ' server is gone.');

          result[te] = {
            status: false,
            message: instanceInfo.exitStatus
          };

          instanceInfo.exitStatus = 'server is gone.';
          break;
        }

        args = ['--color',
                '-I', fs.join('UnitTests', 'HttpInterface'),
                '--format', 'd',
                '--format', 'j',
                '--out', fs.join('out', 'UnitTests', te + '.json'),
                '--require', tmpname,
                tfn
               ];

        if (rspec !== undefined) {
          args = [rspec].concat(args);
        }

        print('\n' + Date() + ' rspec trying', tfn, '...');
        const res = pu.executeAndWait(command, args, options, 'arangosh', instanceInfo.rootDir);

        result[te] = {
          total: 0,
          status: res.status
        };

        const resultfn = fs.join('out', 'UnitTests', te + '.json');

        try {
          const jsonResult = JSON.parse(fs.read(resultfn));

          if (options.extremeVerbosity) {
            print(yaml.safeDump(jsonResult));
          }

          for (let j = 0; j < jsonResult.examples.length; ++j) {
            parseRspecJson(jsonResult.examples[j], result[te],
                           jsonResult.summary.duration);
          }

          result[te].duration = jsonResult.summary.duration;
        } catch (x) {
          print('Failed to parse rspec result: ' + x);
          result[te]['complete_' + te] = res;

          if (res.status === false) {
            options.cleanup = false;

            if (!options.force) {
              break;
            }
          }
        }

        continueTesting = pu.arangod.check.instanceAlive(instanceInfo, options);
      } else {
        if (options.extremeVerbosity) {
          print('Skipped ' + te + ' because of ' + filtered.filter);
        }
      }
    }
  }

  print('Shutting down...');

  if (count === 0) {
    result['ALLTESTS'] = {
      status: false,
      skipped: true
    };
    result.status = false;
    print(RED + 'No testcase matched the filter.' + RESET);
  }

  fs.remove(tmpname);
  pu.shutdownInstance(instanceInfo, options);
  print('done.');

  return result;
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: http_replication
// //////////////////////////////////////////////////////////////////////////////

function httpReplication (options) {
  var opts = {
    'replication': true
  };
  _.defaults(opts, options);

  return rubyTests(opts, false);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: http_server
// //////////////////////////////////////////////////////////////////////////////

function httpServer (options) {
  var opts = {
    'httpTrustedOrigin': 'http://was-erlauben-strunz.it'
  };
  _.defaults(opts, options);
  return rubyTests(opts, false);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief TEST: ssl_server
// //////////////////////////////////////////////////////////////////////////////

function sslServer (options) {
  if (options.skipSsl) {
    return {
      ssl_server: {
        status: true,
        skipped: true
      }
    };
  }
  var opts = {
    'httpTrustedOrigin': 'http://was-erlauben-strunz.it'
  };
  _.defaults(opts, options);
  return rubyTests(opts, true);
}

function setup (testFns, defaultFns, opts, fnDocs, optionsDoc) {
  testFns['http_replication'] = httpReplication;
  testFns['http_server'] = httpServer;
  testFns['ssl_server'] = sslServer;

  defaultFns.push('http_server');
  defaultFns.push('ssl_server');

  opts['skipSsl'] =  false;
  opts['rspec'] = 'rspec';
  opts['ruby'] =  '';

  for (var attrname in functionsDocumentation) { fnDocs[attrname] = functionsDocumentation[attrname]; }
  for (var i = 0; i < optionsDocumentation.length; i++) { optionsDoc.push(optionsDocumentation[i]); }
}

exports.setup = setup;
<|endoftext|>