<commit_before>go.sp = function() {
    var vumigo = require('vumigo_v02');
    var ChoiceState = vumigo.states.ChoiceState;
    var FreeText = vumigo.states.FreeText;
    var Choice = vumigo.states.Choice;

    var sp = {
        // School Monitoring States

        add_emis_school_monitoring: function(name, $, array_emis, contact, im) {
            var error = $("The emis does not exist, please try again. " +
                        "This should have 4-6 digits e.g 4351.");

            var question = $("Please enter the school's EMIS number that you would " +
                            "like to report on. This should have 4-6 digits e.g 4351.");

            return new FreeText(name, {
                question: question,

                check: function(content) {
                    if (go.utils.check_valid_emis(content, array_emis) === false) {
                        return error;
                    }
                },

                next: function(content) {
                    contact.extra.school_monitoring_emis = content;
                    return im.contacts
                        .save(contact)
                        .then(function() {
                            return "monitor_school_visit_complete";
                        });
                }

            });
        },

        monitor_school_visit_complete: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Please complete the following questions after the visit is complete."),

                choices: [
                    new Choice('monitor_school_see_lpip', $("Continue")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        monitor_school_see_lpip: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the School Learner Performance Improvement Plan for this year?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        return 'monitor_school_g2_observation_results';
                    } else {
                        return 'monitor_school_teaching';
                    }
                }
            });
        },

         // q01
        monitor_school_teaching: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Please indicate the following: Is there an activity in the LPIP for " +
                    "improving the teaching of early grade reading?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learner_assessment'
            });
        },

        // q02
        monitor_school_learner_assessment: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for improving learner assessment?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learning_materials'
            });
        },

        // q03
        monitor_school_learning_materials: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for buying or making teaching and learning materials?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learner_attendance'
            });
        },

        // q04
        monitor_school_learner_attendance: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for improving learner attendance?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_reading_time'
            });
        },

        // q05
        monitor_school_reading_time: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for increasing the time available for children to " +
                    "read, inside or outside school?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_struggling_learners'
            });
        },

        // q06
        monitor_school_struggling_learners: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity to give extra or remedial support to struggling " +
                    "learners?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_g2_observation_results'
            });
        },

        // q07 - diverts
        monitor_school_g2_observation_results: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the Grade 2 reading lesson observation results done by the " +
                    "head teacher for the current term?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        return 'monitor_school_gala_sheets';
                    } else {
                        return 'monitor_school_ht_feedback';
                    }
                }
            });
        },

        // q08
        monitor_school_ht_feedback: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("According to the teacher observed, has the Head Teacher given him/her " +
                    "feedback?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_submitted_classroom'
            });
        },

        // q09
        monitor_school_submitted_classroom: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Has the Head Teacher submitted the classroom observation results to the ZSG?"),

                choices: [
                    new Choice('yes_cellphone', $("YES submitted by cell phone")),
                    new Choice('yes_paper', $("YES submitted paper form to DEBS office")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_gala_sheets'
            });
        },

        // q10 - diverts
        monitor_school_gala_sheets: function(name, $, im, contact) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the GALA stimulus sheets completed by the learners for the " +
                    "current term?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_incomplete', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        var emis = contact.extra.school_monitoring_emis;
                        var school_monitoring_data = go.utils.school_monitoring_data_collect(emis, im);
                        return go.utils
                            .cms_post("data/school_monitoring/", school_monitoring_data, im)
                            .then(function(result) {
                                contact.extra.school_monitoring_emis = "";
                                return im.contacts
                                    .save(contact)
                                    .then(function() {
                                        return 'monitor_school_falling_behind';
                                    });
                            });
                    } else {
                        return 'monitor_school_summary_worksheet';
                    }
                }
            });
        },

        // q11
        monitor_school_summary_worksheet: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Was the summary worksheet accurately completed by the Head Teacher?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_ht_feedback_literacy'
            });
        },

        // q12
        monitor_school_ht_feedback_literacy: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("According to the teacher observed, has the Head Teacher given him/her " +
                    "feedback on the literacy assessment results?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_submitted_gala'
            });
        },

        // q13
        monitor_school_submitted_gala: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Has the Head Teacher submitted the GALA results to the ZSG?"),

                choices: [
                    new Choice('yes_cellphone', $("YES submitted by cell phone")),
                    new Choice('yes_paper', $("YES submitted paper form to DEBS office")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_talking_wall'
            });
        },

        // q14
        monitor_school_talking_wall: function(name, $, im, contact) {
            return new ChoiceState(name, {
                question:
                    $("Is the Talking Wall poster on display and up to date?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('yes_incomplete', $("YES but not updated")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    var emis = contact.extra.school_monitoring_emis;
                    var school_monitoring_data = go.utils.school_monitoring_data_collect(emis, im);
                    return go.utils
                        .cms_post("data/school_monitoring/", school_monitoring_data, im)
                        .then(function(result) {
                            contact.extra.school_monitoring_emis = "";
                            return im.contacts
                                .save(contact)
                                .then(function() {
                                    return 'monitor_school_completed';
                                });
                        });
                }
            });
        },

        // q15
        monitor_school_completed: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Congratulations, you have finished reporting on this school."),

                choices: [
                    new Choice('add_emis_school_monitoring',
                                    $("Report on another school monitoring visit")),
                    new Choice('initial_state', $("Go back to the main menu")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        // q16
        monitor_school_falling_behind: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("This school is falling behind with their LPIP and E-SIMON " +
                    "responsibilities. Please assist the Head Teacher to catch up."),

                choices: [
                    new Choice('initial_state', $("Go back to the main menu")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        "commas": "commas"
    };

    return sp;

}();
<commit_msg>change labels & add created_by<commit_after>go.sp = function() {
    var vumigo = require('vumigo_v02');
    var _ = require('lodash');
    var ChoiceState = vumigo.states.ChoiceState;
    var FreeText = vumigo.states.FreeText;
    var Choice = vumigo.states.Choice;

    var sp = {
        // School Monitoring States

        add_emis_school_monitoring: function(name, $, array_emis, contact, im) {
            var error = $("The emis does not exist, please try again. " +
                        "This should have 4-6 digits e.g 4351.");

            var question = $("Please enter the school's EMIS number that you would " +
                            "like to report on. This should have 4-6 digits e.g 4351.");

            return new FreeText(name, {
                question: question,

                check: function(content) {
                    if (go.utils.check_valid_emis(content, array_emis) === false) {
                        return error;
                    }
                },

                next: function(content) {
                    contact.extra.school_monitoring_emis = content;
                    return im.contacts
                        .save(contact)
                        .then(function() {
                            return "monitor_school_visit_complete";
                        });
                }

            });
        },

        monitor_school_visit_complete: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Please complete the following questions after the visit is complete."),

                choices: [
                    new Choice('monitor_school_see_lpip', $("Continue")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        monitor_school_see_lpip: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the School Learner Performance Improvement Plan for this year?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        return 'monitor_school_g2_observation_results';
                    } else {
                        return 'monitor_school_teaching';
                    }
                }
            });
        },

         // q01
        monitor_school_teaching: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Please indicate the following: Is there an activity in the LPIP for " +
                    "improving the teaching of early grade reading?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learner_assessment'
            });
        },

        // q02
        monitor_school_learner_assessment: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for improving learner assessment?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learning_materials'
            });
        },

        // q03
        monitor_school_learning_materials: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for buying or making teaching and learning materials?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_learner_attendance'
            });
        },

        // q04
        monitor_school_learner_attendance: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for improving learner attendance?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_reading_time'
            });
        },

        // q05
        monitor_school_reading_time: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity for increasing the time available for children to " +
                    "read, inside or outside school?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_struggling_learners'
            });
        },

        // q06
        monitor_school_struggling_learners: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Is there an activity to give extra or remedial support to struggling " +
                    "learners?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_g2_observation_results'
            });
        },

        // q07 - diverts
        monitor_school_g2_observation_results: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the Grade 2 reading lesson observation results done by the " +
                    "head teacher for the current term?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        return 'monitor_school_gala_sheets';
                    } else {
                        return 'monitor_school_ht_feedback';
                    }
                }
            });
        },

        // q08
        monitor_school_ht_feedback: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("According to the teacher observed, has the Head Teacher given him/her " +
                    "feedback?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_submitted_classroom'
            });
        },

        // q09
        monitor_school_submitted_classroom: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Has the Head Teacher submitted the classroom observation results to the ZSG?"),

                choices: [
                    new Choice('yes_cellphone', $("YES submitted by cell phone")),
                    new Choice('yes_paper', $("YES submitted paper form to DEBS office")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_gala_sheets'
            });
        },

        // q10 - diverts
        monitor_school_gala_sheets: function(name, $, im, contact) {
            return new ChoiceState(name, {
                question:
                    $("Did you see the GALA stimulus sheets completed by the learners for the " +
                    "current term?"),

                choices: [
                    new Choice('yes', $("YES - completed")),
                    new Choice('yes_in_progress', $("YES - in progress")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    if(choice.value === 'no') {
                        var emis = contact.extra.school_monitoring_emis;
                        var id = contact.extra.rts_id;
                        var school_monitoring_data = go.utils.school_monitoring_data_collect(emis, im);

                        if (_.isUndefined(contact.extra.rts_official_district_id)) {
                            // is head teacher
                            school_monitoring_data.created_by = "/api/v1/data/headteacher/" + id + "/";
                        } else {
                            // is district admin
                            school_monitoring_data.created_by_da = "/api/v1/district_admin/" + id + "/";
                        }

                        return go.utils
                            .cms_post("data/school_monitoring/", school_monitoring_data, im)
                            .then(function(result) {
                                contact.extra.school_monitoring_emis = "";
                                return im.contacts
                                    .save(contact)
                                    .then(function() {
                                        return 'monitor_school_falling_behind';
                                    });
                            });
                    } else {
                        return 'monitor_school_summary_worksheet';
                    }
                }
            });
        },

        // q11
        monitor_school_summary_worksheet: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Was the summary worksheet accurately completed by the Head Teacher?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_ht_feedback_literacy'
            });
        },

        // q12
        monitor_school_ht_feedback_literacy: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("According to the teacher observed, has the Head Teacher given him/her " +
                    "feedback on the literacy assessment results?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_submitted_gala'
            });
        },

        // q13
        monitor_school_submitted_gala: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Has the Head Teacher submitted the GALA results to the ZSG?"),

                choices: [
                    new Choice('yes_cellphone', $("YES submitted by cell phone")),
                    new Choice('yes_paper', $("YES submitted paper form to DEBS office")),
                    new Choice('no', $("NO"))
                ],

                next: 'monitor_school_talking_wall'
            });
        },

        // q14
        monitor_school_talking_wall: function(name, $, im, contact) {
            return new ChoiceState(name, {
                question:
                    $("Is the Talking Wall poster on display and up to date?"),

                choices: [
                    new Choice('yes', $("YES")),
                    new Choice('yes_not_updated', $("YES but not updated")),
                    new Choice('no', $("NO"))
                ],

                next: function(choice) {
                    var emis = contact.extra.school_monitoring_emis;
                    var id = contact.extra.rts_id;
                    var school_monitoring_data = go.utils.school_monitoring_data_collect(emis, im);

                    if (_.isUndefined(contact.extra.rts_official_district_id)) {
                        // is head teacher
                        school_monitoring_data.created_by = "/api/v1/data/headteacher/" + id + "/";
                    } else {
                        // is district admin
                        school_monitoring_data.created_by_da = "/api/v1/district_admin/" + id + "/";
                    }

                    return go.utils
                        .cms_post("data/school_monitoring/", school_monitoring_data, im)
                        .then(function(result) {
                            contact.extra.school_monitoring_emis = "";
                            return im.contacts
                                .save(contact)
                                .then(function() {
                                    return 'monitor_school_completed';
                                });
                        });
                }
            });
        },

        // q15
        monitor_school_completed: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("Congratulations, you have finished reporting on this school."),

                choices: [
                    new Choice('add_emis_school_monitoring',
                                    $("Report on another school monitoring visit")),
                    new Choice('initial_state', $("Go back to the main menu")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        // q16
        monitor_school_falling_behind: function(name, $) {
            return new ChoiceState(name, {
                question:
                    $("This school is falling behind with their LPIP and E-SIMON " +
                    "responsibilities. Please assist the Head Teacher to catch up."),

                choices: [
                    new Choice('initial_state', $("Go back to the main menu")),
                    new Choice('end_state', $("Exit"))
                ],

                next: function(choice) {
                    return choice.value;
                }
            });
        },

        "commas": "commas"
    };

    return sp;

}();
<|endoftext|>


