コード例 #1
0
ファイル: WorkingSetSort.js プロジェクト: 1174751315/brackets
    /*
     * initializes global sort method from preference settings or the default 
     */
    function initSortMethod() {
        var sortMethod = PreferencesManager.getViewState(_WORKING_SET_SORT_PREF);
        
        if (!sortMethod) {
            sortMethod = _convertSortPref(PreferencesManager.getViewState(_LEGACY_SORT_PREF));
        }

        if (!sortMethod) {
            sortMethod = Commands.CMD_WORKINGSET_SORT_BY_ADDED;
        }
        return sortMethod;
    }
コード例 #2
0
ファイル: DocumentManager.js プロジェクト: valtlai/brackets
    /**
     * Called after a file or folder has been deleted. This function is responsible
     * for updating underlying model data and notifying all views of the change.
     *
     * @param {string} fullPath The path of the file/folder that has been deleted
     */
    function notifyPathDeleted(fullPath) {
        // FileSyncManager.syncOpenDocuments() does all the work prompting
        //  the user to save any unsaved changes and then calls us back
        //  via notifyFileDeleted
        FileSyncManager.syncOpenDocuments(Strings.FILE_DELETED_TITLE);

        var projectRoot = ProjectManager.getProjectRoot(),
            context = {
                location : {
                    scope: "user",
                    layer: "project",
                    layerID: projectRoot.fullPath
                }
            };
        var encoding = PreferencesManager.getViewState("encoding", context);
        delete encoding[fullPath];
        PreferencesManager.setViewState("encoding", encoding, context);

        if (!getOpenDocumentForPath(fullPath) &&
                !MainViewManager.findInAllWorkingSets(fullPath).length) {
            // For images not open in the workingset,
            // FileSyncManager.syncOpenDocuments() will
            //  not tell us to close those views
            exports.trigger("pathDeleted", fullPath);
        }
    }
コード例 #3
0
ファイル: ExtensionManager.js プロジェクト: 64octets/brackets
    /**
     * @private
     * Auto-install extensions bundled with installer
     * @return {$.Promise} Promise that resolves when finished
     */
    function _autoInstallExtensions() {
        var dirPath        = FileUtils.getDirectoryPath(FileUtils.getNativeBracketsDirectoryPath()) + FOLDER_AUTOINSTALL + "/",
            autoExtensions = PreferencesManager.getViewState(FOLDER_AUTOINSTALL) || {},
            deferred       = new $.Deferred();

        _getAutoInstallFiles(dirPath, autoExtensions).done(function (result) {
            var installPromise = Async.doSequentially(result.installZips, function (zip) {
                autoExtensions[zip.info.metadata.name] = zip.info.metadata.version;
                return Package.installFromPath(zip.file.fullPath);
            });

            var updatePromise = installPromise.always(function () {
                return Async.doSequentially(result.updateZips, function (zip) {
                    autoExtensions[zip.info.metadata.name] = zip.info.metadata.version;
                    return Package.installUpdate(zip.file.fullPath);
                });
            });

            // Always resolve the outer promise
            updatePromise.always(function () {
                // Keep track of auto-installed extensions so we only install an extension once
                PreferencesManager.setViewState(FOLDER_AUTOINSTALL, autoExtensions);

                deferred.resolve();
            });
        });

        return deferred.promise();
    }
コード例 #4
0
ファイル: FileFilters.js プロジェクト: Blair6204/brackets
    /**
     * Remove the target item from the filter dropdown list and update dropdown button
     * and dropdown list UI.
     * @param {!Event} e Mouse events
     */
    function _handleDeleteFilter(e) {
        // Remove the filter set from the preferences and 
        // clear the active filter set index from view state.
        var filterSets        = PreferencesManager.get("fileFilters") || [],
            activeFilterIndex = PreferencesManager.getViewState("activeFileFilter"),
            filterIndex       = $(e.target).parent().data("index") - FIRST_FILTER_INDEX;

        // Don't let the click bubble upward.
        e.stopPropagation();

        filterSets.splice(filterIndex, 1);
        PreferencesManager.set("fileFilters", filterSets);

        if (activeFilterIndex === filterIndex) {
            // Removing the active filter, so clear the active filter 
            // both in the view state.
            setActiveFilter(null);
        } else if (activeFilterIndex > filterIndex) {
            // Adjust the active filter index after the removal of a filter set before it.
            --activeFilterIndex;
            setActiveFilter(filterSets[activeFilterIndex], activeFilterIndex);
        }

        _updatePicker();
        _doPopulate();
        _picker.refresh();
    }
コード例 #5
0
    MultiRangeInlineEditor.prototype._toggleSection = function (fullPath, duringInit) {
        var $headerItem = this._$headers[fullPath];
        var $disclosureIcon = $headerItem.find(".disclosure-triangle");
        var isCollapsing = $disclosureIcon.hasClass("expanded");
        $disclosureIcon.toggleClass("expanded");
        $headerItem.nextUntil(".section-header").toggle(!isCollapsing);  // explicit visibility arg, since during load() jQ doesn't think nodes are visible

        // Update instance-specific state...
        this._collapsedFiles[fullPath] = isCollapsing;
        // ...AND persist as per-project view state
        if (!duringInit) {
            var setting = PreferencesManager.getViewState("inlineEditor.collapsedFiles", _getPrefsContext()) || {};
            if (isCollapsing) {
                setting[fullPath] = true;
            } else {
                delete setting[fullPath];
            }
            PreferencesManager.setViewState("inlineEditor.collapsedFiles", setting, _getPrefsContext());
        }

        // Show/hide selection indicator if selection was in collapsed section
        this._updateSelectedMarker(false);

        // Changing height of rule list may change ht of overall editor
        this._ruleListHeightChanged();

        // If user expands collapsed section and nothing selected yet, select first result in this section
        if (this._selectedRangeIndex === -1 && !isCollapsing && !duringInit) {
            var index = _.findIndex(this._ranges, function (resultItem) {
                return resultItem.textRange.document.file.fullPath === fullPath;
            });
            this.setSelectedIndex(index);
        }
    };
コード例 #6
0
ファイル: FileFilters.js プロジェクト: Blair6204/brackets
    /**
     * Set up mouse click event listeners for 'Delete' and 'Edit' buttons
     * when the dropdown is open. Also set check mark on the active filter.
     * @param {!Event>} event listRendered event triggered when the dropdown is open
     * @param {!jQueryObject} $dropdown the jQuery DOM node of the dropdown list
     */
    function _handleListRendered(event, $dropdown) {
        var activeFilterIndex = PreferencesManager.getViewState("activeFileFilter"),
            checkedItemIndex = (activeFilterIndex > -1) ? (activeFilterIndex + FIRST_FILTER_INDEX) : -1;
        _picker.setChecked(checkedItemIndex, true);

        $dropdown.find(".filter-trash-icon")
            .on("click", _handleDeleteFilter);
        
        $dropdown.find(".filter-edit-icon")
            .on("click", _handleEditFilter);
    }
コード例 #7
0
ファイル: brackets.js プロジェクト: SairusGeek/brackets
 ProjectManager.openProject(initialProjectPath).always(function () {
     _initTest();
     
     // If this is the first launch, and we have an index.html file in the project folder (which should be
     // the samples folder on first launch), open it automatically. (We explicitly check for the
     // samples folder in case this is the first time we're launching Brackets after upgrading from
     // an old version that might not have set the "afterFirstLaunch" pref.)
     var deferred = new $.Deferred();
     
     if (!params.get("skipSampleProjectLoad") && !PreferencesManager.getViewState("afterFirstLaunch")) {
         PreferencesManager.setViewState("afterFirstLaunch", "true");
         if (ProjectManager.isWelcomeProjectPath(initialProjectPath)) {
             FileSystem.resolve(initialProjectPath + "index.html", function (err, file) {
                 if (!err) {
                     var promise = CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, { fullPath: file.fullPath });
                     promise.then(deferred.resolve, deferred.reject);
                 } else {
                     deferred.reject();
                 }
             });
         } else {
             deferred.resolve();
         }
     } else {
         deferred.resolve();
     }
     
     deferred.always(function () {
         // Signal that Brackets is loaded
         AppInit._dispatchReady(AppInit.APP_READY);
         
         PerfUtils.addMeasurement("Application Startup");
         
         if (PreferencesManager._isUserScopeCorrupt()) {
             Dialogs.showModalDialog(
                 DefaultDialogs.DIALOG_ID_ERROR,
                 Strings.ERROR_PREFS_CORRUPT_TITLE,
                 Strings.ERROR_PREFS_CORRUPT
             )
                 .done(function () {
                     CommandManager.execute(Commands.FILE_OPEN_PREFERENCES);
                 });
         }
         
     });
     
     // See if any startup files were passed to the application
     if (brackets.app.getPendingFilesToOpen) {
         brackets.app.getPendingFilesToOpen(function (err, files) {
             DragAndDrop.openDroppedFiles(files);
         });
     }
 });
