mongo_browser 0.1.3 → 0.2.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +3 -3
- data/README.md +1 -25
- data/Rakefile +0 -1
- data/app/assets/images/background.png +0 -0
- data/app/assets/javascripts/app/controllers/alerts.js.coffee +12 -0
- data/app/assets/javascripts/app/controllers/breadcrumbs.js.coffee +25 -0
- data/app/assets/javascripts/app/controllers/collections.js.coffee +40 -0
- data/app/assets/javascripts/app/controllers/databases.js.coffee +39 -0
- data/app/assets/javascripts/app/controllers/documents.js.coffee +49 -0
- data/app/assets/javascripts/app/controllers/main.js.coffee +10 -0
- data/app/assets/javascripts/app/controllers/server_info.js.coffee +14 -0
- data/app/assets/javascripts/app/controllers.js.coffee +2 -0
- data/app/assets/javascripts/app/directives.js.coffee +20 -0
- data/app/assets/javascripts/app/filters.js.coffee +48 -0
- data/app/assets/javascripts/app/modules/dialogs.js.coffee +29 -0
- data/app/assets/javascripts/app/modules/pager.js.coffee +87 -0
- data/app/assets/javascripts/app/modules/table_filter.js.coffee +27 -0
- data/app/assets/javascripts/app/resources.js.coffee +22 -0
- data/app/assets/javascripts/app/services.js.coffee +36 -0
- data/app/assets/javascripts/app.js.coffee +8 -0
- data/app/assets/javascripts/application.js.coffee +35 -6
- data/app/assets/javascripts/templates/.gitkeep +0 -0
- data/app/assets/javascripts/templates.js.coffee +1 -0
- data/app/assets/javascripts/vendor.js.coffee +5 -0
- data/app/assets/stylesheets/application.css.scss +46 -3
- data/app/assets/templates/collections.html +53 -0
- data/app/assets/templates/databases.html +32 -0
- data/app/assets/templates/documents.html +45 -0
- data/app/assets/templates/pager.html +13 -0
- data/app/{views/server_info.erb → assets/templates/server_info.html} +5 -7
- data/app/assets/templates/table_filter.html +10 -0
- data/bin/mongo_browser +8 -45
- data/config-e2e.ru +20 -0
- data/grunt.js +36 -0
- data/lib/mongo_browser/application.rb +143 -64
- data/lib/mongo_browser/middleware/sprockets_base.rb +11 -0
- data/lib/mongo_browser/middleware/sprockets_sinatra.rb +3 -7
- data/lib/mongo_browser/middleware/sprockets_specs.rb +18 -0
- data/lib/mongo_browser/models/collection.rb +67 -0
- data/lib/mongo_browser/models/database.rb +52 -0
- data/lib/mongo_browser/models/document.rb +17 -0
- data/lib/mongo_browser/models/pager.rb +30 -0
- data/lib/mongo_browser/models/server.rb +51 -0
- data/lib/mongo_browser/version.rb +1 -1
- data/lib/mongo_browser.rb +10 -5
- data/mongo_browser.gemspec +4 -11
- data/public/index.html +47 -0
- data/script/ci_all +21 -0
- data/script/ci_e2e +11 -0
- data/script/ci_javascripts +4 -0
- data/script/ci_rspec +3 -0
- data/spec/features/collections_list_spec.rb +6 -49
- data/spec/features/documents_list_spec.rb +15 -14
- data/spec/features/server_info_spec.rb +3 -3
- data/spec/javascripts/app/controllers/alerts_spec.js.coffee +36 -0
- data/spec/javascripts/app/controllers/breadcrumbs_spec.js.coffee +28 -0
- data/spec/javascripts/app/controllers/collections_spec.js.coffee +78 -0
- data/spec/javascripts/app/controllers/databases_spec.js.coffee +55 -0
- data/spec/javascripts/app/controllers/documents_spec.js.coffee +62 -0
- data/spec/javascripts/app/controllers/main_spec.js.coffee +20 -0
- data/spec/javascripts/app/controllers/server_info_spec.js.coffee +21 -0
- data/spec/javascripts/app/directives_spec.js.coffee +58 -0
- data/spec/javascripts/app/filters_spec.js.coffee +99 -0
- data/spec/javascripts/app/modules/dialogs_spec.js.coffee +51 -0
- data/spec/javascripts/app/modules/pager_spec.js.coffee +104 -0
- data/spec/javascripts/app/modules/table_filter_spec.js.coffee +76 -0
- data/spec/javascripts/app/services_spec.js.coffee +83 -0
- data/spec/javascripts/config/testacular-e2e.conf.js +19 -0
- data/spec/javascripts/config/testacular.conf.js +43 -0
- data/spec/javascripts/e2e/collections_scenario.js.coffee +49 -0
- data/spec/javascripts/e2e/databases_scenario.js.coffee +74 -0
- data/spec/javascripts/e2e/documents_scenario.js.coffee +18 -0
- data/spec/javascripts/e2e/server_info_scenario.js.coffee +12 -0
- data/spec/javascripts/helpers/matchers.js.coffee +5 -0
- data/spec/javascripts/helpers/mocks.js.coffee +7 -0
- data/spec/javascripts/helpers_e2e/app_element.js.coffee +6 -0
- data/spec/javascripts/lib/angular-mocks.js +1740 -0
- data/spec/javascripts/lib/angular-scenario.js +26147 -0
- data/spec/javascripts/lib/jasmine-html.js +681 -0
- data/spec/javascripts/lib/jasmine.css +82 -0
- data/spec/javascripts/lib/jasmine.js +2600 -0
- data/spec/javascripts/runner.html +54 -0
- data/spec/javascripts/runner_e2e.html +10 -0
- data/spec/javascripts/spec.js.coffee +2 -0
- data/spec/javascripts/spec_e2e.js.coffee +2 -0
- data/spec/lib/models/collection_spec.rb +80 -0
- data/spec/lib/models/database_spec.rb +75 -0
- data/spec/lib/models/document_spec.rb +17 -0
- data/spec/lib/models/pager_spec.rb +64 -0
- data/spec/lib/models/server_spec.rb +76 -0
- data/spec/lib/mongo_browser_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -19
- data/spec/support/feature_example_group.rb +8 -3
- data/spec/support/fixtures/databases.json +8 -0
- data/spec/support/fixtures.rb +20 -3
- data/spec/support/matchers/have_flash_message.rb +1 -1
- data/spec/support/mongod.rb +37 -21
- data/spec/support/mongodb.conf +47 -0
- data/vendor/assets/javascripts/angular/angular-bootstrap.js +166 -0
- data/vendor/assets/javascripts/angular/angular-resource.js +435 -0
- data/vendor/assets/javascripts/angular/angular-sanitize.js +535 -0
- data/vendor/assets/javascripts/angular/angular.js +14531 -0
- data/vendor/assets/javascripts/underscore.js +1200 -0
- metadata +136 -148
- data/app/assets/javascripts/app/table_filter.js.coffee +0 -50
- data/app/assets/javascripts/ujs.js.coffee +0 -23
- data/app/views/collections/index.erb +0 -59
- data/app/views/databases/index.erb +0 -29
- data/app/views/documents/index.erb +0 -61
- data/app/views/layout/_flash_messages.erb +0 -10
- data/app/views/layout/_navbar.erb +0 -22
- data/app/views/layout.erb +0 -20
- data/app/views/shared/_filter.erb +0 -14
- data/features/mongo_browser.feature +0 -18
- data/features/step_definitions/mongo_browser_steps.rb +0 -1
- data/features/support/env.rb +0 -18
- data/spec/features/databases_list_spec.rb +0 -65
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,10 +3,10 @@ language: ruby
|
|
3
3
|
rvm:
|
4
4
|
- 1.9.3
|
5
5
|
|
6
|
-
|
6
|
+
before_script:
|
7
|
+
- "npm install -g grunt testacular@canary --quiet"
|
7
8
|
- "export DISPLAY=:99.0"
|
8
9
|
- "sh -e /etc/init.d/xvfb start"
|
9
10
|
|
10
11
|
script:
|
11
|
-
- "
|
12
|
-
- "cucumber features"
|
12
|
+
- "./script/ci_all"
|
data/README.md
CHANGED
@@ -16,33 +16,9 @@ Run the application
|
|
16
16
|
|
17
17
|
$ mongo_browser
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
$ mongo_browser --demonize
|
22
|
-
|
23
|
-
Application will be available by default at http://localhost:4567
|
19
|
+
Application will be available by default at http://localhost:5678
|
24
20
|
In order to run it on custom port number pass the `--port` option with selected port.
|
25
21
|
|
26
|
-
Other options are:
|
27
|
-
|
28
|
-
$ mongo_browser --help
|
29
|
-
[17/11 16:10:06] Usage: mongo_browser [options]
|
30
|
-
|
31
|
-
v0.1.0
|
32
|
-
|
33
|
-
Options:
|
34
|
-
--version Show help/version info
|
35
|
-
--port PORT MongoBrowser port
|
36
|
-
(Default: 4567)
|
37
|
-
--mongodb-host HOST Mongodb database host
|
38
|
-
(Default: localhost)
|
39
|
-
--mongodb-port PORT Mongodb database port
|
40
|
-
(Default: 27017)
|
41
|
-
--demonize Run the app in the background
|
42
|
-
--log-level LEVEL Set the logging level
|
43
|
-
(debug|info|warn|error|fatal)
|
44
|
-
(Default: info)
|
45
|
-
|
46
22
|
## Contributing
|
47
23
|
|
48
24
|
1. Fork it
|
data/Rakefile
CHANGED
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
class AlertsController
|
4
|
+
constructor: (@$scope, @alerts) ->
|
5
|
+
@$scope.alertMessages = @alerts.messages
|
6
|
+
|
7
|
+
@$scope.disposeAlert = (id) =>
|
8
|
+
@alerts.dispose(id)
|
9
|
+
|
10
|
+
AlertsController.$inject = ["$scope", "alerts"]
|
11
|
+
|
12
|
+
module.controller "alerts", AlertsController
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
# TODO find better solution for breadcrumbs
|
4
|
+
class BreadcrumbsController
|
5
|
+
constructor: (@$rootScope, @$scope) ->
|
6
|
+
|
7
|
+
@$scope.showDatabase = => @$scope.dbName
|
8
|
+
@$scope.showCollection = => @$scope.collectionName
|
9
|
+
|
10
|
+
@$rootScope.$on "$routeChangeSuccess", (scope, route) =>
|
11
|
+
@$scope.dbName = route.params.dbName
|
12
|
+
@$scope.collectionName = route.params.collectionName
|
13
|
+
|
14
|
+
if @$scope.dbName
|
15
|
+
@$scope.database =
|
16
|
+
name: @$scope.dbName
|
17
|
+
|
18
|
+
if @$scope.collectionName
|
19
|
+
@$scope.collection =
|
20
|
+
dbName: @$scope.dbName
|
21
|
+
name: @$scope.collectionName
|
22
|
+
|
23
|
+
BreadcrumbsController.$inject = ["$rootScope", "$scope"]
|
24
|
+
|
25
|
+
module.controller "breadcrumbs", BreadcrumbsController
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
# TODO clenup this controller, see DatabasesController
|
4
|
+
class CollectionsController
|
5
|
+
constructor: (@$scope, @$routeParams, @Collection, @$http, @confirmationDialog, @alerts) ->
|
6
|
+
|
7
|
+
@$scope.dbName = @$routeParams.dbName
|
8
|
+
@$scope.filterValue = ""
|
9
|
+
|
10
|
+
_onLoadComplete = (data) =>
|
11
|
+
@$scope.collections = data
|
12
|
+
@$scope.loading = false
|
13
|
+
|
14
|
+
@$scope.fetchCollections = =>
|
15
|
+
@$scope.loading = true
|
16
|
+
@$scope.collections = @Collection.query({ dbName: @$scope.dbName }, _onLoadComplete)
|
17
|
+
|
18
|
+
@$scope.fetchCollections()
|
19
|
+
|
20
|
+
# TODO create resource for this call
|
21
|
+
@$http.get("/api/databases/#{@$scope.dbName}/stats.json").success (data) =>
|
22
|
+
@$scope.dbStats = data
|
23
|
+
|
24
|
+
@$scope.isLoading = => @$scope.loading
|
25
|
+
|
26
|
+
@$scope.delete = (collection) =>
|
27
|
+
@confirmationDialog
|
28
|
+
message: "Deleting #{collection.name}. Are you sure?"
|
29
|
+
onOk: =>
|
30
|
+
resource = new @Collection()
|
31
|
+
params = dbName: collection.dbName, id: collection.name
|
32
|
+
|
33
|
+
resource.$delete params, =>
|
34
|
+
@alerts.info("Collection #{collection.name} has been deleted.")
|
35
|
+
@$scope.fetchCollections()
|
36
|
+
|
37
|
+
CollectionsController.$inject = ["$scope", "$routeParams",
|
38
|
+
"Collection", "$http", "confirmationDialog", "alerts"]
|
39
|
+
|
40
|
+
module.controller "collections", CollectionsController
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
class DatabasesController
|
4
|
+
constructor: (@$scope, @Database, @confirmationDialog, @alerts) ->
|
5
|
+
@loading = false
|
6
|
+
@fetchDatabases()
|
7
|
+
|
8
|
+
# Scope variables
|
9
|
+
@$scope.filterValue = ""
|
10
|
+
|
11
|
+
# Scope methods
|
12
|
+
@$scope.isLoading = -> @loading
|
13
|
+
@$scope.delete = (database) => @dropWithConfirmation(database)
|
14
|
+
|
15
|
+
fetchDatabases: ->
|
16
|
+
@loading = true
|
17
|
+
@Database.query(@onLoadComplete)
|
18
|
+
|
19
|
+
onLoadComplete: (data) =>
|
20
|
+
@$scope.databases = data
|
21
|
+
@loading = false
|
22
|
+
|
23
|
+
dropWithConfirmation: (database) =>
|
24
|
+
@confirmationDialog
|
25
|
+
message: "Deleting #{database.name}. Are you sure?"
|
26
|
+
onOk: => @drop(database)
|
27
|
+
|
28
|
+
drop: (database) ->
|
29
|
+
resource = new @Database()
|
30
|
+
params = id: database.name
|
31
|
+
|
32
|
+
resource.$delete params, =>
|
33
|
+
@alerts.info("Database #{database.name} has been deleted.")
|
34
|
+
@fetchDatabases()
|
35
|
+
|
36
|
+
DatabasesController.$inject = ["$scope",
|
37
|
+
"Database", "confirmationDialog", "alerts"]
|
38
|
+
|
39
|
+
module.controller "databases", DatabasesController
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
# TODO clenup this controller, see DatabasesController
|
4
|
+
class DocumentsController
|
5
|
+
constructor: (@$scope, @$routeParams, @$http, @Document, @confirmationDialog, @alerts) ->
|
6
|
+
@$scope.dbName = @$routeParams.dbName
|
7
|
+
@$scope.collectionName = @$routeParams.collectionName
|
8
|
+
|
9
|
+
@$scope.isLoading = => @$scope.loading
|
10
|
+
|
11
|
+
_onLoadComplete = (data) =>
|
12
|
+
@$scope.loading = false
|
13
|
+
|
14
|
+
@$scope.documents = data.documents
|
15
|
+
@$scope.page = data.page
|
16
|
+
@$scope.totalPages = data.totalPages
|
17
|
+
@$scope.size = data.size
|
18
|
+
|
19
|
+
@$scope.fetchDocuments = (page = 1) =>
|
20
|
+
return if @$scope.isLoading() # TODO workaround for doule request
|
21
|
+
@$scope.loading = true
|
22
|
+
|
23
|
+
params = dbName: @$scope.dbName, collectionName: @$scope.collectionName, page: page
|
24
|
+
@Document.query(params, _onLoadComplete)
|
25
|
+
|
26
|
+
@$scope.page = 1
|
27
|
+
@$scope.fetchDocuments()
|
28
|
+
|
29
|
+
@$scope.$watch "page", (page) =>
|
30
|
+
@$scope.fetchDocuments(page)
|
31
|
+
|
32
|
+
# TODO create resource for this call
|
33
|
+
@$http.get("/api/databases/#{@$scope.dbName}/collections/#{@$scope.collectionName}/stats.json").success (data) =>
|
34
|
+
@$scope.collectionStats = data
|
35
|
+
|
36
|
+
@$scope.delete = (document) =>
|
37
|
+
@confirmationDialog
|
38
|
+
message: "Are you sure?"
|
39
|
+
onOk: =>
|
40
|
+
resource = new @Document()
|
41
|
+
params = dbName: @$scope.dbName, collectionName: @$scope.collectionName, id: document.id
|
42
|
+
|
43
|
+
resource.$delete params, =>
|
44
|
+
@alerts.info("Document #{document.id} has been deleted.")
|
45
|
+
@$scope.fetchDocuments()
|
46
|
+
|
47
|
+
DocumentsController.$inject = ["$scope", "$routeParams", "$http", "Document", "confirmationDialog", "alerts"]
|
48
|
+
|
49
|
+
module.controller "documents", DocumentsController
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
class MainController
|
4
|
+
constructor: (@$scope, @$http) ->
|
5
|
+
@$http.get("/api/version.json").success (data) =>
|
6
|
+
@$scope.appVersion = data.version
|
7
|
+
|
8
|
+
MainController.$inject = ["$scope", "$http"]
|
9
|
+
|
10
|
+
module.controller "main", MainController
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module = angular.module("mb.controllers")
|
2
|
+
|
3
|
+
class ServerInfo
|
4
|
+
constructor: (@$scope, @$http) ->
|
5
|
+
@$scope.loading = true
|
6
|
+
@$http.get("/api/server_info.json").success(@onLoadComplete)
|
7
|
+
|
8
|
+
onLoadComplete: (data) =>
|
9
|
+
@$scope.serverInfo = data
|
10
|
+
@$scope.loading = false
|
11
|
+
|
12
|
+
ServerInfo.$inject = ["$scope", "$http"]
|
13
|
+
|
14
|
+
module.controller "serverInfo", ServerInfo
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module = angular.module("mb.directives", [])
|
2
|
+
|
3
|
+
# Handles ESC key
|
4
|
+
module.directive "onEsc", ->
|
5
|
+
EscapeCode = 27
|
6
|
+
|
7
|
+
($scope, $element, attrs) ->
|
8
|
+
$element.bind "keyup", (event) ->
|
9
|
+
return unless event.keyCode is EscapeCode
|
10
|
+
$scope.$apply(attrs.onEsc)
|
11
|
+
|
12
|
+
module.directive "deleteButton", ->
|
13
|
+
replace: true
|
14
|
+
restrict: "E"
|
15
|
+
template: """
|
16
|
+
<a class="btn btn-danger">
|
17
|
+
<i class="icon-remove-circle"></i>
|
18
|
+
Delete
|
19
|
+
</a>
|
20
|
+
"""
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module = angular.module("mb.filters", [])
|
2
|
+
|
3
|
+
module.filter "humanSize", -> (bytes) ->
|
4
|
+
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
|
5
|
+
n = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10)
|
6
|
+
scaledSize = (bytes / Math.pow(1024, n)).toFixed(0)
|
7
|
+
|
8
|
+
"#{scaledSize} #{sizes[n]}"
|
9
|
+
|
10
|
+
module.filter "jsonDocument", -> (document) ->
|
11
|
+
str = JSON.stringify(document, undefined, 2)
|
12
|
+
|
13
|
+
# Replace for the ObjectId
|
14
|
+
str = str.replace(/_id": {\s+"\$oid":\s+"(\w+)"\s+}/gm, '_id": ObjectId("$1")')
|
15
|
+
|
16
|
+
syntaxHighlight = (json) ->
|
17
|
+
json = json.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
18
|
+
json.replace /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) ->
|
19
|
+
cls = "number"
|
20
|
+
if /^"/.test(match)
|
21
|
+
if /:$/.test(match)
|
22
|
+
# Remove quotes from a key
|
23
|
+
match = match.replace(/^"/, "").replace(/":$/, ":")
|
24
|
+
|
25
|
+
cls = "key"
|
26
|
+
else
|
27
|
+
cls = "string"
|
28
|
+
else if /true|false/.test(match)
|
29
|
+
cls = "boolean"
|
30
|
+
else
|
31
|
+
cls = "null" if /null/.test(match)
|
32
|
+
|
33
|
+
"<span class=\"#{cls}\">#{match}</span>"
|
34
|
+
|
35
|
+
"<pre>#{syntaxHighlight(str)}</pre>"
|
36
|
+
|
37
|
+
module.filter "collectionsPath", ->
|
38
|
+
(database = {}) ->
|
39
|
+
dbName = database.name || ":dbName"
|
40
|
+
|
41
|
+
"/databases/#{dbName}/collections"
|
42
|
+
|
43
|
+
module.filter "documentsPath", (collectionsPathFilter) ->
|
44
|
+
(collection = {}) ->
|
45
|
+
dbName = collection.dbName || ":dbName"
|
46
|
+
collectionName = collection.name || ":collectionName"
|
47
|
+
|
48
|
+
"#{collectionsPathFilter(name: dbName)}/#{collectionName}/documents"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
dialogs = angular.module("mb.dialogs", [])
|
2
|
+
|
3
|
+
# Simple wrapper for http://bootboxjs.com library
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# handler.confirm "the message", (confirmed) ->
|
7
|
+
# doSomething() if confirmed
|
8
|
+
dialogs.factory "dialogsHandler", -> bootbox
|
9
|
+
|
10
|
+
dialogs.factory "confirmationDialog", [
|
11
|
+
"$log", "dialogsHandler", ($log, handler) ->
|
12
|
+
|
13
|
+
# Options:
|
14
|
+
# message - a message to display inside the dialog
|
15
|
+
# default: "Are you sure?"
|
16
|
+
# onOk - a handler for Ok button
|
17
|
+
# onCancel - a handler for Cancel button
|
18
|
+
(options = {}) ->
|
19
|
+
options.message ||= "Are you sure?"
|
20
|
+
$log.info("Displaying confirmation dialog:", options.message)
|
21
|
+
|
22
|
+
handler.confirm options.message, (confirmed) ->
|
23
|
+
if confirmed
|
24
|
+
$log.info("Confirmation dialog was confirmed")
|
25
|
+
(options.onOk || ->)()
|
26
|
+
else
|
27
|
+
$log.info("Confirmation dialog was disposed")
|
28
|
+
(options.onCancel || ->)()
|
29
|
+
]
|
@@ -0,0 +1,87 @@
|
|
1
|
+
pager = angular.module("mb.pager", [])
|
2
|
+
|
3
|
+
# TODO create just a injectable function
|
4
|
+
pager.factory "pager", ->
|
5
|
+
(options = {}) ->
|
6
|
+
page: options.page || 1
|
7
|
+
totalPages: options.totalPages
|
8
|
+
innerWindow: if options.innerWindow? then options.innerWindow else 4
|
9
|
+
outerWindow: if options.outerWindow? then options.outerWindow else 1
|
10
|
+
|
11
|
+
windowedPageNumbers: ->
|
12
|
+
windowFrom = @page - @innerWindow
|
13
|
+
windowTo = @page + @innerWindow
|
14
|
+
|
15
|
+
# adjust lower or upper limit if other is out of bounds
|
16
|
+
if windowTo > @totalPages
|
17
|
+
windowFrom -= windowTo - @totalPages
|
18
|
+
windowTo = @totalPages
|
19
|
+
|
20
|
+
if windowFrom < 1
|
21
|
+
windowFrom = 1
|
22
|
+
windowTo += windowTo - windowFrom
|
23
|
+
windowTo = @totalPages if windowTo > @totalPages
|
24
|
+
|
25
|
+
middle = [windowFrom..windowTo]
|
26
|
+
|
27
|
+
# left window
|
28
|
+
middleFirst = middle[0]
|
29
|
+
if @outerWindow + 3 < middleFirst # there's a gap
|
30
|
+
left = [1..(@outerWindow + 1)]
|
31
|
+
left.push(null)
|
32
|
+
else # runs into visible pages
|
33
|
+
left = [1...middleFirst]
|
34
|
+
|
35
|
+
# right window
|
36
|
+
middleLast = middle[middle.length - 1]
|
37
|
+
if @totalPages - @outerWindow - 2 > middleLast # again, gap
|
38
|
+
right = [(@totalPages - @outerWindow)..@totalPages]
|
39
|
+
right.unshift(null)
|
40
|
+
else # runs into visible pages
|
41
|
+
if middleLast < @totalPages
|
42
|
+
right = [(middleLast + 1)..@totalPages]
|
43
|
+
else
|
44
|
+
right = []
|
45
|
+
|
46
|
+
left.concat(middle).concat(right)
|
47
|
+
|
48
|
+
# TODO block button when the resource is loading
|
49
|
+
class PagerController
|
50
|
+
constructor: (@$scope, @pager) ->
|
51
|
+
@$scope.$watch "page", => @paginate()
|
52
|
+
@$scope.$watch "totalPages", => @paginate()
|
53
|
+
|
54
|
+
@$scope.setPage = (page) =>
|
55
|
+
return if page < 1 or page > @$scope.totalPages
|
56
|
+
@$scope.page = page
|
57
|
+
|
58
|
+
@$scope.next = =>
|
59
|
+
@$scope.page += 1 if @$scope.hasNext()
|
60
|
+
|
61
|
+
@$scope.hasNext = =>
|
62
|
+
@$scope.page < @$scope.totalPages
|
63
|
+
|
64
|
+
@$scope.prev = =>
|
65
|
+
@$scope.page -= 1 if @$scope.hasPrev()
|
66
|
+
|
67
|
+
@$scope.hasPrev = =>
|
68
|
+
@$scope.page > 1
|
69
|
+
|
70
|
+
@$scope.display = => @$scope.totalPages > 1
|
71
|
+
|
72
|
+
paginate: =>
|
73
|
+
prepare = @pager(page: @$scope.page, totalPages: @$scope.totalPages)
|
74
|
+
@$scope.windowedPageNumbers = prepare.windowedPageNumbers()
|
75
|
+
|
76
|
+
pager.controller "pager", PagerController
|
77
|
+
|
78
|
+
pager.directive "pager", ->
|
79
|
+
templateUrl: "/ng/templates/pager.html"
|
80
|
+
restrict: "E"
|
81
|
+
replace: true
|
82
|
+
|
83
|
+
scope:
|
84
|
+
page: "=page"
|
85
|
+
totalPages: "=totalPages"
|
86
|
+
|
87
|
+
controller: "pager"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
taleFilter = angular.module("mb.tableFilter", [])
|
2
|
+
|
3
|
+
class TableFilterController
|
4
|
+
constructor: ($scope) ->
|
5
|
+
# Clears the filter value
|
6
|
+
$scope.clear = -> $scope.value = ""
|
7
|
+
|
8
|
+
# Reruns true when the filter is empty
|
9
|
+
$scope.isEmpty = -> !$scope.value
|
10
|
+
|
11
|
+
TableFilterController.$inject = ["$scope"]
|
12
|
+
|
13
|
+
taleFilter.controller "tableFilter", TableFilterController
|
14
|
+
|
15
|
+
# Filter for databases and collections
|
16
|
+
taleFilter.directive "tableFilter", ->
|
17
|
+
restrict: "E"
|
18
|
+
transclude: true
|
19
|
+
|
20
|
+
templateUrl: "/ng/templates/table_filter.html"
|
21
|
+
replace: true
|
22
|
+
|
23
|
+
scope:
|
24
|
+
placeholder: "@placeholder"
|
25
|
+
value: "=value"
|
26
|
+
|
27
|
+
controller: "tableFilter"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
angular.module "mb.resources", ["ngResource", "mb.filters"], ($provide) ->
|
2
|
+
|
3
|
+
$provide.factory "Database", [
|
4
|
+
"$resource", (resource) ->
|
5
|
+
|
6
|
+
resource "/api/databases.json", {},
|
7
|
+
query: method: "GET", isArray: true
|
8
|
+
]
|
9
|
+
|
10
|
+
$provide.factory "Collection", [
|
11
|
+
"$resource", "$filter", (resource, $filter) ->
|
12
|
+
|
13
|
+
resource "/api#{$filter("collectionsPath")()}.json", {},
|
14
|
+
query: method: "GET", isArray: true
|
15
|
+
]
|
16
|
+
|
17
|
+
$provide.factory "Document", [
|
18
|
+
"$resource", "$filter", (resource, $filter) ->
|
19
|
+
|
20
|
+
resource "/api#{$filter("documentsPath")()}.json", {},
|
21
|
+
query: method: "GET", isArray: false
|
22
|
+
]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
angular.module "mb.services", [], ($provide) ->
|
2
|
+
|
3
|
+
$provide.factory "alerts", [
|
4
|
+
"$log", "$timeout", ($log, $timeout) ->
|
5
|
+
lastId: 0
|
6
|
+
messages: []
|
7
|
+
|
8
|
+
# Returns a next id for the new message
|
9
|
+
nextId: ->
|
10
|
+
@lastId += 1
|
11
|
+
|
12
|
+
push: (type, text) ->
|
13
|
+
id = @nextId()
|
14
|
+
$log.info("Alert [#{id}, #{type}]", text)
|
15
|
+
|
16
|
+
@messages.push(id: id, type: type, text: text)
|
17
|
+
@delayedDispose(id)
|
18
|
+
|
19
|
+
id
|
20
|
+
|
21
|
+
# Helper methods for various alerts types
|
22
|
+
info: (text) -> @push("info", text)
|
23
|
+
error: (text) -> @push("error", text)
|
24
|
+
|
25
|
+
# Disposes a message with the given id
|
26
|
+
dispose: (id) ->
|
27
|
+
at = @messages.map((message) -> message.id).indexOf(id)
|
28
|
+
@messages.splice(at, 1)
|
29
|
+
|
30
|
+
# Dispose the message after the given time in milliseconds
|
31
|
+
delayedDispose: (id, timeout = 3000) ->
|
32
|
+
disposeTheAlert = =>
|
33
|
+
$log.info("Disposing alert", id, "after", timeout, "milliseconds")
|
34
|
+
@dispose(id)
|
35
|
+
$timeout(disposeTheAlert, timeout)
|
36
|
+
]
|
@@ -1,7 +1,36 @@
|
|
1
|
-
#= require
|
2
|
-
#= require app/table_filter
|
1
|
+
#= require app
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
# App Module
|
4
|
+
requires = [
|
5
|
+
"bootstrap", "ngSanitize",
|
6
|
+
"mb.controllers", "mb.directives", "mb.filters",
|
7
|
+
"mb.dialogs", "mb.pager", "mb.tableFilter"]
|
8
|
+
|
9
|
+
angular.module("mb", requires)
|
10
|
+
.config [
|
11
|
+
"$routeProvider", "$locationProvider", (route, location) ->
|
12
|
+
route
|
13
|
+
# Main page, list of all available databases
|
14
|
+
.when "/",
|
15
|
+
templateUrl: "/ng/templates/databases.html",
|
16
|
+
controller: "databases"
|
17
|
+
|
18
|
+
# List of collections for the given database
|
19
|
+
.when "/databases/:dbName/collections",
|
20
|
+
templateUrl: "/ng/templates/collections.html",
|
21
|
+
controller: "collections"
|
22
|
+
|
23
|
+
# list of documents for the given collection
|
24
|
+
.when "/databases/:dbName/collections/:collectionName/documents",
|
25
|
+
templateUrl: "/ng/templates/documents.html",
|
26
|
+
controller: "documents"
|
27
|
+
|
28
|
+
# Information about the server
|
29
|
+
.when "/server_info",
|
30
|
+
templateUrl: "/ng/templates/server_info.html",
|
31
|
+
controller: "serverInfo"
|
32
|
+
|
33
|
+
.otherwise(redirectTo: "/")
|
34
|
+
|
35
|
+
location.html5Mode(true)
|
36
|
+
]
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
#= require_tree ./templates
|