<commit_before>import fscreen from 'fscreen'
import UserAgentDetector, {UserAgents} from './device-detectors/user-agent-detector'
import UcbrowserEnAndroidStateProvider from './state-providers/ucbrowser-en-android-state-provider'
import ChromeAndroidStateProvider from './state-providers/chrome-android-state-provider'
import ChromeIosStateProvider from './state-providers/chrome-ios-state-provider'
import SafariIphoneStateProvider from './state-providers/safari-iphone-state-provider'
import SafariIpadStateProvider from './state-providers/safari-ipad-state-provider'
import SamsungBrowserStateProvider from './state-providers/samsung-browser-state-provider'
import DesktopStateProvider from './state-providers/desktop-state-provider'
import UnknownStateProvider from './state-providers/unknown-state-provider'
import UcbrowserCnAndroidStateProvider from './state-providers/ucbrowser-cn-android-state-provider'
import UcbrowserIosStateProvider from './state-providers/ucbrowser-ios-state-provider'
import StaticStateProvider from './state-providers/static-state-provider'
import OperaMiniIphoneStateProvider from './state-providers/opera-mini-iphone-state-provider'
import QqCnIphoneStateProvider from './state-providers/qq-cn-iphone-state-provider'
import DeviceDetector from './device-detectors/device-detector'

class BrowserUiState {
    constructor(initialOrientation = null, win = window) {
        this._userAgentDetector = new UserAgentDetector(win.navigator.userAgent)

        switch (this._userAgentDetector.userAgent) {
            case UserAgents.CHROME_ANDROID :
                this._provider = new ChromeAndroidStateProvider(win, initialOrientation); break
            case UserAgents.CHROME_IOS :
                this._provider = new ChromeIosStateProvider(win, initialOrientation); break
            case UserAgents.SAFARI_IPHONE :
                this._provider = new SafariIphoneStateProvider(win, initialOrientation); break
            case UserAgents.SAFARI_IPAD :
                this._provider = new SafariIpadStateProvider(win, initialOrientation); break
            case UserAgents.SAMSUNG_BROWSER :
                this._provider = new SamsungBrowserStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_EN_ANDROID :
                this._provider = new UcbrowserEnAndroidStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_CN_ANDROID :
                this._provider = new UcbrowserCnAndroidStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_IOS :
                this._provider = new UcbrowserIosStateProvider(win, initialOrientation); break
            case UserAgents.OPERA_MINI_IPHONE :
                this._provider = new OperaMiniIphoneStateProvider(win, initialOrientation); break
            case UserAgents.QQ_CN_IPHONE :
                this._provider = new QqCnIphoneStateProvider(win, initialOrientation); break
            case UserAgents.DU_BROWSER :
            case UserAgents.UC_BROWSER_EN_IOS_STATIC :
            case UserAgents.OPERA_MINI_IPHONE_STATIC :
            case UserAgents.OPERA_MINI_IPAD :
                this._provider = new StaticStateProvider(win, initialOrientation); break
            case UserAgents.DESKTOP :
                this._provider = new DesktopStateProvider(win, initialOrientation); break
            default :
                this._provider = new UnknownStateProvider(win, initialOrientation)
        }

        //Thanx Opera Mini iOS for this :( It has completely the same user-agent as Safari on homescreen/standalone
        if (win.navigator.standalone && DeviceDetector.isIphone(win.navigator.userAgent)) {
            this._provider = new SafariIphoneStateProvider(win, initialOrientation)
        } else if (win.navigator.standalone && DeviceDetector.isIpad(win.navigator.userAgent)) {
            this._provider = new SafariIpadStateProvider(win, initialOrientation)
        }
    }

    get orientation() {
        return this._provider.orientation
    }

    get screenAspectRatio() {
        return this._provider.screenAspectRatio
    }

    get viewportAspectRatio() {
        return this._provider.viewportAspectRatio
    }

    get delta() {
        return this._provider.delta
    }

    get deviation() {
        return this._provider.deviation
    }

    get collapsedThreshold() {
        return this._provider.collapsedThreshold
    }

    get keyboardThreshold() {
        return this._provider.keyboardThreshold
    }

    get state() {
        return this._provider.state
    }

    get fscreen() {
        return fscreen
    }
}

export default BrowserUiState<commit_msg>Added TODO (patch)<commit_after>import fscreen from 'fscreen'
import UserAgentDetector, {UserAgents} from './device-detectors/user-agent-detector'
import UcbrowserEnAndroidStateProvider from './state-providers/ucbrowser-en-android-state-provider'
import ChromeAndroidStateProvider from './state-providers/chrome-android-state-provider'
import ChromeIosStateProvider from './state-providers/chrome-ios-state-provider'
import SafariIphoneStateProvider from './state-providers/safari-iphone-state-provider'
import SafariIpadStateProvider from './state-providers/safari-ipad-state-provider'
import SamsungBrowserStateProvider from './state-providers/samsung-browser-state-provider'
import DesktopStateProvider from './state-providers/desktop-state-provider'
import UnknownStateProvider from './state-providers/unknown-state-provider'
import UcbrowserCnAndroidStateProvider from './state-providers/ucbrowser-cn-android-state-provider'
import UcbrowserIosStateProvider from './state-providers/ucbrowser-ios-state-provider'
import StaticStateProvider from './state-providers/static-state-provider'
import OperaMiniIphoneStateProvider from './state-providers/opera-mini-iphone-state-provider'
import QqCnIphoneStateProvider from './state-providers/qq-cn-iphone-state-provider'
import DeviceDetector from './device-detectors/device-detector'