コード例 #8
0
ファイル: FileFilters.js プロジェクト: Blair6204/brackets
    /**
     * A search filter is an array of one or more glob strings. The filter must be 'compiled' via compile()
     * before passing to filterPath()/filterFileList().
     * @return {?{name: string, patterns: Array.<string>}}
     */
    function getActiveFilter() {
        var filterSets        = PreferencesManager.get("fileFilters") || [],
            activeFilterIndex = PreferencesManager.getViewState("activeFileFilter"),
            oldFilter         = PreferencesManager.getViewState("search.exclusions") || [],
            activeFilter      = null;

        if (activeFilterIndex === undefined && oldFilter.length) {
            activeFilter = { name: "", patterns: oldFilter };
            activeFilterIndex = _getFilterIndex(filterSets, activeFilter);
            
            // Migrate the old filter into the new filter storage
            if (activeFilterIndex === -1) {
                activeFilterIndex = filterSets.length;
                filterSets.push(activeFilter);
                PreferencesManager.set("fileFilters", filterSets);
            }
            PreferencesManager.setViewState("activeFileFilter", activeFilterIndex);
        } else if (activeFilterIndex > -1 && activeFilterIndex < filterSets.length) {
            activeFilter = filterSets[activeFilterIndex];
        }
        
        return activeFilter;
    }
コード例 #9
0
ファイル: HealthLogger.js プロジェクト: AdaConcordia/brackets
 /**
  * Send Analytics Data
  * @param {string} eventCategory The kind of Event Category that
  * needs to be logged- should be a js var compatible string
  * @param {string} eventSubCategory The kind of Event Sub Category that
  * needs to be logged- should be a js var compatible string
  * @param {string} eventType The kind of Event Type that needs to be logged- should be a js var compatible string
  * @param {string} eventSubType The kind of Event Sub Type that
  * needs to be logged- should be a js var compatible string
  */
 function sendAnalyticsData(eventName, eventCategory, eventSubCategory, eventType, eventSubType) {
     var isEventDataAlreadySent = PreferencesManager.getViewState(eventName),
         isHDTracking   = PreferencesManager.getExtensionPrefs("healthData").get("healthDataTracking"),
         eventParams = {};
     if (isHDTracking && !isEventDataAlreadySent && eventName && eventCategory) {
         eventParams =  {
             eventName: eventName,
             eventCategory: eventCategory,
             eventSubCategory: eventSubCategory || "",
             eventType: eventType || "",
             eventSubType: eventSubType || ""
         };
         notifyHealthManagerToSendData(eventParams);
     }
 }
コード例 #10
0
ファイル: WorkingSetSort.js プロジェクト: 1174751315/brackets
 AppInit.appReady(function () {
     var sortMethod = initSortMethod(),
         curSort    = get(sortMethod),
         autoSort = PreferencesManager.getViewState("automaticSort");
     
     if (curSort) {
         _setCurrentSort(curSort);
     }
     if (autoSort) {
         setAutomatic(autoSort);
     }
     if (curSort && autoSort) {
         curSort.sort();
     }
 });
コード例 #11
0
 promise.done(function (text, readTimestamp) {
     encodingSelect.$button.text(document.file._encoding);
     // Store the preferred encoding in the state
     var projectRoot = ProjectManager.getProjectRoot(),
         context = {
             location : {
                 scope: "user",
                 layer: "project",
                 layerID: projectRoot.fullPath
             }
         };
     var encoding = PreferencesManager.getViewState("encoding", context);
     encoding[document.file.fullPath] = document.file._encoding;
     PreferencesManager.setViewState("encoding", encoding, context);
 });
コード例 #12
0
    /**
     *  Every 24 hours downloads registry information to check for update, but only if the registry download
     *  wasn't triggered by another action (like opening extension manager)
     *  If there isn't 24 hours elapsed from the last download, use cached information from last download
     *  to determine state of the update notification.
     */
    function checkForExtensionsUpdate() {
        var lastExtensionRegistryCheckTime = PreferencesManager.getViewState("lastExtensionRegistryCheckTime"),
            timeOfNextCheck = lastExtensionRegistryCheckTime + ONE_DAY,
            currentTime = (new Date()).getTime();

        // update icon according to previously saved information
        var availableUpdates = PreferencesManager.getViewState("extensionUpdateInfo");
        availableUpdates = ExtensionManager.cleanAvailableUpdates(availableUpdates);
        $("#toolbar-extension-manager").toggleClass("updatesAvailable", availableUpdates.length > 0);

        if (availableUpdates.length === 0) {
            // icon is gray, no updates available
            if (currentTime > timeOfNextCheck) {
                // downloadRegistry, will be resolved in _onRegistryDownloaded
                ExtensionManager.downloadRegistry().done(function () {
                    // schedule another check in 24 hours + 2 minutes
                    setTimeout(checkForExtensionsUpdate, ONE_DAY + TWO_MINUTES);
                });
            } else {
                // schedule the download of the registry in appropriate time
                setTimeout(checkForExtensionsUpdate, (timeOfNextCheck - currentTime) + TWO_MINUTES);
            }
        }
    }
コード例 #13
0
ファイル: FindBar.js プロジェクト: dynstat/brackets
 FindBar.prototype._addElementToSearchHistory = function (searchVal) {
     if (searchVal) {
         var searchHistory = PreferencesManager.getViewState("searchHistory");
         var maxCount = PreferencesManager.get("maxSearchHistory");
         var searchQueryIndex = searchHistory.indexOf(searchVal);
         if (searchQueryIndex !== -1) {
             searchHistory.splice(searchQueryIndex, 1);
         } else {
             if (searchHistory.length === maxCount) {
                 searchHistory.pop();
             }
         }
         searchHistory.unshift(searchVal);
         PreferencesManager.setViewState("searchHistory", searchHistory);
     }
 };
コード例 #14
0
ファイル: DocumentManager.js プロジェクト: A-0-/brackets
    /**
     * @private
     * Initializes the working set.
     */
    function _projectOpen(e) {
        // file root is appended for each project
        var projectRoot = ProjectManager.getProjectRoot(),
            files = [],
            context = { location : { scope: "user",
                                     layer: "project" } };
        
        files = PreferencesManager.getViewState("project.files", context);
        
        console.assert(Object.keys(_openDocuments).length === 0);  // no files leftover from prev proj

        if (!files) {
            return;
        }

        var filesToOpen = [],
            viewStates = {},
            activeFile;

        // Add all files to the working set without verifying that
        // they still exist on disk (for faster project switching)
        files.forEach(function (value, index) {
            filesToOpen.push(FileSystem.getFileForPath(value.file));
            if (value.active) {
                activeFile = value.file;
            }
            if (value.viewState) {
                viewStates[value.file] = value.viewState;
            }
        });
        addListToWorkingSet(filesToOpen);
        
        // Allow for restoring saved editor UI state
        EditorManager._resetViewStates(viewStates);

        // Initialize the active editor
        if (!activeFile && _workingSet.length > 0) {
            activeFile = _workingSet[0].fullPath;
        }

        if (activeFile) {
            var promise = CommandManager.execute(Commands.FILE_OPEN, { fullPath: activeFile });
            // Add this promise to the event's promises to signal that this handler isn't done yet
            e.promises.push(promise);
        }
    }
コード例 #15
0
ファイル: main.js プロジェクト: 0b10011/brackets
 /**
  * Toggles LiveDevelopment and synchronizes the state of UI elements that reports LiveDevelopment status
  *
  * Stop Live Dev when in an active state (ACTIVE, OUT_OF_SYNC, SYNC_ERROR).
  * Start Live Dev when in an inactive state (ERROR, INACTIVE).
  * Do nothing when in a connecting state (CONNECTING, LOADING_AGENTS).
  */
 function _handleGoLiveCommand() {
     if (LiveDevelopment.status >= LiveDevelopment.STATUS_ACTIVE) {
         LiveDevelopment.close();
     } else if (LiveDevelopment.status <= LiveDevelopment.STATUS_INACTIVE) {
         if (!params.get("skipLiveDevelopmentInfo") && !PreferencesManager.getViewState("livedev.afterFirstLaunch")) {
             PreferencesManager.setViewState("livedev.afterFirstLaunch", "true");
             Dialogs.showModalDialog(
                 DefaultDialogs.DIALOG_ID_INFO,
                 Strings.LIVE_DEVELOPMENT_INFO_TITLE,
                 Strings.LIVE_DEVELOPMENT_INFO_MESSAGE
             ).done(function (id) {
                 LiveDevelopment.open();
             });
         } else {
             LiveDevelopment.open();
         }
     }
 }
コード例 #16
0
    /**
     * Restores the font size using the saved style and migrates the old fontSizeAdjustment
     * view state to the new fontSize, when required
     */
    function restoreFontSize() {
        var fsStyle      = prefs.get("fontSize"),
            fsAdjustment = PreferencesManager.getViewState("fontSizeAdjustment");

        if (fsAdjustment) {
            // Always remove the old view state even if we also have the new view state.
            PreferencesManager.setViewState("fontSizeAdjustment");

            if (!fsStyle) {
                // Migrate the old view state to the new one.
                fsStyle = (DEFAULT_FONT_SIZE + fsAdjustment) + "px";
                prefs.set("fontSize", fsStyle);
            }
        }

        if (fsStyle) {
            _removeDynamicFontSize();
            _addDynamicFontSize(fsStyle);
        }
    }
コード例 #17
0
 ProjectManager.on("projectOpen", function () {
     var projectRoot = ProjectManager.getProjectRoot(),
         context = {
             location : {
                 scope: "user",
                 layer: "project",
                 layerID: projectRoot.fullPath
             }
         };
     var encoding = PreferencesManager.getViewState("encoding", context);
     if (!encoding) {
         encoding = {};
         PreferencesManager.setViewState("encoding", encoding, context);
     }
     Async.doSequentially(Object.keys(encoding), function (filePath, index) {
         return _checkFileExistance(filePath, index, encoding);
     }, false)
         .always(function () {
             PreferencesManager.setViewState("encoding", encoding, context);
         });
 });
