| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- @app =
- _$: $
- _$$: $$
- _page: page
- collections: {}
- models: {}
- templates: {}
- views: {}
- init: ->
- try @initErrorTracking() catch
- return unless @browserCheck()
- @showLoading()
- @el = $('._app')
- @store = new Store
- @appCache = new app.AppCache if app.AppCache.isEnabled()
- @settings = new app.Settings @store
- @docs = new app.collections.Docs
- @disabledDocs = new app.collections.Docs
- @entries = new app.collections.Entries
- @router = new app.Router
- @shortcuts = new app.Shortcuts
- @document = new app.views.Document
- @mobile = new app.views.Mobile if @isMobile()
- if navigator.userAgent.match /iPad;.*CPU.*OS 7_\d/i
- document.documentElement.style.height = "#{window.innerHeight}px"
- if @DOC
- @bootOne()
- else if @DOCS
- @bootAll()
- else
- @onBootError()
- return
- browserCheck: ->
- return true if @isSupportedBrowser()
- document.body.className = ''
- document.body.innerHTML = app.templates.unsupportedBrowser
- false
- initErrorTracking: ->
- # Show a warning message and don't track errors when the app is loaded
- # from a domain other than our own, because things are likely to break.
- # (e.g. cross-domain requests)
- if @isInvalidLocation()
- new app.views.Notif 'InvalidLocation'
- else
- if @config.sentry_dsn
- Raven.config @config.sentry_dsn,
- whitelistUrls: [/devdocs/]
- includePaths: [/devdocs/]
- ignoreErrors: [/dpQuery/, /NPObject/, /NS_ERROR/, /^null$/]
- tags:
- mode: if @DOC then 'single' else 'full'
- iframe: (window.top isnt window).toString()
- .install()
- @previousErrorHandler = onerror
- window.onerror = @onWindowError.bind(@)
- return
- bootOne: ->
- @doc = new app.models.Doc @DOC
- @docs.reset [@doc]
- @doc.load @start.bind(@), @onBootError.bind(@), readCache: true
- new app.views.Notice 'singleDoc', @doc
- delete @DOC
- return
- bootAll: ->
- docs = @settings.getDocs()
- for doc in @DOCS
- (if docs.indexOf(doc.slug) >= 0 then @docs else @disabledDocs).add(doc)
- @migrateDocs()
- @docs.sort()
- @disabledDocs.sort()
- @docs.load @start.bind(@), @onBootError.bind(@), readCache: true, writeCache: true
- delete @DOCS
- return
- start: ->
- @entries.add doc.toEntry() for doc in @docs.all()
- @entries.add doc.toEntry() for doc in @disabledDocs.all()
- @initDoc(doc) for doc in @docs.all()
- @db = new app.DB()
- @trigger 'ready'
- @router.start()
- @hideLoading()
- @welcomeBack() unless @doc
- @removeEvent 'ready bootError'
- try navigator.mozApps?.getSelf().onsuccess = -> app.mozApp = true catch
- return
- initDoc: (doc) ->
- @entries.add type.toEntry() for type in doc.types.all()
- @entries.add doc.entries.all()
- return
- migrateDocs: ->
- for slug in @settings.getDocs() when not @docs.findBy('slug', slug)
- needsSaving = true
- if doc = @disabledDocs.findBy('slug_without_version', slug)
- @disabledDocs.remove(doc)
- @docs.add(doc)
- @saveDocs() if needsSaving
- enableDoc: (doc, _onSuccess, onError) ->
- return if @docs.contains(doc)
- onSuccess = =>
- @disabledDocs.remove(doc)
- @docs.add(doc)
- @docs.sort()
- @initDoc(doc)
- @saveDocs()
- _onSuccess()
- return
- doc.load onSuccess, onError, writeCache: true
- return
- saveDocs: ->
- @settings.setDocs(doc.slug for doc in @docs.all())
- @appCache?.updateInBackground()
- welcomeBack: ->
- visitCount = @settings.get('count')
- @settings.set 'count', ++visitCount
- new app.views.Notif 'Share', autoHide: null if visitCount is 5
- new app.views.News()
- @updateChecker = new app.UpdateChecker()
- reload: ->
- @docs.clearCache()
- @disabledDocs.clearCache()
- if @appCache then @appCache.reload() else window.location = '/'
- return
- reset: ->
- @store.clear()
- @settings.reset()
- @db?.reset()
- @appCache?.update()
- window.location = '/'
- return
- showTip: (tip) ->
- return if @isSingleDoc()
- tips = @settings.get('tips') || []
- if tips.indexOf(tip) is -1
- tips.push(tip)
- @settings.set('tips', tips)
- new app.views.Tip(tip)
- return
- showLoading: ->
- document.body.classList.remove '_noscript'
- document.body.classList.add '_loading'
- return
- hideLoading: ->
- document.body.classList.remove '_booting'
- document.body.classList.remove '_loading'
- return
- indexHost: ->
- # Can't load the index files from the host/CDN when applicationCache is
- # enabled because it doesn't support caching URLs that use CORS.
- @config[if @appCache and @settings.hasDocs() then 'index_path' else 'docs_host']
- onBootError: (args...) ->
- @trigger 'bootError'
- @hideLoading()
- return
- onQuotaExceeded: ->
- return if @quotaExceeded
- @quotaExceeded = true
- new app.views.Notif 'QuotaExceeded', autoHide: null
- Raven.captureMessage 'QuotaExceededError'
- onWindowError: (args...) ->
- if @isInjectionError args...
- @onInjectionError()
- else if @isAppError args...
- @previousErrorHandler? args...
- @hideLoading()
- @errorNotif or= new app.views.Notif 'Error'
- @errorNotif.show()
- return
- onInjectionError: ->
- unless @injectionError
- @injectionError = true
- alert """
- JavaScript code has been injected in the page which prevents DevDocs from running correctly.
- Please check your browser extensions/addons. """
- Raven.captureMessage 'injection error'
- return
- isInjectionError: ->
- # Some browser extensions expect the entire web to use jQuery.
- # I gave up trying to fight back.
- window.$ isnt app._$ or window.$$ isnt app._$$ or window.page isnt app._page
- isAppError: (error, file) ->
- # Ignore errors from external scripts.
- file and file.indexOf('devdocs') isnt -1 and file.indexOf('.js') is file.length - 3
- isSupportedBrowser: ->
- try
- features =
- bind: !!Function::bind
- pushState: !!history.pushState
- matchMedia: !!window.matchMedia
- classList: !!document.body.classList
- insertAdjacentHTML: !!document.body.insertAdjacentHTML
- defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false
- cssGradients: supportsCssGradients()
- for key, value of features when not value
- Raven.captureMessage "unsupported/#{key}"
- return false
- true
- catch error
- Raven.captureMessage 'unsupported/exception', extra: { error: error }
- false
- isSingleDoc: ->
- !!(@DOC or @doc)
- isMobile: ->
- try
- # Need to sniff the user agent because some Android and Windows Phone devices don't take
- # resolution (dpi) into account when reporting device width/height.
- @_isMobile ?= (window.matchMedia('(max-device-width: 767px)').matches) or
- (window.matchMedia('(max-device-height: 767px) and (max-device-width: 1024px)').matches) or
- (navigator.userAgent.indexOf('Android') isnt -1 and navigator.userAgent.indexOf('Mobile') isnt -1) or
- (navigator.userAgent.indexOf('IEMobile') isnt -1)
- catch
- @_isMobile = false
- isInvalidLocation: ->
- @config.env is 'production' and location.host.indexOf(app.config.production_host) isnt 0
- supportsCssGradients = ->
- el = document.createElement('div')
- el.style.cssText = "background-image: -webkit-linear-gradient(top, #000, #fff); background-image: linear-gradient(to top, #000, #fff);"
- el.style.backgroundImage.indexOf('gradient') >= 0
- $.extend app, Events
|