//TODO hide internals in private scope using WeakMaps and expose just strings for debug widgets
class BrowserUiState {
    constructor(initialOrientation = null, win = window) {
        this._userAgentDetector = new UserAgentDetector(win.navigator.userAgent)

        switch (this._userAgentDetector.userAgent) {
            case UserAgents.CHROME_ANDROID :
                this._provider = new ChromeAndroidStateProvider(win, initialOrientation); break
            case UserAgents.CHROME_IOS :
                this._provider = new ChromeIosStateProvider(win, initialOrientation); break
            case UserAgents.SAFARI_IPHONE :
                this._provider = new SafariIphoneStateProvider(win, initialOrientation); break
            case UserAgents.SAFARI_IPAD :
                this._provider = new SafariIpadStateProvider(win, initialOrientation); break
            case UserAgents.SAMSUNG_BROWSER :
                this._provider = new SamsungBrowserStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_EN_ANDROID :
                this._provider = new UcbrowserEnAndroidStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_CN_ANDROID :
                this._provider = new UcbrowserCnAndroidStateProvider(win, initialOrientation); break
            case UserAgents.UC_BROWSER_IOS :
                this._provider = new UcbrowserIosStateProvider(win, initialOrientation); break
            case UserAgents.OPERA_MINI_IPHONE :
                this._provider = new OperaMiniIphoneStateProvider(win, initialOrientation); break
            case UserAgents.QQ_CN_IPHONE :
                this._provider = new QqCnIphoneStateProvider(win, initialOrientation); break
            case UserAgents.DU_BROWSER :
            case UserAgents.UC_BROWSER_EN_IOS_STATIC :
            case UserAgents.OPERA_MINI_IPHONE_STATIC :
            case UserAgents.OPERA_MINI_IPAD :
                this._provider = new StaticStateProvider(win, initialOrientation); break
            case UserAgents.DESKTOP :
                this._provider = new DesktopStateProvider(win, initialOrientation); break
            default :
                this._provider = new UnknownStateProvider(win, initialOrientation)
        }

        //Thanx Opera Mini iOS for this :( It has completely the same user-agent as Safari on homescreen/standalone
        if (win.navigator.standalone && DeviceDetector.isIphone(win.navigator.userAgent)) {
            this._provider = new SafariIphoneStateProvider(win, initialOrientation)
        } else if (win.navigator.standalone && DeviceDetector.isIpad(win.navigator.userAgent)) {
            this._provider = new SafariIpadStateProvider(win, initialOrientation)
        }
    }

    get orientation() {
        return this._provider.orientation
    }

    get screenAspectRatio() {
        return this._provider.screenAspectRatio
    }

    get viewportAspectRatio() {
        return this._provider.viewportAspectRatio
    }

    get delta() {
        return this._provider.delta
    }

    get deviation() {
        return this._provider.deviation
    }

    get collapsedThreshold() {
        return this._provider.collapsedThreshold
    }

    get keyboardThreshold() {
        return this._provider.keyboardThreshold
    }

    get state() {
        return this._provider.state
    }

    get fscreen() {
        return fscreen
    }
}

export default BrowserUiState<|endoftext|>


<commit_before>
const util = require('util'),
      // disabling import rule because `electron` is a built-in module
      // eslint-disable-next-line import/no-unresolved
      electron = require('electron'),
      sliced = require('sliced'),
      getBrowserWindowOpts = require('./getBrowserWindowOpts'),
      listenRequestsInPage = require('./listenRequestsInPage'),
      conversionScript = require('./conversionScript'),
      evaluate = require('./evaluateJS'),
      parentChannel = require('../ipc')(process),
      app = electron.app,
      renderer = electron.ipcMain,
      BrowserWindow = electron.BrowserWindow;

let windows = [],
    electronVersion,
    log,
    WORKER_ID,
    DEBUG_MODE,
    CHROME_COMMAND_LINE_SWITCHES,
    ALLOW_LOCAL_FILES_ACCESS,
    MAX_LOG_ENTRY_SIZE;

WORKER_ID = process.env.ELECTRON_WORKER_ID;
DEBUG_MODE = Boolean(process.env.ELECTRON_HTML_TO_DEBUGGING);
CHROME_COMMAND_LINE_SWITCHES = JSON.parse(process.env.chromeCommandLineSwitches);
ALLOW_LOCAL_FILES_ACCESS = process.env.allowLocalFilesAccess === 'true';
MAX_LOG_ENTRY_SIZE = parseInt(process.env.maxLogEntrySize, 10);

if (process.versions.electron) {
  electronVersion = process.versions.electron;
} else if (process.versions['atom-shell']) {
  electronVersion = process.versions['atom-shell'];
} else {
  electronVersion = '';
}

if (isNaN(MAX_LOG_ENTRY_SIZE)) {
  MAX_LOG_ENTRY_SIZE = 1000;
}

log = function() {
  // eslint-disable-next-line prefer-rest-params
  let newArgs = sliced(arguments);

  newArgs.unshift(`[Worker ${WORKER_ID}]`);

  parentChannel.emit.apply(parentChannel, ['log'].concat(newArgs));
};

global.windowsData = {};
global.windowsLogs = {};

Object.keys(CHROME_COMMAND_LINE_SWITCHES).forEach((switchName) => {
  let switchValue = CHROME_COMMAND_LINE_SWITCHES[switchName];

  if (switchValue != null) {
    log(`establishing chrome command line switch [${switchName}:${switchValue}]`);
    app.commandLine.appendSwitch(switchName, switchValue);
  } else {
    log(`establishing chrome command line switch [${switchName}]`);
    app.commandLine.appendSwitch(switchName);
  }
});

if (app.dock && typeof app.dock.hide === 'function') {
  if (!DEBUG_MODE) {
    app.dock.hide();
  }
}

app.on('window-all-closed', () => {
  // by default dont close the app (because the electron ipc will be running)
  // only close when debug mode is on
  if (DEBUG_MODE) {
    app.quit();
  }
});

app.on('ready', () => {
  log('electron process ready..');

  renderer.on('page-error', (ev, windowId, errMsg, errStack) => {
    // saving errors on page
    saveLogsInStore(global.windowsLogs[windowId], 'warn', `error in page: ${errMsg}`);

    saveLogsInStore(global.windowsLogs[windowId], 'warn', `error in page stack: ${errStack}`);

    parentChannel.emit('page-error', windowId, errMsg, errStack);
  });

  renderer.on('page-log', (ev, args) => {
    let windowId = args[0],
        logLevel = args[1],
        logArgs = args.slice(2),
        // removing log level argument
        newArgs = args.slice(0, 1).concat(logArgs);

    // saving logs
    saveLogsInStore(global.windowsLogs[windowId], logLevel, logArgs);

    parentChannel.emit.apply(parentChannel, ['page-log'].concat(newArgs));
  });

  renderer.on('log', function() {
    // eslint-disable-next-line prefer-rest-params
    let newArgs = sliced(arguments),
        windowId = newArgs.splice(0, 2)[1];

    newArgs.unshift(`[Browser window - ${windowId} log ]:`);

    log.apply(log, newArgs);
  });

  // communication with electron-workers ipc
  process.on('message', (data) => {
    if (!data) {
      return;
    }

    function respondIpc(err, payload) {
      let msg = {
        workerEvent: 'taskResponse',
        taskId: data.taskId
      };

      if (err) {
        msg.error = err;
      } else {
        msg.response = payload;
      }

      process.send(msg);
    }

    if (data.workerEvent === 'ping') {
      process.send({ workerEvent: 'pong' });
    } else if (data.workerEvent === 'task') {
      log('new task for electron-ipc..');

      try {
        createBrowserWindow(respondIpc, data.payload);
      } catch (uncaughtErr) {
        respondIpc(uncaughtErr);
      }
    }
  });
});