コード例 #18
0
    /**
     * Check for updates. If "force" is true, update notification dialogs are always displayed
     * (if an update is available). If "force" is false, the update notification is only
     * displayed for newly available updates.
     *
     * If an update is available, show the "update available" notification icon in the title bar.
     *
     * @param {boolean} force If true, always show the notification dialog.
     * @param {Object} _testValues This should only be used for testing purposes. See comments for details.
     * @return {$.Promise} jQuery Promise object that is resolved or rejected after the update check is complete.
     */
    function checkForUpdate(force, _testValues) {
        // This is the last version we notified the user about. If checkForUpdate()
        // is called with "false", only show the update notification dialog if there
        // is an update newer than this one. This value is saved in preferences.
        var lastNotifiedBuildNumber = PreferencesManager.getViewState("lastNotifiedBuildNumber");

        // The second param, if non-null, is an Object containing value overrides. Values
        // in the object temporarily override the local values. This should *only* be used for testing.
        // If any overrides are set, permanent changes are not made (including showing
        // the update notification icon and saving prefs).
        var oldValues;
        var usingOverrides = false; // true if any of the values are overridden.
        var result = new $.Deferred();
        var versionInfoUrl;

        if (_testValues) {
            oldValues = {};

            if (_testValues.hasOwnProperty("_buildNumber")) {
                oldValues._buildNumber = _buildNumber;
                _buildNumber = _testValues._buildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("lastNotifiedBuildNumber")) {
                oldValues.lastNotifiedBuildNumber = lastNotifiedBuildNumber;
                lastNotifiedBuildNumber = _testValues.lastNotifiedBuildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("_versionInfoURL")) {
                versionInfoUrl = _testValues._versionInfoURL;
                usingOverrides = true;
            }
        }

        _getUpdateInformation(force || usingOverrides, usingOverrides, versionInfoUrl)
            .done(function (versionInfo) {
                // Get all available updates
                var allUpdates = _stripOldVersionInfo(versionInfo, _buildNumber);

                // When running directly from GitHub source (as opposed to
                // an installed build), _buildNumber is 0. In this case, if the
                // test is not forced, don't show the update notification icon or
                // dialog.
                if (_buildNumber === 0 && !force) {
                    result.resolve();
                    return;
                }

                if (allUpdates) {
                    // Always show the "update available" icon if any updates are available
                    var $updateNotification = $("#update-notification");

                    $updateNotification.css("display", "block");
                    if (!_addedClickHandler) {
                        _addedClickHandler = true;
                        $updateNotification.on("click", function () {
                            checkForUpdate(true);
                        });
                    }

                    // Only show the update dialog if force = true, or if the user hasn't been
                    // alerted of this update
                    if (force || allUpdates[0].buildNumber >  lastNotifiedBuildNumber) {
                        _showUpdateNotificationDialog(allUpdates);

                        // Update prefs with the last notified build number
                        lastNotifiedBuildNumber = allUpdates[0].buildNumber;
                        // Don't save prefs is we have overridden values
                        if (!usingOverrides) {
                            PreferencesManager.setViewState("lastNotifiedBuildNumber", lastNotifiedBuildNumber);
                        }
                    }
                } else if (force) {
                    // No updates are available. If force == true, let the user know.
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.NO_UPDATE_TITLE,
                        Strings.NO_UPDATE_MESSAGE
                    );
                }

                if (oldValues) {
                    if (oldValues.hasOwnProperty("_buildNumber")) {
                        _buildNumber = oldValues._buildNumber;
                    }
                    if (oldValues.hasOwnProperty("lastNotifiedBuildNumber")) {
                        lastNotifiedBuildNumber = oldValues.lastNotifiedBuildNumber;
                    }
                }
                result.resolve();
            })
            .fail(function () {
                // Error fetching the update data. If this is a forced check, alert the user
                if (force) {
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.ERROR_FETCHING_UPDATE_INFO_TITLE,
                        Strings.ERROR_FETCHING_UPDATE_INFO_MSG
                    );
                }
                result.reject();
            });

        return result.promise();
    }
コード例 #19
0
ファイル: FindBar.js プロジェクト: dynstat/brackets
 resultProvider: function (query) {
     var asyncResult = new $.Deferred();
     asyncResult.resolve(PreferencesManager.getViewState("searchHistory"));
     return asyncResult.promise();
 },
コード例 #20
0
ファイル: FileFilters.js プロジェクト: GAMilligan/brackets
 /**
  * A search filter is an array of one or more glob strings. The filter must be 'compiled' via compile()
  * before passing to filterPath()/filterFileList().
  * @return {!Array.<string>}
  */
 function getLastFilter() {
     return PreferencesManager.getViewState("search.exclusions") || [];
 }
コード例 #21
0
    MultiRangeInlineEditor.prototype.load = function (hostEditor) {
        MultiRangeInlineEditor.prototype.parentClass.load.apply(this, arguments);

        // Create the message area
        this.$messageDiv = $("<div/>")
            .addClass("inline-editor-message");

        // Prevent touch scroll events from bubbling up to the parent editor.
        this.$editorHolder.on("mousewheel.MultiRangeInlineEditor", function (e) {
            e.stopPropagation();
        });

        // Outer container for border-left and scrolling
        this.$relatedContainer = $("<div/>").addClass("related-container");

        // List "selection" highlight
        this.$selectedMarker = $("<div/>").appendTo(this.$relatedContainer).addClass("selection");

        // Inner container
        this.$related = $("<div/>").appendTo(this.$relatedContainer).addClass("related");

        // Range list
        this.$rangeList = $("<ul/>").appendTo(this.$related);

        // Determine which sections are initially collapsed (the actual collapsing happens after onAdded(),
        // because jQuery.hide() requires the computed value of 'display' to work properly)
        var toCollapse = PreferencesManager.getViewState("inlineEditor.collapsedFiles", _getPrefsContext()) || {};
        Object.keys(toCollapse).forEach(function (fullPath) {
            this._collapsedFiles[fullPath] = true;
        }.bind(this));

        // Render list & section headers (matching collapsed state set above)
        this._renderList();

        if (this._ranges.length > 1) {      // attach to main container
            this.$wrapper.before(this.$relatedContainer);
        }

        // Add TextRange listeners to update UI as text changes
        var self = this;
        this._ranges.forEach(function (range, index) {
            // Update list item as TextRange changes
            range.textRange.on("change", function () {
                _updateRangeLabel(range.$listItem, range);
            }).on("contentChange", function () {
                _updateRangeLabel(range.$listItem, range, self._labelCB);
            });

            // If TextRange lost sync, remove it from the list (and close the widget if no other ranges are left)
            range.textRange.on("lostSync", function () {
                self._removeRange(range);
            });
        });

        // Initial selection is the first non-collapsed result item
        var indexToSelect = _.findIndex(this._ranges, function (range) {
            return !this._collapsedFiles[range.textRange.document.file.fullPath];
        }.bind(this));
        if (this._ranges.length === 1 && indexToSelect === -1) {
            // If no right-hand rule list shown, select the one result even if it's in a collapsed file (since no way to expand)
            indexToSelect = 0;
        }

        if (indexToSelect !== -1) {
            // select the first visible range
            this.setSelectedIndex(indexToSelect);
        } else {
            // force the message div to show
            this.setSelectedIndex(-1);
        }

        // Listen for clicks directly on us, so we can set focus back to the editor
        var clickHandler = this._onClick.bind(this);
        this.$htmlContent.on("click.MultiRangeInlineEditor", clickHandler);
        // Also handle mouseup in case the user drags a little bit
        this.$htmlContent.on("mouseup.MultiRangeInlineEditor", clickHandler);

        // Update the rule list navigation menu items when we gain/lose focus.
        this.$htmlContent
            .on("focusin.MultiRangeInlineEditor", this._updateCommands.bind(this))
            .on("focusout.MultiRangeInlineEditor", this._updateCommands.bind(this));
    };
コード例 #22
0
ファイル: brackets.js プロジェクト: foresthz/brackets
 ProjectManager.openProject(initialProjectPath).always(function () {
     _initTest();
     
     // If this is the first launch, and we have an index.html file in the project folder (which should be
     // the samples folder on first launch), open it automatically. (We explicitly check for the
     // samples folder in case this is the first time we're launching Brackets after upgrading from
     // an old version that might not have set the "afterFirstLaunch" pref.)
     var deferred = new $.Deferred();
     
     if (!params.get("skipSampleProjectLoad") && !PreferencesManager.getViewState("afterFirstLaunch")) {
         PreferencesManager.setViewState("afterFirstLaunch", "true");
         if (ProjectManager.isWelcomeProjectPath(initialProjectPath)) {
             FileSystem.resolve(initialProjectPath + "index.html", function (err, file) {
                 if (!err) {
                     var promise = CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, { fullPath: file.fullPath });
                     promise.then(deferred.resolve, deferred.reject);
                 } else {
                     deferred.reject();
                 }
             });
         } else {
             deferred.resolve();
         }
     } else {
         deferred.resolve();
     }
     
     deferred.always(function () {
         // Signal that Brackets is loaded
         AppInit._dispatchReady(AppInit.APP_READY);
         
         PerfUtils.addMeasurement("Application Startup");
         
         if (PreferencesManager._isUserScopeCorrupt()) {
             var userPrefFullPath = PreferencesManager.getUserPrefFile();
             // user scope can get corrupt only if the file exists, is readable,
             // but malformed. no need to check for its existance.
             var info = MainViewManager.findInAllWorkingSets(userPrefFullPath);
             var paneId;
             if (info.length) {
                 paneId = info[0].paneId;
             }
             FileViewController.openFileAndAddToWorkingSet(userPrefFullPath, paneId)
                 .done(function () {
                     Dialogs.showModalDialog(
                         DefaultDialogs.DIALOG_ID_ERROR,
                         Strings.ERROR_PREFS_CORRUPT_TITLE,
                         Strings.ERROR_PREFS_CORRUPT
                     ).done(function () {
                         // give the focus back to the editor with the pref file
                         MainViewManager.focusActivePane();
                     });
                 });
         }
         
     });
     
     // See if any startup files were passed to the application
     if (brackets.app.getPendingFilesToOpen) {
         brackets.app.getPendingFilesToOpen(function (err, paths) {
             DragAndDrop.openDroppedFiles(paths);
         });
     }
 });
