mongo_browser 0.1.3 → 0.2.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|