function createBrowserWindow(respondIpc, settingsData) {
  let evaluateInWindow,
      dataForWindow = {},
      browserWindowOpts,
      converterPath,
      converter,
      currentWindow,
      currentWindowId,
      extraHeaders = '';

  function respond(err, data) {
    log('finishing work in browser-window..');

    if (err) {
      return respondIpc(err);
    }

    if (settingsData.collectLogs) {
      // eslint-disable-next-line no-param-reassign
      data.logs = global.windowsLogs[currentWindowId];
    } else {
      // eslint-disable-next-line no-param-reassign
      data.logs = [];
    }

    respondIpc(null, data);

    if (!currentWindow) {
      return;
    }

    // in debug mode, don't destroy the browser window
    if (!DEBUG_MODE) {
      log('destroying browser window..');
      currentWindow.destroy();
    }
  }

  converterPath = settingsData.converterPath;

  log(`requiring converter module from ${converterPath}`);

  try {
    // eslint-disable-next-line global-require
    converter = require(converterPath);
  } catch (requireErr) {
    return respond(requireErr);
  }

  if (settingsData.waitForJS) {
    log('waitForJS enabled..');

    dataForWindow.waitForJS = settingsData.waitForJS;
    dataForWindow.waitForJSVarName = settingsData.waitForJSVarName;
  }

  // get browser window options with defaults
  browserWindowOpts = getBrowserWindowOpts(settingsData.browserWindow);

  log('creating new browser window with options:', browserWindowOpts);

  if (DEBUG_MODE) {
    browserWindowOpts.show = true;
  }

  if (browserWindowOpts.show) {
    log('browser window visibility activated');
  }

  currentWindow = new BrowserWindow(browserWindowOpts);
  currentWindowId = currentWindow.id;
  addWindow(currentWindow);

  evaluateInWindow = evaluate(currentWindow);
  global.windowsData[currentWindowId] = dataForWindow;
  global.windowsLogs[currentWindowId] = [];

  saveLogsInStore(
    global.windowsLogs[currentWindowId],
    'debug',
    `Converting using electron-ipc strategy in electron ${electronVersion}`
  );

  currentWindow.webContents.setAudioMuted(true);

  listenRequestsInPage(
    currentWindow,
    {
      allowLocalFilesAccess: ALLOW_LOCAL_FILES_ACCESS,
      pageUrl: settingsData.url
    },
    log,
    saveLogsInStore(global.windowsLogs[currentWindowId])
  );

  currentWindow.on('closed', () => {
    log('browser-window closed..');

    delete global.windowsData[currentWindowId];
    delete global.windowsLogs[currentWindowId];

    removeWindow(currentWindow);
    currentWindow = null;
  });

  conversionScript(settingsData, currentWindow, evaluateInWindow, log, converter, respond);

  if (settingsData.userAgent) {
    log(`setting up custom user agent: ${settingsData.userAgent}`);
    currentWindow.webContents.setUserAgent(settingsData.userAgent);
  }

  if (typeof settingsData.extraHeaders === 'object') {
    Object.keys(settingsData.extraHeaders).forEach((key) => {
      extraHeaders += `${key}: ${settingsData.extraHeaders[key]}\n`;
    });
  }

  log(util.format('loading url in browser window: %s, with headers: %s', settingsData.url, extraHeaders));

  if (extraHeaders) {
    currentWindow.loadURL(settingsData.url, {
      extraHeaders
    });
  } else {
    currentWindow.loadURL(settingsData.url);
  }

  // useful in windows to prevent the electron process to hang..
  currentWindow.focus();
}

function addWindow(browserWindow) {
  windows.push(browserWindow);
}

function removeWindow(browserWindow) {
  windows.forEach((win, index) => {
    if (win === browserWindow) {
      windows.splice(index, 1);
    }
  });
}

function saveLogsInStore(store, level, msg) {
  // eslint-disable-next-line prefer-rest-params
  let args = sliced(arguments);

  if (args.length === 1) {
    return _saveLogs.bind(undefined, store);
  }

  return _saveLogs(store, level, msg);

  function _saveLogs(_store, _level, _msg) {
    _store.push({
      level: _level,
      message: trimMessage(_msg),
      timestamp: new Date().getTime()
    });
  }
}

function trimMessage(args) {
  let message = args;

  if (Array.isArray(args)) {
    message = args.join(' ');
  }

  if (message.length > MAX_LOG_ENTRY_SIZE) {
    return `${message.substring(0, MAX_LOG_ENTRY_SIZE)}...`;
  }

  return message;
}
<commit_msg>[fix] error message lost when using ipc strategy<commit_after>
const util = require('util'),
      // disabling import rule because `electron` is a built-in module
      // eslint-disable-next-line import/no-unresolved
      electron = require('electron'),
      sliced = require('sliced'),
      assign = require('object-assign'),
      getBrowserWindowOpts = require('./getBrowserWindowOpts'),
      listenRequestsInPage = require('./listenRequestsInPage'),
      conversionScript = require('./conversionScript'),
      evaluate = require('./evaluateJS'),
      parentChannel = require('../ipc')(process),
      app = electron.app,
      renderer = electron.ipcMain,
      BrowserWindow = electron.BrowserWindow;

let windows = [],
    electronVersion,
    log,
    WORKER_ID,
    DEBUG_MODE,
    CHROME_COMMAND_LINE_SWITCHES,
    ALLOW_LOCAL_FILES_ACCESS,
    MAX_LOG_ENTRY_SIZE;

WORKER_ID = process.env.ELECTRON_WORKER_ID;
DEBUG_MODE = Boolean(process.env.ELECTRON_HTML_TO_DEBUGGING);
CHROME_COMMAND_LINE_SWITCHES = JSON.parse(process.env.chromeCommandLineSwitches);
ALLOW_LOCAL_FILES_ACCESS = process.env.allowLocalFilesAccess === 'true';
MAX_LOG_ENTRY_SIZE = parseInt(process.env.maxLogEntrySize, 10);

if (process.versions.electron) {
  electronVersion = process.versions.electron;
} else if (process.versions['atom-shell']) {
  electronVersion = process.versions['atom-shell'];
} else {
  electronVersion = '';
}

if (isNaN(MAX_LOG_ENTRY_SIZE)) {
  MAX_LOG_ENTRY_SIZE = 1000;
}

log = function() {
  // eslint-disable-next-line prefer-rest-params
  let newArgs = sliced(arguments);

  newArgs.unshift(`[Worker ${WORKER_ID}]`);

  parentChannel.emit.apply(parentChannel, ['log'].concat(newArgs));
};

global.windowsData = {};
global.windowsLogs = {};

Object.keys(CHROME_COMMAND_LINE_SWITCHES).forEach((switchName) => {
  let switchValue = CHROME_COMMAND_LINE_SWITCHES[switchName];

  if (switchValue != null) {
    log(`establishing chrome command line switch [${switchName}:${switchValue}]`);
    app.commandLine.appendSwitch(switchName, switchValue);
  } else {
    log(`establishing chrome command line switch [${switchName}]`);
    app.commandLine.appendSwitch(switchName);
  }
});

if (app.dock && typeof app.dock.hide === 'function') {
  if (!DEBUG_MODE) {
    app.dock.hide();
  }
}

app.on('window-all-closed', () => {
  // by default dont close the app (because the electron ipc will be running)
  // only close when debug mode is on
  if (DEBUG_MODE) {
    app.quit();
  }
});

app.on('ready', () => {
  log('electron process ready..');

  renderer.on('page-error', (ev, windowId, errMsg, errStack) => {
    // saving errors on page
    saveLogsInStore(global.windowsLogs[windowId], 'warn', `error in page: ${errMsg}`);

    saveLogsInStore(global.windowsLogs[windowId], 'warn', `error in page stack: ${errStack}`);

    parentChannel.emit('page-error', windowId, errMsg, errStack);
  });

  renderer.on('page-log', (ev, args) => {
    let windowId = args[0],
        logLevel = args[1],
        logArgs = args.slice(2),
        // removing log level argument
        newArgs = args.slice(0, 1).concat(logArgs);

    // saving logs
    saveLogsInStore(global.windowsLogs[windowId], logLevel, logArgs);

    parentChannel.emit.apply(parentChannel, ['page-log'].concat(newArgs));
  });

  renderer.on('log', function() {
    // eslint-disable-next-line prefer-rest-params
    let newArgs = sliced(arguments),
        windowId = newArgs.splice(0, 2)[1];

    newArgs.unshift(`[Browser window - ${windowId} log ]:`);

    log.apply(log, newArgs);
  });

  // communication with electron-workers ipc
  process.on('message', (data) => {
    if (!data) {
      return;
    }

    function respondIpc(err, payload) {
      let msg = {
        workerEvent: 'taskResponse',
        taskId: data.taskId
      };

      if (err) {
        // err.message is lost when serialized, so we need to explicitly set it
        msg.error = assign({}, err);
        msg.error.message = err.message;
      } else {
        msg.response = payload;
      }

      process.send(msg);
    }

    if (data.workerEvent === 'ping') {
      process.send({ workerEvent: 'pong' });
    } else if (data.workerEvent === 'task') {
      log('new task for electron-ipc..');

      try {
        createBrowserWindow(respondIpc, data.payload);
      } catch (uncaughtErr) {
        respondIpc(uncaughtErr);
      }
    }
  });
});