コード例 #23
0
ファイル: FindBar.js プロジェクト: 1833183060/brackets
 FindBar.prototype._updateSearchBarFromPrefs = function () {
     // Have to make sure we explicitly cast the second parameter to a boolean, because
     // toggleClass expects literal true/false.
     this.$("#find-case-sensitive").toggleClass("active", !!PreferencesManager.getViewState("caseSensitive"));
     this.$("#find-regexp").toggleClass("active", !!PreferencesManager.getViewState("regexp"));
 };
コード例 #24
0
ファイル: HealthLogger.js プロジェクト: Coder206/brackets
 /**
  * Return all health data logged till now stored in the state prefs
  * @return {Object} Health Data aggregated till now
  */
 function getStoredHealthData() {
     var storedData = PreferencesManager.getViewState(HEALTH_DATA_STATE_KEY) || {};
     return storedData;
 }
コード例 #25
0
ファイル: main.js プロジェクト: 0b10011/brackets
 .on("change", function () {
     config.highlight = PreferencesManager.getViewState("livedev.highlight");
     _updateHighlightCheckmark();
 });
コード例 #26
0
ファイル: FindBar.js プロジェクト: marswh/brackets
 .on("keydown", "#find-what, #replace-with", function (e) {
     lastTypedTime = new Date().getTime();
     lastKeyCode = e.keyCode;
     var executeSearchIfNeeded = function () {
         // We only do instant search via node.
         if (FindUtils.isNodeSearchDisabled() || FindUtils.isInstantSearchDisabled()) {
             // we still keep the intrval timer up as instant search could get enabled/disabled based on node busy state
             return;
         }
         if (self._closed) {
             return;
         }
         currentTime = new Date().getTime();
         if (lastTypedTime && (currentTime - lastTypedTime >= 100) && self.getQueryInfo().query !==  lastQueriedText &&
                 !FindUtils.isNodeSearchInProgress()) {
             // init Search
             if (self._options.multifile) {
                 if ($(e.target).is("#find-what")) {
                     if (!self._options.replace) {
                         HealthLogger.searchDone(HealthLogger.SEARCH_INSTANT);
                         self.trigger("doFind");
                         lastQueriedText = self.getQueryInfo().query;
                     }
                 }
             }
         }
     };
     if (intervalId === 0) {
         intervalId = window.setInterval(executeSearchIfNeeded, 50);
     }
     var searchHistory = PreferencesManager.getViewState("searchHistory");
     var maxCount = PreferencesManager.get("maxSearchHistory");
     if (e.keyCode === KeyEvent.DOM_VK_RETURN) {
         e.preventDefault();
         e.stopPropagation();
         var searchVal = self.$("#find-what").val();
         var searchQueryIndex = searchHistory.indexOf(searchVal);
         if (searchQueryIndex !== -1) {
             searchHistory.splice(searchQueryIndex, 1);
         } else {
             if (searchHistory.length === maxCount) {
                 searchHistory.pop();
             }
         }
         if (searchVal) {
             searchHistory.unshift(searchVal);
         }
         PreferencesManager.setViewState("searchHistory", searchHistory);
         lastQueriedText = self.getQueryInfo().query;
         if (self._options.multifile) {
             if ($(e.target).is("#find-what")) {
                 if (self._options.replace) {
                     // Just set focus to the Replace field.
                     self.focusReplace();
                 } else {
                     HealthLogger.searchDone(HealthLogger.SEARCH_ON_RETURN_KEY);
                     // Trigger a Find (which really means "Find All" in this context).
                     self.trigger("doFind");
                 }
             } else {
                 HealthLogger.searchDone(HealthLogger.SEARCH_REPLACE_ALL);
                 self.trigger("doReplaceBatch");
             }
         } else {
             // In the single file case, we just want to trigger a Find Next (or Find Previous
             // if Shift is held down).
             self.trigger("doFind", e.shiftKey);
         }
         historyIndex = 0;
     } else if (e.keyCode === KeyEvent.DOM_VK_DOWN || e.keyCode === KeyEvent.DOM_VK_UP) {
         var quickSearchContainer = $(".quick-search-container");
         if (!self.searchField) {
             self.showSearchHints();
         } else if (!quickSearchContainer.is(':visible')) {
             quickSearchContainer.show();
         }
     }
 });
コード例 #27
0
define(function (require, exports, module) {
    "use strict";
    
    var Dialogs              = require("widgets/Dialogs"),
        DefaultDialogs       = require("widgets/DefaultDialogs"),
        ExtensionManager     = require("extensibility/ExtensionManager"),
        PreferencesManager   = require("preferences/PreferencesManager"),
        Global               = require("utils/Global"),
        NativeApp            = require("utils/NativeApp"),
        StringUtils          = require("utils/StringUtils"),
        Strings              = require("strings"),
        UpdateDialogTemplate = require("text!htmlContent/update-dialog.html"),
        UpdateListTemplate   = require("text!htmlContent/update-list.html");
    
    // duration of one day in milliseconds
    var ONE_DAY = 1000 * 60 * 60 * 24;

    // duration of two minutes in milliseconds
    var TWO_MINUTES = 1000 * 60 * 2;

    // Extract current build number from package.json version field 0.0.0-0
    var _buildNumber = Number(/-([0-9]+)/.exec(brackets.metadata.version)[1]);
    
    // Init default last build number
    PreferencesManager.stateManager.definePreference("lastNotifiedBuildNumber", "number", 0);
    // Time of last registry check for update
    PreferencesManager.stateManager.definePreference("lastExtensionRegistryCheckTime", "number", 0);
    // Data about available updates in the registry
    PreferencesManager.stateManager.definePreference("extensionUpdateInfo", "Array", []);

    PreferencesManager.convertPreferences(module, {
        "lastNotifiedBuildNumber": "user",
        "lastInfoURLFetchTime": "user",
        "updateInfo": "user"
    }, true);
    
    // This is the last version we notified the user about. If checkForUpdate()
    // is called with "false", only show the update notification dialog if there
    // is an update newer than this one. This value is saved in preferences.
    var _lastNotifiedBuildNumber = PreferencesManager.getViewState("lastNotifiedBuildNumber");
    
    // Last time the versionInfoURL was fetched
    var _lastInfoURLFetchTime = PreferencesManager.getViewState("lastInfoURLFetchTime");

    // URL to load version info from. By default this is loaded no more than once a day. If
    // you force an update check it is always loaded.
    
    // Information on all posted builds of Brackets. This is an Array, where each element is
    // an Object with the following fields:
    //
    //  {Number} buildNumber Number of the build
    //  {String} versionString String representation of the build number (ie "Sprint 14")
    //  {String} dateString Date of the build
    //  {String} releaseNotesURL URL of the release notes for this build
    //  {String} downloadURL URL to download this build
    //  {Array} newFeatures Array of new features in this build. Each entry has two fields:
    //      {String} name Name of the feature
    //      {String} description Description of the feature
    //
    // This array must be reverse sorted by buildNumber (newest build info first)
    
    /**
     * @private
     * Flag that indicates if we've added a click handler to the update notification icon.
     */
    var _addedClickHandler = false;

    /**
     * Construct a new version update url with the given locale.
     *
     * @param {string=} locale - optional locale, defaults to 'brackets.getLocale()' when omitted.
     * @param {boolean=} removeCountryPartOfLocale - optional, remove existing country information from locale 'en-gb' => 'en'
     * return {string} the new version update url
     */
    function _getVersionInfoUrl(locale, removeCountryPartOfLocale) {
        locale = locale || brackets.getLocale();

        if (removeCountryPartOfLocale) {
            locale = locale.substring(0, 2);
        }

        return brackets.config.update_info_url + locale + ".json";
    }

    /**
     * Get a data structure that has information for all builds of Brackets.
     *
     * If force is true, the information is always fetched from _versionInfoURL.
     * If force is false, we try to use cached information. If more than
     * 24 hours have passed since the last fetch, or if cached data can't be found,
     * the data is fetched again.
     *
     * If new data is fetched and dontCache is false, the data is saved in preferences
     * for quick fetching later.
     * _versionInfoUrl is used for unit testing.
     */
    function _getUpdateInformation(force, dontCache, _versionInfoUrl) {
        var result = new $.Deferred();
        var fetchData = false;
        var data;
        
        // If force is true, always fetch
        if (force) {
            fetchData = true;
        }
        
        // If we don't have data saved in prefs, fetch
        data = PreferencesManager.getViewState("updateInfo");
        if (!data) {
            fetchData = true;
        }
        
        // If more than 24 hours have passed since our last fetch, fetch again
        if ((new Date()).getTime() > _lastInfoURLFetchTime + ONE_DAY) {
            fetchData = true;
        }
        
        if (fetchData) {
            var lookupPromise = new $.Deferred(),
                localVersionInfoUrl;

            // If the current locale isn't "en" or "en-US", check whether we actually have a
            //   locale-specific update notification, and fall back to "en" if not.
            // Note: we check for both "en" and "en-US" to watch for the general case or
            //    country-specific English locale.  The former appears default on Mac, while
            //    the latter appears default on Windows.
            var locale = brackets.getLocale().toLowerCase();
            if (locale !== "en" && locale !== "en-us") {
                localVersionInfoUrl = _versionInfoUrl || _getVersionInfoUrl();
                $.ajax({
                    url: localVersionInfoUrl,
                    cache: false,
                    type: "HEAD"
                }).fail(function (jqXHR, status, error) {
                    // get rid of any country information from locale and try again
                    var tmpUrl = _getVersionInfoUrl(brackets.getLocale(), true);
                    if (tmpUrl !== localVersionInfoUrl) {
                        $.ajax({
                            url: tmpUrl,
                            cache: false,
                            type: "HEAD"
                        }).fail(function (jqXHR, status, error) {
                            localVersionInfoUrl = _getVersionInfoUrl("en");
                        }).done(function (jqXHR, status, error) {
                            localVersionInfoUrl = tmpUrl;
                        }).always(function (jqXHR, status, error) {
                            lookupPromise.resolve();
                        });
                    } else {
                        localVersionInfoUrl = _getVersionInfoUrl("en");
                        lookupPromise.resolve();
                    }
                }).done(function (jqXHR, status, error) {
                    lookupPromise.resolve();
                });
            } else {
                localVersionInfoUrl = _versionInfoUrl || _getVersionInfoUrl("en");
                lookupPromise.resolve();
            }

            lookupPromise.done(function () {
                $.ajax({
                    url: localVersionInfoUrl,
                    dataType: "json",
                    cache: false
                }).done(function (updateInfo, textStatus, jqXHR) {
                    if (!dontCache) {
                        _lastInfoURLFetchTime = (new Date()).getTime();
                        PreferencesManager.setViewState("lastInfoURLFetchTime", _lastInfoURLFetchTime);
                        PreferencesManager.setViewState("updateInfo", updateInfo);
                    }
                    result.resolve(updateInfo);
                }).fail(function (jqXHR, status, error) {
                    // When loading data for unit tests, the error handler is
                    // called but the responseText is valid. Try to use it here,
                    // but *don't* save the results in prefs.

                    if (!jqXHR.responseText) {
                        // Text is NULL or empty string, reject().
                        result.reject();
                        return;
                    }

                    try {
                        data = JSON.parse(jqXHR.responseText);
                        result.resolve(data);
                    } catch (e) {
                        result.reject();
                    }
                });
            });
        } else {
            result.resolve(data);
        }

        return result.promise();
    }
    
    /**
     * Return a new array of version information that is newer than "buildNumber".
     * Returns null if there is no new version information.
     */
    function _stripOldVersionInfo(versionInfo, buildNumber) {
        // Do a simple linear search. Since we are going in reverse-chronological order, we
        // should get through the search quickly.
        var lastIndex = 0;
        var len = versionInfo.length;
        
        while (lastIndex < len) {
            if (versionInfo[lastIndex].buildNumber <= buildNumber) {
                break;
            }
            lastIndex++;
        }
        
        if (lastIndex > 0) {
            return versionInfo.slice(0, lastIndex);
        }
        
        // No new version info
        return null;
    }
    
    /**
     * Show a dialog that shows the update
     */
    function _showUpdateNotificationDialog(updates) {
        Dialogs.showModalDialogUsingTemplate(Mustache.render(UpdateDialogTemplate, Strings))
            .done(function (id) {
                if (id === Dialogs.DIALOG_BTN_DOWNLOAD) {
                    // The first entry in the updates array has the latest download link
                    NativeApp.openURLInDefaultBrowser(updates[0].downloadURL);
                }
            });
        
        // Populate the update data
        var $dlg        = $(".update-dialog.instance"),
            $updateList = $dlg.find(".update-info");
        
        updates.Strings = Strings;
        $updateList.html(Mustache.render(UpdateListTemplate, updates));
    }
    
    /**
     * Calculate state of notification everytime registries are downloaded - no matter who triggered the download
     */
    function _onRegistryDownloaded() {
        var availableUpdates = ExtensionManager.getAvailableUpdates();
        PreferencesManager.setViewState("extensionUpdateInfo", availableUpdates);
        PreferencesManager.setViewState("lastExtensionRegistryCheckTime", (new Date()).getTime());
        $("#toolbar-extension-manager").toggleClass("updatesAvailable", availableUpdates.length > 0);
    }

    /**
     *  Every 24 hours downloads registry information to check for update, but only if the registry download
     *  wasn't triggered by another action (like opening extension manager)
     *  If there isn't 24 hours elapsed from the last download, use cached information from last download
     *  to determine state of the update notification.
     */
    function checkForExtensionsUpdate() {
        var lastExtensionRegistryCheckTime = PreferencesManager.getViewState("lastExtensionRegistryCheckTime"),
            timeOfNextCheck = lastExtensionRegistryCheckTime + ONE_DAY,
            currentTime = (new Date()).getTime();

        // update icon according to previously saved information
        var availableUpdates = PreferencesManager.getViewState("extensionUpdateInfo");
        availableUpdates = ExtensionManager.cleanAvailableUpdates(availableUpdates);
        $("#toolbar-extension-manager").toggleClass("updatesAvailable", availableUpdates.length > 0);

        if (availableUpdates.length === 0) {
            // icon is gray, no updates available
            if (currentTime > timeOfNextCheck) {
                // downloadRegistry, will be resolved in _onRegistryDownloaded
                ExtensionManager.downloadRegistry().done(function () {
                    // schedule another check in 24 hours + 2 minutes
                    setTimeout(checkForExtensionsUpdate, ONE_DAY + TWO_MINUTES);
                });
            } else {
                // schedule the download of the registry in appropriate time
                setTimeout(checkForExtensionsUpdate, (timeOfNextCheck - currentTime) + TWO_MINUTES);
            }
        }
    }

    /**
     * Check for updates. If "force" is true, update notification dialogs are always displayed
     * (if an update is available). If "force" is false, the update notification is only
     * displayed for newly available updates.
     *
     * If an update is available, show the "update available" notification icon in the title bar.
     *
     * @param {boolean} force If true, always show the notification dialog.
     * @param {Object} _testValues This should only be used for testing purposes. See comments for details.
     * @return {$.Promise} jQuery Promise object that is resolved or rejected after the update check is complete.
     */
    function checkForUpdate(force, _testValues) {
        // The second param, if non-null, is an Object containing value overrides. Values
        // in the object temporarily override the local values. This should *only* be used for testing.
        // If any overrides are set, permanent changes are not made (including showing
        // the update notification icon and saving prefs).
        var oldValues;
        var usingOverrides = false; // true if any of the values are overridden.
        var result = new $.Deferred();
        var versionInfoUrl;
        
        if (_testValues) {
            oldValues = {};
            
            if (_testValues.hasOwnProperty("_buildNumber")) {
                oldValues._buildNumber = _buildNumber;
                _buildNumber = _testValues._buildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("_lastNotifiedBuildNumber")) {
                oldValues._lastNotifiedBuildNumber = _lastNotifiedBuildNumber;
                _lastNotifiedBuildNumber = _testValues._lastNotifiedBuildNumber;
                usingOverrides = true;
            }

            if (_testValues.hasOwnProperty("_versionInfoURL")) {
                versionInfoUrl = _testValues._versionInfoURL;
                usingOverrides = true;
            }
        }
        
        _getUpdateInformation(force || usingOverrides, usingOverrides, versionInfoUrl)
            .done(function (versionInfo) {
                // Get all available updates
                var allUpdates = _stripOldVersionInfo(versionInfo, _buildNumber);
                
                // When running directly from GitHub source (as opposed to
                // an installed build), _buildNumber is 0. In this case, if the
                // test is not forced, don't show the update notification icon or
                // dialog.
                if (_buildNumber === 0 && !force) {
                    result.resolve();
                    return;
                }
                
                if (allUpdates) {
                    // Always show the "update available" icon if any updates are available
                    var $updateNotification = $("#update-notification");
                    
                    $updateNotification.css("display", "block");
                    if (!_addedClickHandler) {
                        _addedClickHandler = true;
                        $updateNotification.on("click", function () {
                            checkForUpdate(true);
                        });
                    }
                
                    // Only show the update dialog if force = true, or if the user hasn't been
                    // alerted of this update
                    if (force || allUpdates[0].buildNumber >  _lastNotifiedBuildNumber) {
                        _showUpdateNotificationDialog(allUpdates);
                        
                        // Update prefs with the last notified build number
                        _lastNotifiedBuildNumber = allUpdates[0].buildNumber;
                        // Don't save prefs is we have overridden values
                        if (!usingOverrides) {
                            PreferencesManager.setViewState("lastNotifiedBuildNumber", _lastNotifiedBuildNumber);
                        }
                    }
                } else if (force) {
                    // No updates are available. If force == true, let the user know.
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.NO_UPDATE_TITLE,
                        Strings.NO_UPDATE_MESSAGE
                    );
                }
        
                if (oldValues) {
                    if (oldValues.hasOwnProperty("_buildNumber")) {
                        _buildNumber = oldValues._buildNumber;
                    }
                    if (oldValues.hasOwnProperty("_lastNotifiedBuildNumber")) {
                        _lastNotifiedBuildNumber = oldValues._lastNotifiedBuildNumber;
                    }
                }
                result.resolve();
            })
            .fail(function () {
                // Error fetching the update data. If this is a forced check, alert the user
                if (force) {
                    Dialogs.showModalDialog(
                        DefaultDialogs.DIALOG_ID_ERROR,
                        Strings.ERROR_FETCHING_UPDATE_INFO_TITLE,
                        Strings.ERROR_FETCHING_UPDATE_INFO_MSG
                    );
                }
                result.reject();
            });
        
        return result.promise();
    }
    
    /**
     * Launches both check for Brackets update and check for installed extensions update
     */
    function launchAutomaticUpdate() {
        // launch immediately and then every 24 hours + 2 minutes
        checkForUpdate();
        checkForExtensionsUpdate();
        window.setInterval(checkForUpdate, ONE_DAY + TWO_MINUTES);
    }

    // Events listeners
    $(ExtensionManager).on("registryDownload", _onRegistryDownloaded);

    // Define public API
    exports.launchAutomaticUpdate = launchAutomaticUpdate;
    exports.checkForUpdate        = checkForUpdate;
});
コード例 #28
0
    /**
     * Get a data structure that has information for all builds of Brackets.
     *
     * If force is true, the information is always fetched from _versionInfoURL.
     * If force is false, we try to use cached information. If more than
     * 24 hours have passed since the last fetch, or if cached data can't be found,
     * the data is fetched again.
     *
     * If new data is fetched and dontCache is false, the data is saved in preferences
     * for quick fetching later.
     * _versionInfoUrl is used for unit testing.
     */
    function _getUpdateInformation(force, dontCache, _versionInfoUrl) {
        // Last time the versionInfoURL was fetched
        var lastInfoURLFetchTime = PreferencesManager.getViewState("lastInfoURLFetchTime");

        var result = new $.Deferred();
        var fetchData = false;
        var data;

        // If force is true, always fetch
        if (force) {
            fetchData = true;
        }

        // If we don't have data saved in prefs, fetch
        data = PreferencesManager.getViewState("updateInfo");
        if (!data) {
            fetchData = true;
        }

        // If more than 24 hours have passed since our last fetch, fetch again
        if ((new Date()).getTime() > lastInfoURLFetchTime + ONE_DAY) {
            fetchData = true;
        }

        if (fetchData) {
            var lookupPromise = new $.Deferred(),
                localVersionInfoUrl;

            // If the current locale isn't "en" or "en-US", check whether we actually have a
            //   locale-specific update notification, and fall back to "en" if not.
            // Note: we check for both "en" and "en-US" to watch for the general case or
            //    country-specific English locale.  The former appears default on Mac, while
            //    the latter appears default on Windows.
            var locale = brackets.getLocale().toLowerCase();
            if (locale !== "en" && locale !== "en-us") {
                localVersionInfoUrl = _versionInfoUrl || _getVersionInfoUrl();
                $.ajax({
                    url: localVersionInfoUrl,
                    cache: false,
                    type: "HEAD"
                }).fail(function (jqXHR, status, error) {
                    // get rid of any country information from locale and try again
                    var tmpUrl = _getVersionInfoUrl(brackets.getLocale(), true);
                    if (tmpUrl !== localVersionInfoUrl) {
                        $.ajax({
                            url: tmpUrl,
                            cache: false,
                            type: "HEAD"
                        }).fail(function (jqXHR, status, error) {
                            localVersionInfoUrl = _getVersionInfoUrl("en");
                        }).done(function (jqXHR, status, error) {
                            localVersionInfoUrl = tmpUrl;
                        }).always(function (jqXHR, status, error) {
                            lookupPromise.resolve();
                        });
                    } else {
                        localVersionInfoUrl = _getVersionInfoUrl("en");
                        lookupPromise.resolve();
                    }
                }).done(function (jqXHR, status, error) {
                    lookupPromise.resolve();
                });
            } else {
                localVersionInfoUrl = _versionInfoUrl || _getVersionInfoUrl("en");
                lookupPromise.resolve();
            }

            lookupPromise.done(function () {
                $.ajax({
                    url: localVersionInfoUrl,
                    dataType: "json",
                    cache: false
                }).done(function (updateInfo, textStatus, jqXHR) {
                    if (!dontCache) {
                        lastInfoURLFetchTime = (new Date()).getTime();
                        PreferencesManager.setViewState("lastInfoURLFetchTime", lastInfoURLFetchTime);
                        PreferencesManager.setViewState("updateInfo", updateInfo);
                    }
                    result.resolve(updateInfo);
                }).fail(function (jqXHR, status, error) {
                    // When loading data for unit tests, the error handler is
                    // called but the responseText is valid. Try to use it here,
                    // but *don't* save the results in prefs.

                    if (!jqXHR.responseText) {
                        // Text is NULL or empty string, reject().
                        result.reject();
                        return;
                    }

                    try {
                        data = JSON.parse(jqXHR.responseText);
                        result.resolve(data);
                    } catch (e) {
                        result.reject();
                    }
                });
            });
        } else {
            result.resolve(data);
        }

        return result.promise();
    }