function createBrowserWindow(respondIpc, settingsData) {
  let evaluateInWindow,
      dataForWindow = {},
      browserWindowOpts,
      converterPath,
      converter,
      currentWindow,
      currentWindowId,
      extraHeaders = '';

  function respond(err, data) {
    log('finishing work in browser-window..');

    if (err) {
      return respondIpc(err);
    }

    if (settingsData.collectLogs) {
      // eslint-disable-next-line no-param-reassign
      data.logs = global.windowsLogs[currentWindowId];
    } else {
      // eslint-disable-next-line no-param-reassign
      data.logs = [];
    }

    respondIpc(null, data);

    if (!currentWindow) {
      return;
    }

    // in debug mode, don't destroy the browser window
    if (!DEBUG_MODE) {
      log('destroying browser window..');
      currentWindow.destroy();
    }
  }

  converterPath = settingsData.converterPath;

  log(`requiring converter module from ${converterPath}`);

  try {
    // eslint-disable-next-line global-require
    converter = require(converterPath);
  } catch (requireErr) {
    return respond(requireErr);
  }

  if (settingsData.waitForJS) {
    log('waitForJS enabled..');

    dataForWindow.waitForJS = settingsData.waitForJS;
    dataForWindow.waitForJSVarName = settingsData.waitForJSVarName;
  }

  // get browser window options with defaults
  browserWindowOpts = getBrowserWindowOpts(settingsData.browserWindow);

  log('creating new browser window with options:', browserWindowOpts);

  if (DEBUG_MODE) {
    browserWindowOpts.show = true;
  }

  if (browserWindowOpts.show) {
    log('browser window visibility activated');
  }

  currentWindow = new BrowserWindow(browserWindowOpts);
  currentWindowId = currentWindow.id;
  addWindow(currentWindow);

  evaluateInWindow = evaluate(currentWindow);
  global.windowsData[currentWindowId] = dataForWindow;
  global.windowsLogs[currentWindowId] = [];

  saveLogsInStore(
    global.windowsLogs[currentWindowId],
    'debug',
    `Converting using electron-ipc strategy in electron ${electronVersion}`
  );

  currentWindow.webContents.setAudioMuted(true);

  listenRequestsInPage(
    currentWindow,
    {
      allowLocalFilesAccess: ALLOW_LOCAL_FILES_ACCESS,
      pageUrl: settingsData.url
    },
    log,
    saveLogsInStore(global.windowsLogs[currentWindowId])
  );

  currentWindow.on('closed', () => {
    log('browser-window closed..');

    delete global.windowsData[currentWindowId];
    delete global.windowsLogs[currentWindowId];

    removeWindow(currentWindow);
    currentWindow = null;
  });

  conversionScript(settingsData, currentWindow, evaluateInWindow, log, converter, respond);

  if (settingsData.userAgent) {
    log(`setting up custom user agent: ${settingsData.userAgent}`);
    currentWindow.webContents.setUserAgent(settingsData.userAgent);
  }

  if (typeof settingsData.extraHeaders === 'object') {
    Object.keys(settingsData.extraHeaders).forEach((key) => {
      extraHeaders += `${key}: ${settingsData.extraHeaders[key]}\n`;
    });
  }

  log(util.format('loading url in browser window: %s, with headers: %s', settingsData.url, extraHeaders));

  if (extraHeaders) {
    currentWindow.loadURL(settingsData.url, {
      extraHeaders
    });
  } else {
    currentWindow.loadURL(settingsData.url);
  }

  // useful in windows to prevent the electron process to hang..
  currentWindow.focus();
}

function addWindow(browserWindow) {
  windows.push(browserWindow);
}

function removeWindow(browserWindow) {
  windows.forEach((win, index) => {
    if (win === browserWindow) {
      windows.splice(index, 1);
    }
  });
}

function saveLogsInStore(store, level, msg) {
  // eslint-disable-next-line prefer-rest-params
  let args = sliced(arguments);

  if (args.length === 1) {
    return _saveLogs.bind(undefined, store);
  }

  return _saveLogs(store, level, msg);

  function _saveLogs(_store, _level, _msg) {
    _store.push({
      level: _level,
      message: trimMessage(_msg),
      timestamp: new Date().getTime()
    });
  }
}

function trimMessage(args) {
  let message = args;

  if (Array.isArray(args)) {
    message = args.join(' ');
  }

  if (message.length > MAX_LOG_ENTRY_SIZE) {
    return `${message.substring(0, MAX_LOG_ENTRY_SIZE)}...`;
  }

  return message;
}
<|endoftext|>