コード例 #29
0
ファイル: Resizer.js プロジェクト: Coder206/brackets
    /**
     * Adds resizing and (optionally) expand/collapse capabilities to a given html element. The element's size
     * & visibility are automatically saved & restored as a view-state preference.
     *
     * Resizing can be configured in two directions:
     *  - Vertical ("vert"): Resizes the height of the element
     *  - Horizontal ("horz"): Resizes the width of the element
     *
     * Resizer handlers can be positioned on the element at:
     *  - Top ("top") or bottom ("bottom") for vertical resizing
     *  - Left ("left") or right ("right") for horizontal resizing
     *
     * A resizable element triggers the following events while resizing:
     *  - panelResizeStart: When the resize starts. Passed the new size.
     *  - panelResizeUpdate: When the resize gets updated. Passed the new size.
     *  - panelResizeEnd: When the resize ends. Passed the final size.
     *  - panelCollapsed: When the panel gets collapsed (or hidden). Passed the last size
     *      before collapse. May occur without any resize events.
     *  - panelExpanded: When the panel gets expanded (or shown). Passed the initial size.
     *      May occur without any resize events.
     *
     * @param {!DOMNode} element DOM element which should be made resizable. Must have an id attribute, for
     *                          use as a preferences key.
     * @param {!string} direction Direction of the resize action: one of the DIRECTION_* constants.
     * @param {!string} position Which side of the element can be dragged: one of the POSITION_* constants
     *                          (TOP/BOTTOM for vertical resizing or LEFT/RIGHT for horizontal).
     * @param {?number} minSize Minimum size (width or height) of the element's outer dimensions, including
     *                          border & padding. Defaults to DEFAULT_MIN_SIZE.
     * @param {?boolean} collapsible Indicates the panel is collapsible on double click on the
     *                          resizer. Defaults to false.
     * @param {?string} forceLeft CSS selector indicating element whose 'left' should be locked to the
     *                          the resizable element's size (useful for siblings laid out to the right of
     *                          the element). Must lie in element's parent's subtree.
     * @param {?boolean} createdByWorkspaceManager For internal use only
     * @param {?boolean} usePercentages Maintain the size of the element as a percentage of its parent
     *                          the default is to maintain the size of the element in pixels
     * @param {?boolean} _attachToParent Attaches the resizer element to parent of the element rather than
     *                          to element itself. Attach the resizer to the parent *ONLY* if element has the
     *                          same offset as parent otherwise the resizer will be incorrectly positioned.
     *                          FOR INTERNAL USE ONLY
     */
    function makeResizable(element, direction, position, minSize, collapsible, forceLeft, createdByWorkspaceManager, usePercentages, _attachToParent) {
        var $resizer            = $('<div class="' + direction + '-resizer"></div>'),
            $element            = $(element),
            $parent             = $element.parent(),
            $resizableElement   = $($element.find(".resizable-content:first")[0]),
            $body               = $(window.document.body),
            elementID           = $element.attr("id"),
            elementPrefs        = PreferencesManager.getViewState(elementID) || {},
            animationRequest    = null,
            directionProperty   = direction === DIRECTION_HORIZONTAL ? "clientX" : "clientY",
            directionIncrement  = (position === POSITION_TOP || position === POSITION_LEFT) ? 1 : -1,
            parentSizeFunction  = direction === DIRECTION_HORIZONTAL ? $parent.innerWidth : $parent.innerHeight,

            elementSizeFunction = function (newSize) {
                if (!newSize) {
                    // calling the function as a getter
                    if (direction === DIRECTION_HORIZONTAL) {
                        return this.width();
                    } else {
                        return this.height();
                    }
                } else if (!usePercentages) {
                    if (direction === DIRECTION_HORIZONTAL) {
                        return this.width(newSize);
                    } else {
                        return this.height(newSize);
                    }
                } else {
                    // calling the function as a setter
                    var parentSize = parentSizeFunction.apply($parent),
                        percentage,
                        prop;

                    if (direction === DIRECTION_HORIZONTAL) {
                        prop = "width";
                    } else {
                        prop = "height";
                    }
                    percentage = newSize / parentSize;
                    this.css(prop, (percentage * 100) + "%");

                    return this; // chainable
                }
            },

            resizerCSSPosition  = direction === DIRECTION_HORIZONTAL ? "left" : "top",
            contentSizeFunction = direction === DIRECTION_HORIZONTAL ? $resizableElement.width : $resizableElement.height;

        if (PreferencesManager.get(PREFS_PURE_CODE) &&
                ($element.hasClass("bottom-panel") || $element.hasClass("sidebar"))) {
            elementPrefs.visible = false;
        }

        if (!elementID) {
            console.error("Resizable panels must have a DOM id to use as a preferences key:", element);
            return;
        }
        // Detect legacy cases where panels in the editor area are created without using WorkspaceManager APIs
        if ($parent[0] && $parent.is(".content") && !createdByWorkspaceManager) {
            console.error("Resizable panels within the editor area should be created via WorkspaceManager.createBottomPanel(). \nElement:", element);
            return;
        }

        if (minSize === undefined) {
            minSize = DEFAULT_MIN_SIZE;
        }

        collapsible = collapsible || false;

        if (_attachToParent) {
            $parent.prepend($resizer);
        } else {
            $element.prepend($resizer);
        }
        // Important so min/max sizes behave predictably
        $element.css("box-sizing", "border-box");

        function adjustSibling(size) {
            if (forceLeft !== undefined) {
                $(forceLeft, $parent).css("left", size);
            }
        }

        function resizeElement(elementSize, contentSize) {
            elementSizeFunction.apply($element, [elementSize]);

            if ($resizableElement.length) {
                contentSizeFunction.apply($resizableElement, [contentSize]);
            }
        }

        // If the resizer is positioned right or bottom of the panel, we need to listen to
        // reposition it if the element size changes externally
        function repositionResizer(elementSize) {
            var resizerPosition = elementSize || 1;
            if (position === POSITION_RIGHT || position === POSITION_BOTTOM) {
                $resizer.css(resizerCSSPosition, resizerPosition);
            }
        }

        $element.data("removeSizable", function () {
            $resizer.off(".resizer");

            $element.removeData("show");
            $element.removeData("hide");
            $element.removeData("resyncSizer");
            $element.removeData("removeSizable");

            $resizer.remove();
        });

        $element.data("resyncSizer", function () {
            repositionResizer(elementSizeFunction.apply($element));
        });

        $element.data("show", function () {
            var elementOffset   = $element.offset(),
                elementSize     = elementSizeFunction.apply($element) || elementPrefs.size,
                contentSize     = contentSizeFunction.apply($resizableElement) || elementPrefs.contentSize;

            // Resize the element before showing it again. If the panel was collapsed by dragging
            // the resizer, the size of the element should be 0, so we restore size in preferences
            resizeElement(elementSize, contentSize);

            $element.show();
            elementPrefs.visible = true;

            if (collapsible) {
                if (_attachToParent) {
                    $parent.prepend($resizer);
                } else {
                    $element.prepend($resizer);
                }
                if (position === POSITION_TOP) {
                    $resizer.css(resizerCSSPosition, "");
                } else if (position === POSITION_RIGHT) {
                    $resizer.css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize);
                }
            }

            adjustSibling(elementSize);

            $element.trigger("panelExpanded", [elementSize]);
            PreferencesManager.setViewState(elementID, elementPrefs, null, isResizing);
        });

        $element.data("hide", function () {
            var elementOffset   = $element.offset(),
                elementSize     = elementSizeFunction.apply($element),
                resizerSize     = elementSizeFunction.apply($resizer);

            $element.hide();
            elementPrefs.visible = false;
            if (collapsible) {
                $resizer.insertBefore($element);
                if (position === POSITION_RIGHT) {
                    $resizer.css(resizerCSSPosition, "");
                } else if (position === POSITION_TOP) {
                    $resizer.css(resizerCSSPosition, elementOffset[resizerCSSPosition] + elementSize - resizerSize);
                }
            }

            adjustSibling(0);

            $element.trigger("panelCollapsed", [elementSize]);
            PreferencesManager.setViewState(elementID, elementPrefs, null, isResizing);
        });


        $resizer.on("mousedown.resizer", function (e) {
            var $resizeShield   = $("<div class='resizing-container " + direction + "-resizing' />"),
                startPosition   = e[directionProperty],
                startSize       = $element.is(":visible") ? elementSizeFunction.apply($element) : 0,
                newSize         = startSize,
                previousSize    = startSize,
                baseSize        = 0,
                resizeStarted   = false;

            isResizing = true;
            $body.append($resizeShield);

            if ($resizableElement.length) {
                $element.children().not(".horz-resizer, .vert-resizer, .resizable-content").each(function (index, child) {
                    if (direction === DIRECTION_HORIZONTAL) {
                        baseSize += $(child).outerWidth();
                    } else {
                        baseSize += $(child).outerHeight();
                    }
                });
            }

            function doRedraw() {
                // only run this if the mouse is down so we don't constantly loop even
                // after we're done resizing.
                if (!isResizing) {
                    return;
                }

                // Check for real size changes to avoid unnecessary resizing and events
                if (newSize !== previousSize) {
                    previousSize = newSize;

                    if ($element.is(":visible")) {
                        if (newSize < 10) {
                            toggle($element);
                            elementSizeFunction.apply($element, [0]);
                        } else {
                            // Trigger resizeStarted just before the first successful resize update
                            if (!resizeStarted) {
                                resizeStarted = true;
                                $element.trigger("panelResizeStart", newSize);
                            }

                            // Resize the main element to the new size. If there is a content element,
                            // its size is the new size minus the size of the non-resizable elements
                            resizeElement(newSize, (newSize - baseSize));
                            adjustSibling(newSize);

                            $element.trigger("panelResizeUpdate", [newSize]);
                        }
                    } else if (newSize > 10) {
                        elementSizeFunction.apply($element, [newSize]);
                        toggle($element);

                        // Trigger resizeStarted after expanding the element if it was previously collapsed
                        if (!resizeStarted) {
                            resizeStarted = true;
                            $element.trigger("panelResizeStart", newSize);
                        }
                    }
                }

                animationRequest = window.requestAnimationFrame(doRedraw);
            }

            function onMouseMove(e) {
                // calculate newSize adding to startSize the difference
                // between starting and current position, capped at minSize
                newSize = Math.max(startSize + directionIncrement * (startPosition - e[directionProperty]), minSize);

                // respect max size if one provided (e.g. by WorkspaceManager)
                var maxSize = $element.data("maxsize");
                if (maxSize !== undefined) {
                    newSize = Math.min(newSize, maxSize);
                }

                e.preventDefault();

                if (animationRequest === null) {
                    animationRequest = window.requestAnimationFrame(doRedraw);
                }
            }

            $(window.document).on("mousemove", onMouseMove);

            // If the element is marked as collapsible, check for double click
            // to toggle the element visibility
            if (collapsible) {
                $resizeShield.on("mousedown", function (e) {
                    $(window.document).off("mousemove", onMouseMove);
                    $resizeShield.off("mousedown");
                    $resizeShield.remove();
                    animationRequest = null;
                    toggle($element);
                });
            }

            function endResize(e) {
                if (isResizing) {

                    var elementSize	= elementSizeFunction.apply($element);
                    if ($element.is(":visible")) {
                        elementPrefs.size = elementSize;
                        if ($resizableElement.length) {
                            elementPrefs.contentSize = contentSizeFunction.apply($resizableElement);
                        }
                        PreferencesManager.setViewState(elementID, elementPrefs);
                        repositionResizer(elementSize);
                    }

                    isResizing = false;

                    if (resizeStarted) {
                        $element.trigger("panelResizeEnd", [elementSize]);
                    }

                    // We wait 300ms to remove the resizer container to capture a mousedown
                    // on the container that would account for double click
                    window.setTimeout(function () {
                        $(window.document).off("mousemove", onMouseMove);
                        $resizeShield.off("mousedown");
                        $resizeShield.remove();
                        animationRequest = null;
                    }, 300);
                }
            }

            $(window.document).one("mouseup", endResize);

            e.preventDefault();
        });

        // Panel preferences initialization
        if (elementPrefs) {

            if (elementPrefs.size !== undefined) {
                elementSizeFunction.apply($element, [elementPrefs.size]);
            }

            if (elementPrefs.contentSize !== undefined) {
                contentSizeFunction.apply($resizableElement, [elementPrefs.contentSize]);
            }

            if (elementPrefs.visible !== undefined && !elementPrefs.visible) {
                hide($element);
            } else {
                adjustSibling(elementSizeFunction.apply($element));
                repositionResizer(elementSizeFunction.apply($element));
            }
        }
    }
コード例 #30
0
ファイル: main.js プロジェクト: 0b10011/brackets
define(function main(require, exports, module) {
    "use strict";

    var DocumentManager     = require("document/DocumentManager"),
        Commands            = require("command/Commands"),
        AppInit             = require("utils/AppInit"),
        LiveDevelopment     = require("LiveDevelopment/LiveDevelopment"),
        Inspector           = require("LiveDevelopment/Inspector/Inspector"),
        CommandManager      = require("command/CommandManager"),
        PreferencesManager  = require("preferences/PreferencesManager"),
        Dialogs             = require("widgets/Dialogs"),
        DefaultDialogs      = require("widgets/DefaultDialogs"),
        UrlParams           = require("utils/UrlParams").UrlParams,
        Strings             = require("strings"),
        ExtensionUtils      = require("utils/ExtensionUtils"),
        StringUtils         = require("utils/StringUtils");

    var params = new UrlParams();
    var config = {
        experimental: false, // enable experimental features
        debug: true, // enable debug output and helpers
        autoconnect: false, // go live automatically after startup?
        highlight: true, // enable highlighting?
        highlightConfig: { // the highlight configuration for the Inspector
            borderColor:  {r: 255, g: 229, b: 153, a: 0.66},
            contentColor: {r: 111, g: 168, b: 220, a: 0.55},
            marginColor:  {r: 246, g: 178, b: 107, a: 0.66},
            paddingColor: {r: 147, g: 196, b: 125, a: 0.66},
            showInfo: true
        }
    };
    var _checkMark = "✓"; // Check mark character
    // Status labels/styles are ordered: error, not connected, progress1, progress2, connected.
    var _statusTooltip = [
        Strings.LIVE_DEV_STATUS_TIP_NOT_CONNECTED,
        Strings.LIVE_DEV_STATUS_TIP_NOT_CONNECTED,
        Strings.LIVE_DEV_STATUS_TIP_PROGRESS1,
        Strings.LIVE_DEV_STATUS_TIP_PROGRESS2,
        Strings.LIVE_DEV_STATUS_TIP_CONNECTED,
        Strings.LIVE_DEV_STATUS_TIP_OUT_OF_SYNC,
        Strings.LIVE_DEV_STATUS_TIP_SYNC_ERROR
    ];

    var _statusStyle = ["warning", "", "info", "info", "success", "out-of-sync", "sync-error"];  // Status indicator's CSS class
    var _allStatusStyles = _statusStyle.join(" ");

    var _$btnGoLive; // reference to the GoLive button
    var _$btnHighlight; // reference to the HighlightButton

    /** Load Live Development LESS Style */
    function _loadStyles() {
        var lessText    = require("text!LiveDevelopment/main.less"),
            parser      = new less.Parser();
        
        parser.parse(lessText, function onParse(err, tree) {
            console.assert(!err, err);
            ExtensionUtils.addEmbeddedStyleSheet(tree.toCSS());
        });
    }

    /**
     * Change the appearance of a button. Omit text to remove any extra text; omit style to return to default styling;
     * omit tooltip to leave tooltip unchanged.
     */
    function _setLabel($btn, text, style, tooltip) {
        // Clear text/styles from previous status
        $("span", $btn).remove();
        $btn.removeClass(_allStatusStyles);

        // Set text/styles for new status
        if (text && text.length > 0) {
            $("<span class=\"label\">")
                .addClass(style)
                .text(text)
                .appendTo($btn);
        } else {
            $btn.addClass(style);
        }

        if (tooltip) {
            $btn.attr("title", tooltip);
        }
    }

    /**
     * Toggles LiveDevelopment and synchronizes the state of UI elements that reports LiveDevelopment status
     *
     * Stop Live Dev when in an active state (ACTIVE, OUT_OF_SYNC, SYNC_ERROR).
     * Start Live Dev when in an inactive state (ERROR, INACTIVE).
     * Do nothing when in a connecting state (CONNECTING, LOADING_AGENTS).
     */
    function _handleGoLiveCommand() {
        if (LiveDevelopment.status >= LiveDevelopment.STATUS_ACTIVE) {
            LiveDevelopment.close();
        } else if (LiveDevelopment.status <= LiveDevelopment.STATUS_INACTIVE) {
            if (!params.get("skipLiveDevelopmentInfo") && !PreferencesManager.getViewState("livedev.afterFirstLaunch")) {
                PreferencesManager.setViewState("livedev.afterFirstLaunch", "true");
                Dialogs.showModalDialog(
                    DefaultDialogs.DIALOG_ID_INFO,
                    Strings.LIVE_DEVELOPMENT_INFO_TITLE,
                    Strings.LIVE_DEVELOPMENT_INFO_MESSAGE
                ).done(function (id) {
                    LiveDevelopment.open();
                });
            } else {
                LiveDevelopment.open();
            }
        }
    }

    /** Called on status change */
    function _showStatusChangeReason(reason) {
        // Destroy the previous twipsy (options are not updated otherwise)
        _$btnGoLive.twipsy("hide").removeData("twipsy");
        
        // If there was no reason or the action was an explicit request by the user, don't show a twipsy
        if (!reason || reason === "explicit_close") {
            return;
        }

        // Translate the reason
        var translatedReason = Strings["LIVE_DEV_" + reason.toUpperCase()];
        if (!translatedReason) {
            translatedReason = StringUtils.format(Strings.LIVE_DEV_CLOSED_UNKNOWN_REASON, reason);
        }
        
        // Configure the twipsy
        var options = {
            placement: "left",
            trigger: "manual",
            autoHideDelay: 5000,
            title: function () {
                return translatedReason;
            }
        };

        // Show the twipsy with the explanation
        _$btnGoLive.twipsy(options).twipsy("show");
    }
    
    /** Create the menu item "Go Live" */
    function _setupGoLiveButton() {
        _$btnGoLive = $("#toolbar-go-live");
        _$btnGoLive.click(function onGoLive() {
            _handleGoLiveCommand();
        });
        $(LiveDevelopment).on("statusChange", function statusChange(event, status, reason) {
            // status starts at -1 (error), so add one when looking up name and style
            // See the comments at the top of LiveDevelopment.js for details on the
            // various status codes.
            _setLabel(_$btnGoLive, null, _statusStyle[status + 1], _statusTooltip[status + 1]);
            _showStatusChangeReason(reason);
            if (config.autoconnect) {
                window.sessionStorage.setItem("live.enabled", status === 3);
            }
        });

        // Initialize tooltip for 'not connected' state
        _setLabel(_$btnGoLive, null, _statusStyle[1], _statusTooltip[1]);
    }
    
    /** Maintains state of the Live Preview menu item */
    function _setupGoLiveMenu() {
        $(LiveDevelopment).on("statusChange", function statusChange(event, status) {
            // Update the checkmark next to 'Live Preview' menu item
            // Add checkmark when status is STATUS_ACTIVE; otherwise remove it
            CommandManager.get(Commands.FILE_LIVE_FILE_PREVIEW).setChecked(status === LiveDevelopment.STATUS_ACTIVE);
            CommandManager.get(Commands.FILE_LIVE_HIGHLIGHT).setEnabled(status === LiveDevelopment.STATUS_ACTIVE);
        });
    }

    function _updateHighlightCheckmark() {
        CommandManager.get(Commands.FILE_LIVE_HIGHLIGHT).setChecked(config.highlight);
    }
    
    function _handlePreviewHighlightCommand() {
        config.highlight = !config.highlight;
        _updateHighlightCheckmark();
        if (config.highlight) {
            LiveDevelopment.showHighlight();
        } else {
            LiveDevelopment.hideHighlight();
        }
        PreferencesManager.setViewState("livedev.highlight", config.highlight);
    }
    
    /** Setup window references to useful LiveDevelopment modules */
    function _setupDebugHelpers() {
        window.ld = LiveDevelopment;
        window.i = Inspector;
        window.report = function report(params) { window.params = params; console.info(params); };
    }

    /** Initialize LiveDevelopment */
    AppInit.appReady(function () {
        params.parse();

        Inspector.init(config);
        LiveDevelopment.init(config);
        _loadStyles();
        _setupGoLiveButton();
        _setupGoLiveMenu();

        _updateHighlightCheckmark();
        
        if (config.debug) {
            _setupDebugHelpers();
        }

        // trigger autoconnect
        if (config.autoconnect &&
                window.sessionStorage.getItem("live.enabled") === "true" &&
                DocumentManager.getCurrentDocument()) {
            _handleGoLiveCommand();
        }
        
        // Redraw highlights when window gets focus. This ensures that the highlights
        // will be in sync with any DOM changes that may have occurred.
        $(window).focus(function () {
            if (Inspector.connected() && config.highlight) {
                LiveDevelopment.redrawHighlight();
            }
        });
    });
    
    // init prefs
    PreferencesManager.stateManager.definePreference("livedev.highlight", "boolean", true)
        .on("change", function () {
            config.highlight = PreferencesManager.getViewState("livedev.highlight");
            _updateHighlightCheckmark();
        });
    
    PreferencesManager.convertPreferences(module, {
        "highlight": "user livedev.highlight",
        "afterFirstLaunch": "user livedev.afterFirstLaunch"
    }, true);
    
    config.highlight = PreferencesManager.getViewState("livedev.highlight");
   
    // init commands
    CommandManager.register(Strings.CMD_LIVE_FILE_PREVIEW,  Commands.FILE_LIVE_FILE_PREVIEW, _handleGoLiveCommand);
    CommandManager.register(Strings.CMD_LIVE_HIGHLIGHT, Commands.FILE_LIVE_HIGHLIGHT, _handlePreviewHighlightCommand);
    CommandManager.get(Commands.FILE_LIVE_HIGHLIGHT).setEnabled(false);

    // Export public functions
});