<commit_before>(function ($) {

$(document).on('mouseenter', '#layout_content img:not(.big-image-handled)', function () {
    var $that   = $(this),
        $parent = $that.parent(),
        src     = $that.attr('src'),
        width   = parseInt($that.width(), 10),
        height  = parseInt($that.height(), 10),
        img;

    if ($that.is('[class*=avatar][class*=user]')
        || ($parent.is('a[href]') && $parent.attr('href') !== src)
        || $that.attr('src').match(STUDIP.ASSETS_URL + 'images/icons/'))
    {
        // Prevent any further handling
        $(this).addClass('big-image-handled');
        return;
    }

    img = new Image();
    img.onload = function () {
        if (img.width > width * window.devicePixelRatio || img.height > height * window.devicePixelRatio) {
            var offset = $that.offset(),
                info   = $('
').hide(),
                text   = 'Dieses Bild wird verkleinert dargestellt.'.toLocaleString() + ' ';
            if (width >= 500) {
                text += '<nobr>' + 'Klicken Sie auf das Bild, um es in Originalgre zu ffnen.'.toLocaleString() + '</nobr>';
            } else if (width >= 300) {
                text += '<nobr>' + 'Klicken fr Originalgre.'.toLocaleString() + '</nobr>';
            } else if (width < 100) {
                text = $('').attr('title', text).attr('src', STUDIP.ASSETS_URL + 'images/icons/16/white/visibility-invisible.png');
            }

            info.addClass('big-image-info').html(text);

            $that.wrap('
').after(info).addClass('big-image-handled');
        }
    };
    img.src = src;
});

$(document).on('mouseenter', '.big-image-handler', function () {
    $('div', this).stop(true, true).show('slide', {direction: 'up'});
}).on('mouseleave', '.big-image-handler', function () {
    $('div', this).stop(true, true).hide('slide', {direction: 'up'});
}).on('click', '.big-image-handler', function (event) {
    // Open as full screen lightbox
    var src     = $('img', this).attr('src'),
        img     = $(''),
        close   = $('
'),
        overlay = $('
'),
        offset;

    img.load(function () {
        offset = img.position();
        close.css({
            left: offset.left + img.width() - 18,
            top: offset.top + 2
        }).insertAfter(img);
    });
    img.attr('src', src);

    overlay.append(img).appendTo('body').click(function () {
        $(document).off('keypress.big-image');
        $(this).remove();
    });

    $(document).on('keypress.big-image', function (event) {
        if (event.key === 'Esc') {
            overlay.click();
        }
    });

    event.preventDefault();
});

}(jQuery));
<commit_msg>exlude svg, fixes #4<commit_after>(function ($) {

$(document).on('mouseenter', '#layout_content img:not(.big-image-handled)', function () {
    var $that   = $(this),
        $parent = $that.parent(),
        src     = $that.attr('src'),
        width   = parseInt($that.width(), 10),
        height  = parseInt($that.height(), 10),
        img;

    if ($that.is('[class*=avatar][class*=user]')
        || ($parent.is('a[href]') && $parent.attr('href') !== src)
        || src.match(STUDIP.ASSETS_URL + 'images/icons/')
        || src.match(/\.svg/))
    {
        // Prevent any further handling
        $(this).addClass('big-image-handled');
        return;
    }

    img = new Image();
    img.onload = function () {
        if (img.width > width * window.devicePixelRatio || img.height > height * window.devicePixelRatio) {
            var offset = $that.offset(),
                info   = $('
').hide(),
                text   = 'Dieses Bild wird verkleinert dargestellt.'.toLocaleString() + ' ';
            if (width >= 500) {
                text += '<nobr>' + 'Klicken Sie auf das Bild, um es in Originalgröße zu öffnen.'.toLocaleString() + '</nobr>';
            } else if (width >= 300) {
                text += '<nobr>' + 'Klicken für Originalgröße.'.toLocaleString() + '</nobr>';
            } else if (width < 100) {
                text = $('').attr('title', text).attr('src', STUDIP.ASSETS_URL + 'images/icons/16/white/visibility-invisible.png');
            }

            info.addClass('big-image-info').html(text);

            $that.wrap('
').after(info).addClass('big-image-handled');
        }
    };
    img.src = src;
});

$(document).on('mouseenter', '.big-image-handler', function () {
    $('div', this).stop(true, true).show('slide', {direction: 'up'});
}).on('mouseleave', '.big-image-handler', function () {
    $('div', this).stop(true, true).hide('slide', {direction: 'up'});
}).on('click', '.big-image-handler', function (event) {
    // Open as full screen lightbox
    var src     = $('img', this).attr('src'),
        img     = $(''),
        close   = $('
'),
        overlay = $('
'),
        offset;

    img.load(function () {
        offset = img.position();
        close.css({
            left: offset.left + img.width() - 18,
            top: offset.top + 2
        }).insertAfter(img);
    });
    img.attr('src', src);

    overlay.append(img).appendTo('body').click(function () {
        $(document).off('keypress.big-image');
        $(this).remove();
    });

    $(document).on('keypress.big-image', function (event) {
        if (event.key === 'Esc') {
            overlay.click();
        }
    });

    event.preventDefault();
});

}(jQuery));
<|endoftext|>


<commit_before>//http://stackoverflow.com/questions/5203407/javascript-multiple-keys-pressed-at-once
//http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
var injectedJS = "(function(original) {    Element.prototype.addEventListener = function(type, listener, useCapture) { 	var attr = this.getAttribute('_handlerTypes'); 	var types = attr ? attr.split(',') : []; 	var found = false; 	for (var i = 0; i < types.length; ++i) { 	  if (types[i] == type) { 		found = true; 		break; 	  } 	} 	if (!found) { 	  types.push(type); 	}        	this.setAttribute('_handlerTypes', types.join(',')); 	return original.apply(this, arguments);   } })(Element.prototype.addEventListener); (function(original) {   Element.prototype.removeEventListener = function(type, listener, useCapture) { 	var attr = this.getAttribute('_handlerTypes'); 	var types = attr ? attr.split(',') : []; 	var removed = false; 	for (var i = 0; i < types.length; ++i) { 	  if (types[i] == type) { 		types.splice(i, 1); 		removed = true;    		break;        	  }         	}        	if (removed) { 	  this.setAttribute('_handlerTypes', types.join(',')); 	} 	return original.apply(this, arguments);   } })(Element.prototype.removeEventListener);";
var script = document.createElement("script");
script.type = "text/javascript";
script.appendChild(document.createTextNode(injectedJS));
document.documentElement.appendChild(script);

(function ($) {
	var elements = [];
	var tooltips = [];
	var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","-","=","[","]","\\","/","."];
	// necessary because fucking keydown.keyCode + String.fromCharCode are totally unrealiable
	var keyCodes = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 189, 187, 219, 221, 220, 191, 190];
	var keymap = [];
	var tooltipsVisible = false;
	var offset = 0;

	function addTooltip(elm, letter) {
		var style = "position: absolute;z-index: 999999999;color: rgba(0,0,0,0.8);font-weight: bold;text-indent: 0px;background: rgba(255,255,255,0.8);"
					+ "float: none;width: 25px;height: 20px;border: 1px solid rgba(0,0,0,0.3);text-align: center;vertical-align: middle;"
					+ "margin: 0px 0px 0px 0px;font-family: Arial;font-size: 14px;padding: 0px 0px 0px 0px;outline: none;";

		var tooltip = $(''+letter+'');
		tooltip.css('top',(elm.offset().top) + 'px');
		tooltip.css('left',(elm.offset().left) + 'px');

		$('body').append(tooltip);
		tooltips.push(tooltip);
	}
	function addTooltips() {

		var i = 0;
		var offsetSkip = offset;
		$.each(getClickable().filter(function(idx,val){return isScrolledIntoView(val);}), function(idx, val){
			if (i == letters.length) return;
			if (offsetSkip-- > 0) return; // pulamos offset vezes
			elements[letters[i++]] = {elm: val, type: 'click'};
		});

		$.each(getFocusable().filter(function(idx,val){return isScrolledIntoView(val);}), function(idx, val){
			if (i == letters.length) return;
			if (offsetSkip-- > 0) return; // pulamos offset vezes
			elements[letters[i++]] = {elm: val, type: 'focus'};
		});

		elements = unique(elements);

		for (i = 0; i<letters.length;i++){
			var val = letters[i];
			if (elements[val] == undefined){
				console.log(val);
				break;
			}
			addTooltip($(elements[val].elm), val);
		}
	}

	function getClickable(){
		var clickable = $('*').filter(function(idx, val){
							// TODO: getEventListeners nao existe fora do console...
							// to testando ver se _handlerTypes do tests.html funciona
							// se funcionar da pra combinar ele com .onclick != undefined ou algo assim
							var evts = this.getAttribute('_handlerTypes');
							return this.onclick != undefined || (evts != undefined && evts.indexOf('click') >= 0);
						});
		$('input[type="submit"],input[type="file"],a[href]').each(function(idx,val){clickable.push(val);});
		return unique(clickable);
	}

	function unique(array){
		array = array.toArray != undefined ? array.toArray() : array;
		return array.filter(function (idx) {
			return array.lastIndexOf(array[idx]) === idx;
		});
	}

	function getFocusable(){
		var focusable = $('*').filter(function(idx, val){
							var evts = this.getAttribute('_handlerTypes');
							return this.onfocus != undefined || (evts != undefined && evts.indexOf('focus') >= 0)
								|| this.onmouseover != undefined || (evts != undefined && evts.indexOf('onmouseover') >= 0);
						});
		$('select,input[type="text"]').each(function(idx,val){focusable.push(val);});
		return focusable;
	}

	function isScrolledIntoView(elem) {
		var docViewTop = $(window).scrollTop();
		var docViewBottom = docViewTop + $(window).height();

		var elemTop = $(elem).offset().top;
		var elemBottom = elemTop + $(elem).height();

		return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
	}

	function removeTooltips(){
		for (var i=0;i<tooltips.length;i++){
			$(tooltips[i]).remove();
		}
	}

	function simulatedClick(target, options) {
		var event = target.ownerDocument.createEvent('MouseEvents'),
			options = options || {};

		//Set your default options to the right of ||
		var opts = {
			type: options.type                  || 'click',
			canBubble:options.canBubble             || true,
			cancelable:options.cancelable           || true,
			view:options.view                       || target.ownerDocument.defaultView,
			detail:options.detail                   || 1,
			screenX:options.screenX                 || 0, //The coordinates within the entire page
			screenY:options.screenY                 || 0,
			clientX:options.clientX                 || 0, //The coordinates within the viewport
			clientY:options.clientY                 || 0,
			ctrlKey:options.ctrlKey                 || false,
			altKey:options.altKey                   || false,
			shiftKey:options.shiftKey               || false,
			metaKey:options.metaKey                 || false, //I *think* 'meta' is 'Cmd/Apple' on Mac, and 'Windows key' on Win. Not sure, though!
			button:options.button                   || 0, //0 = left, 1 = middle, 2 = right
			relatedTarget:options.relatedTarget     || null,
		}

		//Pass in the options
		event.initMouseEvent(
			opts.type,
			opts.canBubble,
			opts.cancelable,
			opts.view,
			opts.detail,
			opts.screenX,
			opts.screenY,
			opts.clientX,
			opts.clientY,
			opts.ctrlKey,
			opts.altKey,
			opts.shiftKey,
			opts.metaKey,
			opts.button,
			opts.relatedTarget
		);

		//Fire the event
		target.dispatchEvent(event);
	}

	var handler = function(e){
		if(keymap[17] && keymap[16] && keymap[18] && (e.keyCode == 16 || e.keyCode == 17 || e.keyCode == 18) && e.type=='keyup'){
			console.log('removeTooltips');
			removeTooltips();
			tooltipsVisible = false;
		}

		if (tooltipsVisible){
			// detects up or down, to shift tooltip placement
			if (e.keyCode == 38 || e.keyCode == 40) { // up or down
				var change = e.keyCode - 39;
				offset = offset + (change * 10);

				if (offset < 0)
					offset = 0;
				// if (offset+letters.length > elements.length)
					// offset = elements.length-letters.length;

				console.log('offsetting: ' + offset);
				removeTooltips();
				addTooltips();
			}

			// detects hotkey pressed
			var letter = letters[keyCodes.indexOf(e.keyCode)]; // necessary because fucking keydown.keyCode + String.fromCharCode are totally unrealiable
			var elementFromLetter = elements[letter];
			if (!keymap[e.keyCode] && elementFromLetter != undefined) {
				var elm = elementFromLetter;
				if (elm.type == 'click') {
					console.log('clicking...');
					console.log(elm.elm);
					simulatedClick(elm.elm);
				}
				else if (elm.type == 'focus') {
					console.log('focusing...');
					console.log(elm.elm);
					elm.elm.focus();
				}
			}
		}

		keymap[e.keyCode] = e.type == 'keydown';

		if(keymap[17] && keymap[16] && keymap[18]){
			if (tooltipsVisible == true)
				return;
			tooltipsVisible = true;
			offset = 0;
			addTooltips();
		}
	}
	window.onkeydown = handler;
	window.onkeyup = handler;
})(Zepto || jQuery);
//})(jQuery);
<commit_msg>Altering code to use keyCodes instead of letters. Detecting if somehow zepto got the Window element inside my element arrays.<commit_after>//http://stackoverflow.com/questions/5203407/javascript-multiple-keys-pressed-at-once
//http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
var injectedJS = "(function(original) {    Element.prototype.addEventListener = function(type, listener, useCapture) { 	var attr = this.getAttribute('_handlerTypes'); 	var types = attr ? attr.split(',') : []; 	var found = false; 	for (var i = 0; i < types.length; ++i) { 	  if (types[i] == type) { 		found = true; 		break; 	  } 	} 	if (!found) { 	  types.push(type); 	}        	this.setAttribute('_handlerTypes', types.join(',')); 	return original.apply(this, arguments);   } })(Element.prototype.addEventListener); (function(original) {   Element.prototype.removeEventListener = function(type, listener, useCapture) { 	var attr = this.getAttribute('_handlerTypes'); 	var types = attr ? attr.split(',') : []; 	var removed = false; 	for (var i = 0; i < types.length; ++i) { 	  if (types[i] == type) { 		types.splice(i, 1); 		removed = true;    		break;        	  }         	}        	if (removed) { 	  this.setAttribute('_handlerTypes', types.join(',')); 	} 	return original.apply(this, arguments);   } })(Element.prototype.removeEventListener);";
var script = document.createElement("script");
script.type = "text/javascript";
script.appendChild(document.createTextNode(injectedJS));
document.documentElement.appendChild(script);

(function ($) {
	var elements = []; // array of elements for each valid keyCode
	var tooltips = [];
	var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","-","=","[","]","\\","/","."];
	// necessary because fucking keydown.keyCode + String.fromCharCode are totally unrealiable
	var keyCodes = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 189, 187, 219, 221, 220, 191, 190];
	var keymap = [];
	var tooltipsVisible = false;
	var offset = 0;

	function addTooltip(elm, letter) {
		var style = "position: absolute;z-index: 999999999;color: rgba(0,0,0,0.8);font-weight: bold;text-indent: 0px;background: rgba(255,255,255,0.8);"
					+ "float: none;width: 25px;height: 20px;border: 1px solid rgba(0,0,0,0.3);text-align: center;vertical-align: middle;"
					+ "margin: 0px 0px 0px 0px;font-family: Arial;font-size: 14px;padding: 0px 0px 0px 0px;outline: none;";

		var tooltip = $(''+letter+'');
		tooltip.css('top',(elm.offset().top) + 'px');
		tooltip.css('left',(elm.offset().left) + 'px');

		$('body').append(tooltip);
		tooltips.push(tooltip);
	}
	function addTooltips() {

		var i = 0;
		var offsetSkip = offset;
		$.each(getClickable().filter(function(idx,val){return isScrolledIntoView(this);}), function(idx, val){
			if (i == keyCodes.length) return;
			if (offsetSkip-- > 0) return; // pulamos offset vezes
			elements[keyCodes[i++]] = {elm: val, type: 'click'};
		});

		$.each(getFocusable().filter(function(idx,val){return isScrolledIntoView(this);}), function(idx, val){
			if (i == keyCodes.length) return;
			if (offsetSkip-- > 0) return; // pulamos offset vezes
			elements[keyCodes[i++]] = {elm: val, type: 'focus'};
		});

		elements = unique(elements);

		for (i = 0; i<letters.length;i++){
			var letter = letters[i];
			var keyCode = keyCodes[i];
			if (elements[keyCode] == undefined){
				console.log('['+letter+'='+keyCode+'] has no element assigned');
				break;
			}
			addTooltip($(elements[keyCode].elm), keyCode);
		}
	}

	function getClickable(){
		var clickable = $('*').filter(function(idx, val){
							// TODO: getEventListeners nao existe fora do console...
							// to testando ver se _handlerTypes do tests.html funciona
							// se funcionar da pra combinar ele com .onclick != undefined ou algo assim
							var evts = this.getAttribute('_handlerTypes');
							return (this.onclick != undefined || (evts != undefined && evts.indexOf('click') >= 0)) && this != window;
						});
		$('input[type="submit"],input[type="file"],a[href]').each(function(idx,val){clickable.push(val);});
		return unique(clickable);
	}

	function unique(array){
		array = array.toArray != undefined ? array.toArray() : array;
		return array.filter(function (val, idx) {
			return array.lastIndexOf(val) === idx;
		});
	}

	function getFocusable() {
		var focusable = $('*').filter(function(idx, val){
							var evts = this.getAttribute('_handlerTypes');
							return (this.onfocus != undefined || (evts != undefined && evts.indexOf('focus') >= 0)
								|| this.onmouseover != undefined || (evts != undefined && evts.indexOf('onmouseover') >= 0))
								&& this != window;
						});
		$('select,input[type="text"]').each(function(idx,val){focusable.push(val);});
		return focusable;
	}

	function isScrolledIntoView(elem) {
		if (elem == window) return false;

		var docViewTop = $(window).scrollTop();
		var docViewBottom = docViewTop + $(window).height();

		var elemTop = $(elem).offset().top;
		var elemBottom = elemTop + $(elem).height();

		return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
	}

	function removeTooltips(){
		for (var i=0;i<tooltips.length;i++){
			$(tooltips[i]).remove();
		}
	}

	function simulatedClick(target, options) {
		var event = target.ownerDocument.createEvent('MouseEvents'),
			options = options || {};

		//Set your default options to the right of ||
		var opts = {
			type: options.type                  || 'click',
			canBubble:options.canBubble             || true,
			cancelable:options.cancelable           || true,
			view:options.view                       || target.ownerDocument.defaultView,
			detail:options.detail                   || 1,
			screenX:options.screenX                 || 0, //The coordinates within the entire page
			screenY:options.screenY                 || 0,
			clientX:options.clientX                 || 0, //The coordinates within the viewport
			clientY:options.clientY                 || 0,
			ctrlKey:options.ctrlKey                 || false,
			altKey:options.altKey                   || false,
			shiftKey:options.shiftKey               || false,
			metaKey:options.metaKey                 || false, //I *think* 'meta' is 'Cmd/Apple' on Mac, and 'Windows key' on Win. Not sure, though!
			button:options.button                   || 0, //0 = left, 1 = middle, 2 = right
			relatedTarget:options.relatedTarget     || null,
		}

		//Pass in the options
		event.initMouseEvent(
			opts.type,
			opts.canBubble,
			opts.cancelable,
			opts.view,
			opts.detail,
			opts.screenX,
			opts.screenY,
			opts.clientX,
			opts.clientY,
			opts.ctrlKey,
			opts.altKey,
			opts.shiftKey,
			opts.metaKey,
			opts.button,
			opts.relatedTarget
		);

		//Fire the event
		target.dispatchEvent(event);
	}

	var handler = function(e){
		if(keymap[17] && keymap[16] && keymap[18] && (e.keyCode == 16 || e.keyCode == 17 || e.keyCode == 18) && e.type=='keyup'){
			console.log('removeTooltips');
			removeTooltips();
			tooltipsVisible = false;
		}

		if (tooltipsVisible){
			// detects up or down, to shift tooltip placement
			if (e.keyCode == 38 || e.keyCode == 40) { // up or down
				var change = e.keyCode - 39;
				offset = offset + (change * 10);

				if (offset < 0)
					offset = 0;

				console.log('offsetting: ' + offset);
				removeTooltips();
				addTooltips();
			}

			// detects hotkey pressed
			var elementFromLetter = elements[e.keyCode];
			if (!keymap[e.keyCode] && elementFromLetter != undefined) {
				var elm = elementFromLetter;
				if (elm.type == 'click') {
					console.log('clicking...');
					console.log(elm.elm);
					simulatedClick(elm.elm);
				}
				else if (elm.type == 'focus') {
					console.log('focusing...');
					console.log(elm.elm);
					elm.elm.focus();
				}
			}
		}

		keymap[e.keyCode] = e.type == 'keydown';

		if(keymap[17] && keymap[16] && keymap[18]){
			if (tooltipsVisible == true)
				return;
			tooltipsVisible = true;
			offset = 0;
			addTooltips();
		}
	}
	window.onkeydown = handler;
	window.onkeyup = handler;
})(Zepto || jQuery);
//})(jQuery);
<|endoftext|>


<commit_before>function RollbarWrap(impl, options, client) {
  this.impl = impl(options, client, this);
  this.options = options;
  this.client = client;
  _setupForwarding(RollbarWrap.prototype);
}

function _setupForwarding(prototype) {
  var _forward = function(method) {
    return function() {
      var args = Array.prototype.slice.call(arguments, 0);
      if (this.impl[method]) {
        return this.impl[method].apply(this.impl, args);
      }
    };
  };

  var _methods = 'log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,_createItem,wrap,loadFull,shimId'.split(',');
  for (var i=0; i<_methods.length; i++) {
    prototype[_methods[i]] = _forward(_methods[i]);
  }
}

RollbarWrap.prototype._swapAndProcessMessages = function(impl, messages) {
  this.impl = impl(this.options, this.client);
  var msg, method, args;
  while ((msg = messages.shift())) {
    method = msg.method;
    args = msg.args;
    if (this[method] && typeof this[method] === 'function') {
      this[method].apply(this, args);
    }
  }
  return this;
};

module.exports = RollbarWrap;
<commit_msg>get rid of client stuff in wrapper, it wasn't doing anything<commit_after>function RollbarWrap(impl, options) {
  this.impl = impl(options, this);
  this.options = options;
  _setupForwarding(RollbarWrap.prototype);
}

function _setupForwarding(prototype) {
  var _forward = function(method) {
    return function() {
      var args = Array.prototype.slice.call(arguments, 0);
      if (this.impl[method]) {
        return this.impl[method].apply(this.impl, args);
      }
    };
  };

  var _methods = 'log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleUnhandledRejection,_createItem,wrap,loadFull,shimId'.split(',');
  for (var i=0; i<_methods.length; i++) {
    prototype[_methods[i]] = _forward(_methods[i]);
  }
}

RollbarWrap.prototype._swapAndProcessMessages = function(impl, messages) {
  this.impl = impl(this.options);
  var msg, method, args;
  while ((msg = messages.shift())) {
    method = msg.method;
    args = msg.args;
    if (this[method] && typeof this[method] === 'function') {
      this[method].apply(this, args);
    }
  }
  return this;
};

module.exports = RollbarWrap;
<|endoftext|>


<commit_before>import React from 'react';
import omit from 'lodash/object/omit';
import { props, t } from '../utils';
import easing from './easingFunctions';

/**
 * ### Utility component to make scrolling easier:
 * - can stop scroll propagation
 * - smooth programmatic scroll with 22 easing functions (see `easingFunctions.js`)
 * - out of the box momentum scrolling on iOS
 */
const PropTypes = {
  /**
   * components/nodes content. If you need to scroll programmatically pass a function and save `scrollTo(x, y, milliseconds)` callback for later use (it will be passed as first argument) ex: `(scrollTo) => { this.scrollTo = scrollTo; return <MyScrollViewContent />; }`
   */
  children: t.union([ t.ReactNode, t.Function ]),
  /**
   * enable horizontal scrolling
   */
  scrollX: t.maybe(t.Boolean),
  /**
   * enable vertical scrolling
   */
  scrollY: t.maybe(t.Boolean),
  /**
   * enable scroll propagation
   */
  scrollPropagation: t.maybe(t.Boolean),
  /**
   * easing function used when scrolling with `scrollTo`
   */
  easing: t.maybe(t.enums.of(Object.keys(easing))),
  style: t.maybe(t.Object)
};

@props(PropTypes, { strict: false })
export default class ScrollView extends React.Component {

  static defaultProps = {
    scrollX: true,
    scrollY: true,
    scrollPropagation: true,
    easing: 'easeInOutQuad'
  };

  getScrollView = () => React.findDOMNode(this.refs.scrollView);

  getEventListeners = () => {
    return !this.props.scrollPropagation ? {
      onScroll: this.stopScrollPropagation,
      onWheel: this.stopScrollPropagation,
      onTouchStart: this.initializeTouchEventDirection,
      onTouchEnd: this.clearTouchEventDirection,
      onTouchMove: this.stopScrollPropagation
    } : {};
  };

  isEventInsideScrollView = (el) => {
    if (el === this.getScrollView()) {
      return true;
    } else if (el.parentNode) {
      return this.isEventInsideScrollView(el.parentNode);
    } else {
      return false;
    }
  };

  initializeTouchEventDirection = (e) => {
    this.lastY = e.touches[0].clientY;
  };

  clearTouchEventDirection = () => {
    this.lastY = null;
  };

  stopScrollPropagation = ({ nativeEvent: e }) => {
    const el = e.target || e.srcElement;
    const isEventInsideScrollView = this.isEventInsideScrollView(el);
    if (isEventInsideScrollView) {

      // const { scrollTop, scrollHeight, offsetHeight } = this.getScrollView();

      let up;
      let down;

      if (e instanceof TouchEvent) {
        const { clientY } = e.touches[0];
        up = clientY > this.lastY;
        this.lastY = clientY;
      } else if (e instanceof WheelEvent) {
        up = e.wheelDelta > 0;
      }
      down = !up;

      if((down && this.isAtBottom()) || (up && this.isAtTop())) {
        e.preventDefault();
      }
    }
  };

  isAtTop = () => this.getScrollView().scrollTop === 0;

  isAtBottom = () => {
    const { scrollTop, scrollHeight, offsetHeight } = this.getScrollView();
    return scrollTop + offsetHeight === scrollHeight;
  };

  computeStyle = () => {
    const { scrollX, scrollY, style } = this.props;
    return {
      overflowY: scrollY ? 'scroll' : undefined, /* has to be scroll for iOS, not auto */
      overflowX: scrollX ? 'scroll' : undefined, /* has to be scroll for iOS, not auto */
      WebkitOverflowScrolling: 'touch',
      ...style
    };
  };

  scrollTo = (_x, _y, scrollDuration) => {
    const { scrollTop, scrollLeft } = this.getScrollView();
    const x = _x === null ? scrollLeft : _x;
    const y = _y === null ? scrollTop : _y;

    this._scrollTo(x, y, scrollDuration, Date.now(), scrollLeft, scrollTop);
  };

  _scrollTo = (x, y, scrollDuration, startTime, startX, startY) => {
    if (scrollDuration > 0) {
      const { scrollTop, scrollLeft } = this.getScrollView();
      const easingFunction = easing[this.props.easing];

      if ((t.Number.is(x) && scrollLeft !== x) || (t.Number.is(y) && scrollTop !== y)) {
        const currentTime = Math.min(scrollDuration, (Date.now() - startTime));
        const distanceX = (x - startX);
        const distanceY = (y - startY);
        this.getScrollView().scrollLeft = easingFunction(currentTime, startX, distanceX, scrollDuration);
        this.getScrollView().scrollTop = easingFunction(currentTime, startY, distanceY, scrollDuration);

        requestAnimationFrame(() => this._scrollTo(x, y, scrollDuration, startTime, startX, startY));
      }
    } else {
      this.getScrollView().scrollLeft = x;
      this.getScrollView().scrollTop = y;
    }
  };

  render() {
    const props = omit(this.props, Object.keys(PropTypes));
    const { children } = this.props;
    return (
      <div { ...props } { ...this.getEventListeners() } style={this.computeStyle()} ref='scrollView'>
        {t.Function.is(children) ? children(this.scrollTo) : children}
      </div>
    );
  }

}
<commit_msg>replace offsetHeight with clientHeight<commit_after>import React from 'react';
import omit from 'lodash/object/omit';
import { props, t } from '../utils';
import easing from './easingFunctions';

/**
 * ### Utility component to make scrolling easier:
 * - can stop scroll propagation
 * - smooth programmatic scroll with 22 easing functions (see `easingFunctions.js`)
 * - out of the box momentum scrolling on iOS
 */
const PropTypes = {
  /**
   * components/nodes content. If you need to scroll programmatically pass a function and save `scrollTo(x, y, milliseconds)` callback for later use (it will be passed as first argument) ex: `(scrollTo) => { this.scrollTo = scrollTo; return <MyScrollViewContent />; }`
   */
  children: t.union([ t.ReactNode, t.Function ]),
  /**
   * enable horizontal scrolling
   */
  scrollX: t.maybe(t.Boolean),
  /**
   * enable vertical scrolling
   */
  scrollY: t.maybe(t.Boolean),
  /**
   * enable scroll propagation
   */
  scrollPropagation: t.maybe(t.Boolean),
  /**
   * easing function used when scrolling with `scrollTo`
   */
  easing: t.maybe(t.enums.of(Object.keys(easing))),
  style: t.maybe(t.Object)
};

@props(PropTypes, { strict: false })
export default class ScrollView extends React.Component {

  static defaultProps = {
    scrollX: true,
    scrollY: true,
    scrollPropagation: true,
    easing: 'easeInOutQuad'
  };

  getScrollView = () => React.findDOMNode(this.refs.scrollView);

  getEventListeners = () => {
    return !this.props.scrollPropagation ? {
      onScroll: this.stopScrollPropagation,
      onWheel: this.stopScrollPropagation,
      onTouchStart: this.initializeTouchEventDirection,
      onTouchEnd: this.clearTouchEventDirection,
      onTouchMove: this.stopScrollPropagation
    } : {};
  };

  isEventInsideScrollView = (el) => {
    if (el === this.getScrollView()) {
      return true;
    } else if (el.parentNode) {
      return this.isEventInsideScrollView(el.parentNode);
    } else {
      return false;
    }
  };

  initializeTouchEventDirection = (e) => {
    this.lastY = e.touches[0].clientY;
  };

  clearTouchEventDirection = () => {
    this.lastY = null;
  };

  stopScrollPropagation = ({ nativeEvent: e }) => {
    const el = e.target || e.srcElement;
    const isEventInsideScrollView = this.isEventInsideScrollView(el);
    if (isEventInsideScrollView) {

      // const { scrollTop, scrollHeight, offsetHeight } = this.getScrollView();

      let up;
      let down;

      if (e instanceof TouchEvent) {
        const { clientY } = e.touches[0];
        up = clientY > this.lastY;
        this.lastY = clientY;
      } else if (e instanceof WheelEvent) {
        up = e.wheelDelta > 0;
      }
      down = !up;

      if((down && this.isAtBottom()) || (up && this.isAtTop())) {
        e.preventDefault();
      }
    }
  };

  isAtTop = () => this.getScrollView().scrollTop === 0;

  isAtBottom = () => {
    const { scrollTop, scrollHeight, clientHeight } = this.getScrollView();
    return scrollTop + clientHeight === scrollHeight;
  };

  computeStyle = () => {
    const { scrollX, scrollY, style } = this.props;
    return {
      overflowY: scrollY ? 'scroll' : undefined, /* has to be scroll for iOS, not auto */
      overflowX: scrollX ? 'scroll' : undefined, /* has to be scroll for iOS, not auto */
      WebkitOverflowScrolling: 'touch',
      ...style
    };
  };

  scrollTo = (_x, _y, scrollDuration) => {
    const { scrollTop, scrollLeft } = this.getScrollView();
    const x = _x === null ? scrollLeft : _x;
    const y = _y === null ? scrollTop : _y;

    this._scrollTo(x, y, scrollDuration, Date.now(), scrollLeft, scrollTop);
  };

  _scrollTo = (x, y, scrollDuration, startTime, startX, startY) => {
    if (scrollDuration > 0) {
      const { scrollTop, scrollLeft } = this.getScrollView();
      const easingFunction = easing[this.props.easing];

      if ((t.Number.is(x) && scrollLeft !== x) || (t.Number.is(y) && scrollTop !== y)) {
        const currentTime = Math.min(scrollDuration, (Date.now() - startTime));
        const distanceX = (x - startX);
        const distanceY = (y - startY);
        this.getScrollView().scrollLeft = easingFunction(currentTime, startX, distanceX, scrollDuration);
        this.getScrollView().scrollTop = easingFunction(currentTime, startY, distanceY, scrollDuration);

        requestAnimationFrame(() => this._scrollTo(x, y, scrollDuration, startTime, startX, startY));
      }
    } else {
      this.getScrollView().scrollLeft = x;
      this.getScrollView().scrollTop = y;
    }
  };

  render() {
    const props = omit(this.props, Object.keys(PropTypes));
    const { children } = this.props;
    return (
      <div { ...props } { ...this.getEventListeners() } style={this.computeStyle()} ref='scrollView'>
        {t.Function.is(children) ? children(this.scrollTo) : children}
      </div>
    );
  }

}
<|endoftext|>