├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE ├── README.md ├── app ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── images │ ├── icon-128.png │ └── icon-16.png ├── manifest.json ├── nacl_src │ ├── Makefile │ ├── communication_exception.cc │ ├── communication_exception.h │ ├── ssh2_port_forwarding.cc │ ├── ssh2_port_forwarding.h │ ├── ssh2_port_forwarding_event_listener.h │ ├── ssh2_port_forwarding_thread.cc │ └── ssh2_port_forwarding_thread.h ├── scripts │ ├── background.js │ ├── chromereload.js │ ├── lib │ │ ├── bootstrap.min.js │ │ ├── encoding-indexes.js │ │ ├── encoding.js │ │ ├── forge.min.js │ │ ├── jcanvas.min.js │ │ ├── jquery.jqplot.min.js │ │ ├── mysql_js_driver_2.1.0.min.js │ │ ├── ng-grid-2.0.7.min.js │ │ └── sha1.js │ └── window │ │ ├── controllers │ │ ├── add_column_dialog_controller.js │ │ ├── add_index_dialog_controller.js │ │ ├── add_relation_dialog_controller.js │ │ ├── change_window_panel_controller.js │ │ ├── configuration_dialog_controller.js │ │ ├── confirm_dialog_controller.js │ │ ├── create_database_dialog_controller.js │ │ ├── create_routine_dialog_controller.js │ │ ├── create_table_dialog_controller.js │ │ ├── database_object_list_controller.js │ │ ├── database_panel_controller.js │ │ ├── edit_column_dialog_controller.js │ │ ├── er_diagram_panel_controller.js │ │ ├── error_dialog_controller.js │ │ ├── favorite_list_controller.js │ │ ├── find_rows_with_the_value_dialog_controller.js │ │ ├── find_same_rows_dialog_controller.js │ │ ├── information_panel_controller.js │ │ ├── insert_row_dialog_controller.js │ │ ├── login_form_controller.js │ │ ├── main_footer_controller.js │ │ ├── navbar_controller.js │ │ ├── procedures_functions_panel_controller.js │ │ ├── progress_bar_controller.js │ │ ├── query_panel_controller.js │ │ ├── relation_panel_controller.js │ │ ├── rows_panel_controller.js │ │ ├── status_graph_panel_controller.js │ │ ├── structure_panel_controller.js │ │ ├── update_row_dialog_controller.js │ │ └── window_title_panel_controller.js │ │ ├── directives │ │ ├── add_column_dialog.js │ │ ├── add_index_dialog.js │ │ ├── add_relation_dialog.js │ │ ├── change_window_panel.js │ │ ├── configuration_dialog.js │ │ ├── confirm_dialog.js │ │ ├── create_database_dialog.js │ │ ├── create_routine_dialog.js │ │ ├── create_table_dialog.js │ │ ├── database_object_list_panel.js │ │ ├── database_panel.js │ │ ├── edit_column_dialog.js │ │ ├── er_diagram.js │ │ ├── er_diagram_panel.js │ │ ├── error_dialog.js │ │ ├── favorite_list_panel.js │ │ ├── file_drop.js │ │ ├── find_rows_with_the_value_dialog.js │ │ ├── find_same_rows_dialog.js │ │ ├── footer_button.js │ │ ├── information_panel.js │ │ ├── insert_row_dialog.js │ │ ├── login_form.js │ │ ├── main_footer.js │ │ ├── navbar_menu_item.js │ │ ├── navbar_panel.js │ │ ├── procedures_functions_panel.js │ │ ├── progress_panel.js │ │ ├── query_panel.js │ │ ├── relation_panel.js │ │ ├── resize_when.js │ │ ├── rows_panel.js │ │ ├── sglclick.js │ │ ├── status_graph.js │ │ ├── status_graph_panel.js │ │ ├── structure_panel.js │ │ ├── update_row_dialog.js │ │ └── window_title_panel.js │ │ ├── main.js │ │ ├── services │ │ ├── any_query_execute_service.js │ │ ├── clipboard_service.js │ │ ├── column_type_service.js │ │ ├── configuration_service.js │ │ ├── connection_information_service.js │ │ ├── export_all_databases_service.js │ │ ├── favorite_service.js │ │ ├── identity_keep_service.js │ │ ├── mode_service.js │ │ ├── mysql_client_service.js │ │ ├── mysql_query_service.js │ │ ├── ping_service.js │ │ ├── query_history_service.js │ │ ├── query_selection_service.js │ │ ├── relation_selection_service.js │ │ ├── relation_service.js │ │ ├── routine_selection_service.js │ │ ├── rows_paging_service.js │ │ ├── rows_selection_service.js │ │ ├── sql_expression_service.js │ │ ├── ssh2_known_host_service.js │ │ ├── ssh2_port_forwarding_service.js │ │ ├── target_object_service.js │ │ └── type_service.js │ │ └── utils │ │ └── constants.js ├── styles │ ├── angular-csp.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── font-awesome.min.css │ ├── jquery.jqplot.min.css │ ├── ng-grid.min.css │ └── window.css ├── templates │ ├── add_column_dialog.html │ ├── add_index_dialog.html │ ├── add_relation_dialog.html │ ├── change_window_panel.html │ ├── configuration_dialog.html │ ├── confirm_dialog.html │ ├── create_database_dialog.html │ ├── create_routine_dialog.html │ ├── create_table_dialog.html │ ├── database_object_list_panel.html │ ├── database_panel.html │ ├── edit_column_dialog.html │ ├── er_diagram.html │ ├── er_diagram_panel.html │ ├── error_dialog.html │ ├── favorite_list_panel.html │ ├── find_rows_with_the_value_dialog.html │ ├── find_same_rows_dialog.html │ ├── footer_button.html │ ├── information_panel.html │ ├── insert_row_dialog.html │ ├── login_form.html │ ├── main_footer.html │ ├── navbar_menu_item.html │ ├── navbar_panel.html │ ├── procs_funcs_panel.html │ ├── progress_panel.html │ ├── query_panel.html │ ├── relation_panel.html │ ├── rows_panel.html │ ├── status_graph.html │ ├── status_graph_panel.html │ ├── structure_panel.html │ ├── update_row_dialog.html │ └── window_title_panel.html └── window.html ├── bower.json ├── package.json └── test ├── .bowerrc ├── app └── manifest.json ├── bower.json ├── bower_components ├── chai │ ├── .bower.json │ ├── History.md │ ├── README.md │ ├── ReleaseNotes.md │ ├── bower.json │ ├── chai.js │ ├── component.json │ ├── karma.conf.js │ ├── karma.sauce.js │ ├── package.json │ └── sauce.browsers.js └── mocha │ ├── .bower.json │ ├── History.md │ ├── LICENSE │ ├── Readme.md │ ├── bower.json │ ├── media │ └── logo.svg │ ├── mocha.css │ └── mocha.js ├── index.html └── spec └── test.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | .tmp 4 | dist 5 | .sass-cache 6 | app/bower_components 7 | package 8 | bower_components 9 | app/scripts/lib/angular 10 | app/scripts/lib/jquery 11 | app/scripts/lib/ace-builds 12 | app/scripts/lib/angular-ui-ace 13 | app/newlib 14 | app/clang-newlib 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "regexp": true, 15 | "undef": true, 16 | "unused": false, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals" : { 21 | "chrome": true, 22 | "MySQL": true, 23 | "angular": true, 24 | "$": true, 25 | "chromeMyAdmin": true, 26 | "jQuery": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Code Climate](https://codeclimate.com/github/yoichiro/chrome_mysql_admin/badges/gpa.svg)](https://codeclimate.com/github/yoichiro/chrome_mysql_admin) 2 | 3 | ChromeMyAdmin 4 | ======================= 5 | 6 | What's this? 7 | ------------ 8 | 9 | This project provides the source code set of the Chrome app "ChromeMyAdmin" to manage and operate MySQL server. 10 | 11 | How to install this ChromeMyAdmin app 12 | ------------------------------------- 13 | 14 | You can install this application from Chrome Web store: 15 | 16 | [ChromeMyAdmin - Chrome Web store](https://chrome.google.com/webstore/detail/chromemyadmin/ndgnpnpakfcdjmpgmcaknimfgcldechn) 17 | 18 | How to build 19 | ------------ 20 | 21 | ``` 22 | $ git clone https://github.com/yoichiro/chrome_mysql_admin.git 23 | $ cd chrome_mysql_admin 24 | $ npm install 25 | $ grunt 26 | ``` 27 | 28 | Dependent libraries 29 | ------------------- 30 | 31 | * [encoding.js](http://code.google.com/p/stringencoding/) 32 | * [crypto-js](https://code.google.com/p/crypto-js/) 33 | * [Forge](https://github.com/digitalbazaar/forge) 34 | * [mysql_js_driver](https://github.com/yoichiro/mysql_js_driver) 35 | -------------------------------------------------------------------------------- /app/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/images/icon-128.png -------------------------------------------------------------------------------- /app/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoichiro/chrome_mysql_admin/ddbc0b4e17dbe21e835795d178305a984673dbc8/app/images/icon-16.png -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Chrome MySQL Admin", 4 | "short_name": "MyAdmin", 5 | "description": "This application provides you 'MySQL GUI Admin console' windows.", 6 | "version": "4.10.0", 7 | "author": "Yoichiro Tanaka", 8 | "app": { 9 | "background": { 10 | "scripts": [ 11 | "scripts/background.js" 12 | ] 13 | } 14 | }, 15 | "permissions": [ 16 | "storage", 17 | "alwaysOnTopWindows", 18 | { 19 | "socket": [ 20 | "resolve-host", 21 | "tcp-connect:*:*", 22 | "tcp-listen::*" 23 | ] 24 | }, 25 | { 26 | "fileSystem": ["write"] 27 | } 28 | ], 29 | "offline_enabled": true, 30 | "icons": { 31 | "16": "images/icon-16.png", 32 | "128": "images/icon-128.png" 33 | }, 34 | "sockets": { 35 | "tcp": { 36 | "connect": "*" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/nacl_src/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | # GNU Makefile based on shared rules provided by the Native Client SDK. 6 | # See README.Makefiles for more details. 7 | 8 | VALID_TOOLCHAINS := clang-newlib 9 | 10 | NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../..) 11 | OUTBASE = .. 12 | 13 | include $(NACL_SDK_ROOT)/tools/common.mk 14 | 15 | TARGET = ssh2_port_forwarding 16 | LIBS = jsoncpp ssh2 crypto nacl_io ppapi_cpp ppapi pthread 17 | 18 | # CFLAGS = -Wall -std=c++0x 19 | CFLAGS = -Wall -std=gnu++11 20 | SOURCES = communication_exception.cc ssh2_port_forwarding_thread.cc ssh2_port_forwarding.cc 21 | 22 | # Build rules generated by macros from common.mk: 23 | 24 | $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) 25 | 26 | # The PNaCl workflow uses both an unstripped and finalized/stripped binary. 27 | # On NaCl, only produce a stripped binary for Release configs (not Debug). 28 | ifneq (,$(or $(findstring pnacl,$(TOOLCHAIN)),$(findstring Release,$(CONFIG)))) 29 | $(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) 30 | $(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) 31 | else 32 | $(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) 33 | endif 34 | 35 | $(eval $(call NMF_RULE,$(TARGET),)) 36 | -------------------------------------------------------------------------------- /app/nacl_src/communication_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "communication_exception.h" 4 | 5 | CommunicationException::CommunicationException(const std::string message, 6 | const int result_code, 7 | const char *file_name, 8 | const char *function_name, 9 | const int line_number) 10 | :message_(message), 11 | result_code_(result_code), 12 | file_name_(file_name), 13 | function_name_(function_name), 14 | line_number_(line_number) 15 | { 16 | } 17 | 18 | CommunicationException::~CommunicationException() throw() 19 | { 20 | } 21 | 22 | const char* CommunicationException::what() const throw() 23 | { 24 | return message_.c_str(); 25 | } 26 | 27 | const int CommunicationException::getResultCode() 28 | { 29 | return result_code_; 30 | } 31 | 32 | const char* CommunicationException::getFileName() 33 | { 34 | return file_name_; 35 | } 36 | 37 | const char* CommunicationException::getFunctionName() 38 | { 39 | return function_name_; 40 | } 41 | 42 | const int CommunicationException::getLineNumber() 43 | { 44 | return line_number_; 45 | } 46 | 47 | const std::string CommunicationException::toString() 48 | { 49 | std::ostringstream oss; 50 | oss << message_.c_str() << " "; 51 | oss << result_code_; 52 | std::string result = oss.str(); 53 | return result; 54 | } 55 | -------------------------------------------------------------------------------- /app/nacl_src/communication_exception.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMUNICATION_EXCEPTION_H 2 | #define COMMUNICATION_EXCEPTION_H 3 | 4 | #include 5 | #include 6 | 7 | #define THROW_COMMUNICATION_EXCEPTION(message, result_code) \ 8 | std::string msg = message; \ 9 | throw CommunicationException(msg, result_code, __FILE__, __func__, __LINE__) 10 | 11 | class CommunicationException : public std::exception { 12 | 13 | public: 14 | 15 | CommunicationException(const std::string message, 16 | const int result_code, 17 | const char *file_name, 18 | const char *function_name, 19 | const int line_number); 20 | virtual ~CommunicationException() throw(); 21 | const int getResultCode(); 22 | const char* getFileName(); 23 | const char* getFunctionName(); 24 | const int getLineNumber(); 25 | virtual const char* what() const throw(); 26 | const std::string toString(); 27 | 28 | private: 29 | 30 | const std::string message_; 31 | const int result_code_; 32 | const char *file_name_; 33 | const char *function_name_; 34 | const int line_number_; 35 | 36 | }; 37 | 38 | #endif // COMMUNICATION_EXCEPTION_H 39 | -------------------------------------------------------------------------------- /app/nacl_src/ssh2_port_forwarding.h: -------------------------------------------------------------------------------- 1 | #ifndef SSH2_PORT_FORWARDING 2 | #define SSH2_PORT_FORWARDING 3 | 4 | #include "ppapi/utility/completion_callback_factory.h" 5 | #include "ppapi/cpp/instance.h" 6 | #include "ppapi/cpp/module.h" 7 | #include "ppapi/cpp/var.h" 8 | #include "json/json.h" 9 | 10 | #include "ssh2_port_forwarding_thread.h" 11 | #include "ssh2_port_forwarding_event_listener.h" 12 | 13 | class Ssh2PortForwardingInstance : public pp::Instance, public Ssh2PortForwardingEventListener 14 | { 15 | 16 | public: 17 | 18 | explicit Ssh2PortForwardingInstance(PP_Instance instance); 19 | virtual ~Ssh2PortForwardingInstance(); 20 | 21 | virtual void HandleMessage(const pp::Var &var_message); 22 | 23 | virtual void OnHandshakeFinished(const std::string &fingerprint, 24 | const std::string &hostkey_method); 25 | virtual void OnWaitingConnection(const int port); 26 | virtual void OnForwardingStarted(); 27 | virtual void OnSentBytes(const int length); 28 | virtual void OnReadBytes(const int length); 29 | virtual void OnShutdown(); 30 | virtual void OnErrorOccurred(const std::string &message); 31 | 32 | private: 33 | 34 | pp::Core *pp_core_; 35 | pp::CompletionCallbackFactory factory_; 36 | 37 | Ssh2PortForwardingThread *ssh2_port_forwarding_thread_; 38 | 39 | int GetIntegerValueFromJsonArgs(const Json::Value &args, const int index); 40 | void Log(const char *message); 41 | void Log(const int code); 42 | void SendResponse(const std::string &message, const std::vector &values); 43 | void SendResponseImpl(int32_t result, const std::string &message, const std::vector &values); 44 | 45 | }; 46 | 47 | class Ssh2PortForwardingModule : public pp::Module 48 | { 49 | 50 | public: 51 | 52 | Ssh2PortForwardingModule(); 53 | virtual ~Ssh2PortForwardingModule(); 54 | virtual pp::Instance* CreateInstance(PP_Instance instance); 55 | 56 | }; 57 | 58 | namespace pp { 59 | 60 | Module* CreateModule() { 61 | return new Ssh2PortForwardingModule(); 62 | } 63 | 64 | } 65 | 66 | #endif // SSH2_PORT_FORWARDING 67 | -------------------------------------------------------------------------------- /app/nacl_src/ssh2_port_forwarding_event_listener.h: -------------------------------------------------------------------------------- 1 | #ifndef SSH2_PORT_FORWARDING_EVENT_LISTENER_H 2 | #define SSH2_PORT_FORWARDING_EVENT_LISTENER_H 3 | 4 | #include 5 | 6 | class Ssh2PortForwardingEventListener { 7 | 8 | public: 9 | 10 | virtual ~Ssh2PortForwardingEventListener() {}; 11 | 12 | virtual void OnHandshakeFinished(const std::string &fingerprint, 13 | const std::string &hostkey_method) = 0; 14 | virtual void OnWaitingConnection(const int port) = 0; 15 | virtual void OnForwardingStarted() = 0; 16 | virtual void OnSentBytes(const int length) = 0; 17 | virtual void OnReadBytes(const int length) = 0; 18 | virtual void OnShutdown() = 0; 19 | virtual void OnErrorOccurred(const std::string &message) = 0; 20 | 21 | }; 22 | 23 | #endif // SSH2_PORT_FORWARDING_EVENT_LISTENER_H 24 | -------------------------------------------------------------------------------- /app/nacl_src/ssh2_port_forwarding_thread.h: -------------------------------------------------------------------------------- 1 | #ifndef SSH2_PORT_FORWARDING_THREAD_H 2 | #define SSH2_PORT_FORWARDING_THREAD_H 3 | 4 | #include "ppapi/cpp/completion_callback.h" 5 | #include "ppapi/cpp/instance.h" 6 | #include "ppapi/cpp/module.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "communication_exception.h" 15 | #include "ssh2_port_forwarding_event_listener.h" 16 | 17 | class Ssh2PortForwardingThread 18 | { 19 | 20 | public: 21 | 22 | explicit Ssh2PortForwardingThread(pp::Instance *instance, 23 | Ssh2PortForwardingEventListener *listener); 24 | virtual ~Ssh2PortForwardingThread(); 25 | 26 | void ConnectAndHandshake(const std::string server_hostname, const int server_port); 27 | 28 | void AuthenticateAndForward(const std::string auth_type, 29 | const std::string username, 30 | const std::string password, 31 | const std::string remote_dest_hostname, 32 | const int remote_dest_port, 33 | const std::string private_key); 34 | 35 | std::string GetPassword(); 36 | 37 | private: 38 | 39 | pthread_t thread_; 40 | pp::Instance *pp_instance_; 41 | Ssh2PortForwardingEventListener *listener_; 42 | 43 | std::string server_hostname_; 44 | int server_port_; 45 | 46 | std::string auth_type_; 47 | std::string username_; 48 | std::string password_; 49 | 50 | std::string remote_dest_hostname_; 51 | int remote_dest_port_; 52 | std::string private_key_; 53 | 54 | int server_sock_; 55 | LIBSSH2_SESSION *session_; 56 | 57 | static void* StartConnectAndHandshakeThread(void *arg); 58 | 59 | void ConnectAndHandshakeImpl(); 60 | 61 | static void* StartAuthenticateAndForwardThread(void *arg); 62 | 63 | void AuthenticateAndForwardImpl(); 64 | 65 | void InitializeLibssh2() throw(CommunicationException); 66 | 67 | int ConnectToSshServer(const std::string &hostname, 68 | const int port) throw(CommunicationException); 69 | 70 | LIBSSH2_SESSION* InitializeSession() throw(CommunicationException); 71 | 72 | void HandshakeSession(LIBSSH2_SESSION *session, 73 | int sock) throw(CommunicationException); 74 | 75 | std::string GetHostKeyHash(LIBSSH2_SESSION *session); 76 | 77 | std::string GetHostKeyMethod(LIBSSH2_SESSION *session); 78 | 79 | void AuthenticateUser() throw(CommunicationException); 80 | 81 | void AuthenticateByPassword(LIBSSH2_SESSION *session, 82 | const std::string &username, 83 | const std::string &password) throw(CommunicationException); 84 | 85 | void AuthenticateByKeyboardInteractive(LIBSSH2_SESSION *session, 86 | const std::string &username, 87 | const std::string &password) throw(CommunicationException); 88 | 89 | static void KeyboardCallback(const char *name, 90 | int name_len, 91 | const char *instruction, 92 | int instruction_len, 93 | int num_prompts, 94 | const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, 95 | LIBSSH2_USERAUTH_KBDINT_RESPONSE *response, 96 | void **abstract); 97 | 98 | void AuthenticateByPublicKey(LIBSSH2_SESSION *session, 99 | const std::string &username, 100 | const std::string &password, 101 | const std::string &private_key) 102 | throw(CommunicationException); 103 | 104 | int SearchUnusedPort() throw(CommunicationException); 105 | 106 | std::tuple 107 | StartLocalServer(LIBSSH2_SESSION *session, 108 | const std::string &remote_dest_hostname, 109 | const int remote_dest_port) throw(CommunicationException); 110 | 111 | void SetNonBlocking(LIBSSH2_SESSION *session); 112 | 113 | void ForwardPacket(const int forward_sock, 114 | LIBSSH2_CHANNEL *channel) throw(CommunicationException); 115 | 116 | void CloseSocket(const int sock, const int listen_sock, const int forward_sock); 117 | 118 | void CloseSession(LIBSSH2_SESSION *session); 119 | 120 | void CloseChannel(LIBSSH2_CHANNEL *channel); 121 | 122 | void Log(const char *message); 123 | 124 | void Log(const int code); 125 | 126 | void Log(const char *message, const int code); 127 | 128 | void Log(const std::string& message); 129 | 130 | }; 131 | 132 | #endif // SSH2_PORT_FORWARDING_THREAD_H 133 | -------------------------------------------------------------------------------- /app/scripts/background.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yoichiro Tanaka. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | "use strict"; 18 | 19 | var windows = []; 20 | 21 | function createWindow() { 22 | chrome.storage.local.get("windowSize", function(items) { 23 | var params = {}; 24 | if (items.windowSize) { 25 | var windowSize = items.windowSize; 26 | var state = "normal"; 27 | if (windowSize.isFullscreen) { 28 | state = "fullscreen"; 29 | } else if (windowSize.isMaximized) { 30 | state = "maximized"; 31 | } 32 | params.bounds = windowSize.bounds; 33 | params.state = state; 34 | } else { 35 | params.bounds = { 36 | width: 800, 37 | height: 600 38 | }; 39 | params.state = "normal"; 40 | } 41 | params.minWidth = 800; 42 | params.minHeight = 600; 43 | params.resizable = true; 44 | params.frame = "chrome"; 45 | params.id = String((new Date()).getTime()); 46 | chrome.app.window.create("window.html", params, function(createdWindow) { 47 | createdWindow.onClosed.addListener((function(closedWindow) { 48 | return function() { 49 | for (var i = 0; i < windows.length; i++) { 50 | var window = windows[i]; 51 | if (window.id === closedWindow.id) { 52 | windows.splice(i, 1); 53 | break; 54 | } 55 | } 56 | }; 57 | })(createdWindow)); 58 | windows.push(createdWindow); 59 | var storePosition = ((function(window) { 60 | return function() { 61 | var windowSize = { 62 | bounds: { 63 | top: window.innerBounds.top, 64 | left: window.innerBounds.left, 65 | width: window.innerBounds.width, 66 | height: window.innerBounds.height 67 | }, 68 | isFullscreen: window.isFullscreen(), 69 | isMaximized: window.isMaximized() 70 | }; 71 | chrome.storage.local.set({windowSize: windowSize}, function() { 72 | }); 73 | }; 74 | })(createdWindow)); 75 | createdWindow.onBoundsChanged.addListener(storePosition); 76 | createdWindow.onFullscreened.addListener(storePosition); 77 | createdWindow.onMaximized.addListener(storePosition); 78 | createdWindow.onMinimized.addListener(storePosition); 79 | createdWindow.onRestored.addListener(storePosition); 80 | }); 81 | }); 82 | } 83 | 84 | function getWindows() { 85 | var result = []; 86 | for (var i = 0; i < windows.length; i++) { 87 | result.push({ 88 | id: windows[i].id, 89 | title: windows[i].contentWindow.document.title 90 | }); 91 | } 92 | return result; 93 | } 94 | 95 | function focusWindow(id) { 96 | for (var i = 0; i < windows.length; i++) { 97 | if (windows[i].id === id) { 98 | windows[i].focus(); 99 | break; 100 | } 101 | } 102 | } 103 | 104 | function migrateStorage(callback) { 105 | chrome.storage.sync.get([ 106 | 'windowSize', 107 | 'erDiagramDimensions', 108 | 'configurations', 109 | 'favorites', 110 | 'identities', 111 | 'queries', 112 | 'knownHosts' 113 | ], function(items) { 114 | if (Object.keys(items).length !== 0) { 115 | chrome.storage.local.set(items, function() { 116 | chrome.storage.sync.clear(function() { 117 | console.log('Configuration migrated.'); 118 | callback(); 119 | }); 120 | }); 121 | } else { 122 | callback(); 123 | } 124 | }); 125 | } 126 | 127 | chrome.app.runtime.onLaunched.addListener(function() { 128 | migrateStorage(function() { 129 | createWindow(); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /app/scripts/chromereload.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Reload client for Chrome Apps & Extensions. 4 | // The relaod client has a compatibility with livereload. 5 | // WARNING: only supports reload command. 6 | 7 | var LIVERELOAD_HOST = 'localhost:'; 8 | var LIVERELOAD_PORT = 35729; 9 | var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload'); 10 | 11 | connection.onerror = function (error) { 12 | console.log('reload connection got error' + JSON.stringify(error)); 13 | }; 14 | 15 | connection.onmessage = function (e) { 16 | if (e.data) { 17 | var data = JSON.parse(e.data); 18 | if (data && data.command === 'reload') { 19 | chrome.runtime.reload(); 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /app/scripts/lib/sha1.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | var CryptoJS=CryptoJS||function(e,m){var p={},j=p.lib={},l=function(){},f=j.Base={extend:function(a){l.prototype=this;var c=new l;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, 8 | n=j.WordArray=f.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=m?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var c=this.words,q=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var b=0;b>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((d+b)%4);else if(65535>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 9 | 32-8*(c%4);a.length=e.ceil(c/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b>>2]>>>24-8*(d%4)&255;b.push((f>>>4).toString(16));b.push((f&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>3]|=parseInt(a.substr(d, 10 | 2),16)<<24-4*(d%8);return new n.init(b,c/2)}},g=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],d=0;d>>2]>>>24-8*(d%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],d=0;d>>2]|=(a.charCodeAt(d)&255)<<24-8*(d%4);return new n.init(b,c)}},r=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(g.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return g.parse(unescape(encodeURIComponent(a)))}}, 11 | k=j.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new n.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=r.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,d=c.sigBytes,f=this.blockSize,h=d/(4*f),h=a?e.ceil(h):e.max((h|0)-this._minBufferSize,0);a=h*f;d=e.min(4*a,d);if(a){for(var g=0;ga;a++){if(16>a)l[a]=f[n+a]|0;else{var c=l[a-3]^l[a-8]^l[a-14]^l[a-16];l[a]=c<<1|c>>>31}c=(h<<5|h>>>27)+j+l[a];c=20>a?c+((g&e|~g&k)+1518500249):40>a?c+((g^e^k)+1859775393):60>a?c+((g&e|g&k|e&k)-1894007588):c+((g^e^ 15 | k)-899497514);j=k;k=e;e=g<<30|g>>>2;g=h;h=c}b[0]=b[0]+h|0;b[1]=b[1]+g|0;b[2]=b[2]+e|0;b[3]=b[3]+k|0;b[4]=b[4]+j|0},_doFinalize:function(){var f=this._data,e=f.words,b=8*this._nDataBytes,h=8*f.sigBytes;e[h>>>5]|=128<<24-h%32;e[(h+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(h+64>>>9<<4)+15]=b;f.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=j.clone.call(this);e._hash=this._hash.clone();return e}});e.SHA1=j._createHelper(m);e.HmacSHA1=j._createHmacHelper(m)})(); 16 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/add_index_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("AddIndexDialogController", function( 2 | $scope, 3 | Events, 4 | mySQLClientService, 5 | targetObjectService 6 | ) { 7 | "use strict"; 8 | 9 | var onShowDialog = function(data) { 10 | resetErrorMessage(); 11 | $scope.selectedTable = data.table; 12 | $scope.keyType = "PRIMARY"; 13 | $scope.keyName = "PRIMARY"; 14 | $scope.columns = data.columns; 15 | if ($scope.columns.length > 0) { 16 | $scope.column = $scope.columns[0]; 17 | } 18 | $scope.selectedColumns = []; 19 | $("#addIndexDialog").modal("show"); 20 | }; 21 | 22 | var assignEventHandlers = function() { 23 | $scope.$on(Events.SHOW_ADD_INDEX_DIALOG, function(event, data) { 24 | onShowDialog(data); 25 | }); 26 | }; 27 | 28 | var resetErrorMessage = function() { 29 | $scope.errorMessage = ""; 30 | }; 31 | 32 | $scope.isErrorMessageVisible = function() { 33 | return $scope.errorMessage.length > 0; 34 | }; 35 | 36 | var setupItems = function() { 37 | $scope.keyTypes = ["PRIMARY", "INDEX", "UNIQUE"]; 38 | $scope.keyType = "PRIMARY"; 39 | }; 40 | 41 | $scope.initialize = function() { 42 | resetErrorMessage(); 43 | assignEventHandlers(); 44 | setupItems(); 45 | }; 46 | 47 | $scope.onChangeKeyType = function() { 48 | if ($scope.keyType === "PRIMARY") { 49 | $scope.keyName = "PRIMARY"; 50 | } else if ($scope.keyName === "PRIMARY") { 51 | $scope.keyName = ""; 52 | } 53 | }; 54 | 55 | $scope.isKeyNameDisabled = function() { 56 | return $scope.keyType === "PRIMARY"; 57 | }; 58 | 59 | $scope.isAddDisabled = function() { 60 | return !$scope.selectedColumns || $scope.selectedColumns.length === 0; 61 | }; 62 | 63 | $scope.addColumn = function() { 64 | $scope.selectedColumns.push($scope.column); 65 | $scope.columns = $scope.columns.filter(function(e) { 66 | return $scope.column !== e; 67 | }); 68 | if ($scope.columns.length > 0) { 69 | $scope.column = $scope.columns[0]; 70 | } 71 | }; 72 | 73 | $scope.deleteSelectedColumn = function(selectedColumn) { 74 | $scope.columns.push(selectedColumn); 75 | $scope.selectedColumns = $scope.selectedColumns.filter(function(e) { 76 | return selectedColumn !== e; 77 | }); 78 | if ($scope.columns.length === 1) { 79 | $scope.column = $scope.columns[0]; 80 | } 81 | }; 82 | 83 | $scope.upColumn = function(index) { 84 | var previous = $scope.selectedColumns[index - 1]; 85 | $scope.selectedColumns[index - 1] = $scope.selectedColumns[index]; 86 | $scope.selectedColumns[index] = previous; 87 | }; 88 | 89 | $scope.downColumn = function(index) { 90 | var next = $scope.selectedColumns[index + 1]; 91 | $scope.selectedColumns[index + 1] = $scope.selectedColumns[index]; 92 | $scope.selectedColumns[index] = next; 93 | }; 94 | 95 | var createColumnListString = function(list) { 96 | var tmp = []; 97 | angular.forEach($scope.selectedColumns, function(e) { 98 | this.push("`" + e + "`"); 99 | }, tmp); 100 | return tmp.join(","); 101 | }; 102 | 103 | $scope.addIndex = function() { 104 | resetErrorMessage(); 105 | var sql = ""; 106 | var columnListString = "(" + createColumnListString($scope.selectedColumns) + ")"; 107 | if ($scope.keyType === "PRIMARY") { 108 | sql += "ALTER TABLE `" + $scope.selectedTable.name + "` ADD PRIMARY KEY "; 109 | sql += columnListString; 110 | } else if ($scope.keyType === "INDEX") { 111 | sql += "ALTER TABLE `" + $scope.selectedTable.name + "` ADD INDEX "; 112 | if ($scope.keyName) { 113 | sql += "`" + $scope.keyName + "` "; 114 | } 115 | sql += columnListString; 116 | } else if ($scope.keyType === "UNIQUE") { 117 | sql += "ALTER TABLE `" + $scope.selectedTable.name + "` ADD UNIQUE INDEX "; 118 | if ($scope.keyName) { 119 | sql += "`" + $scope.keyName + "` "; 120 | } 121 | sql += columnListString; 122 | } 123 | mySQLClientService.query(sql).then(function(result) { 124 | if (result.hasResultsetRows) { 125 | $scope.fatalErrorOccurred("Adding index failed."); 126 | } else { 127 | $("#addIndexDialog").modal("hide"); 128 | targetObjectService.reSelectTable(); 129 | } 130 | }, function(reason) { 131 | var errorMessage = "[Error code:" + reason.errorCode; 132 | errorMessage += " SQL state:" + reason.sqlState; 133 | errorMessage += "] "; 134 | errorMessage += reason.errorMessage; 135 | $scope.errorMessage = errorMessage; 136 | }); 137 | }; 138 | 139 | }); 140 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/change_window_panel_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("ChangeWindowPanelController", function( 2 | $scope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var assignWindowResizeEventHandler = function() { 8 | $(window).resize(function(evt) { 9 | adjustChangeWindowPanelHeight(); 10 | }); 11 | }; 12 | 13 | var adjustChangeWindowPanelHeight = function() { 14 | $("#changeWindowPanel").height($(window).height()); 15 | }; 16 | 17 | var setVisibleChangeWindowPanel = function(newVisibleChangeWindowPanel) { 18 | $scope.visibleChangeWindowPanel = newVisibleChangeWindowPanel; 19 | }; 20 | 21 | var assignEventHandlers = function() { 22 | $scope.$on(Events.SHOW_CHANGE_WINDOW_PANEL, function(event) { 23 | loadWindows(); 24 | }); 25 | $scope.$on(Events.HIDE_CHANGE_WINDOW_PANEL, function(event) { 26 | setVisibleChangeWindowPanel(false); 27 | }); 28 | }; 29 | 30 | var loadWindows = function() { 31 | chrome.runtime.getBackgroundPage(function(bg) { 32 | var windows = bg.getWindows(); 33 | var currentWindow = chrome.app.window.current(); 34 | var otherWindows = []; 35 | angular.forEach(windows, function(window) { 36 | if (currentWindow.id !== window.id) { 37 | otherWindows.push(window); 38 | } 39 | }, otherWindows); 40 | if (otherWindows.length > 0) { 41 | $scope.safeApply(function() { 42 | setVisibleChangeWindowPanel(true); 43 | $scope.windows = otherWindows; 44 | }); 45 | } 46 | }); 47 | }; 48 | 49 | $scope.initialize = function() { 50 | assignEventHandlers(); 51 | $scope.visibleChangeWindowPanel = false; 52 | assignWindowResizeEventHandler(); 53 | adjustChangeWindowPanelHeight(); 54 | }; 55 | 56 | $scope.isVisibleChangeWindowPanel = function() { 57 | return $scope.visibleChangeWindowPanel; 58 | }; 59 | 60 | $scope.hideChangeWindowPanel = function() { 61 | setVisibleChangeWindowPanel(false); 62 | }; 63 | 64 | $scope.changeWindow = function(id) { 65 | setVisibleChangeWindowPanel(false); 66 | chrome.runtime.getBackgroundPage(function(bg) { 67 | bg.focusWindow(id); 68 | }); 69 | }; 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/configuration_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("ConfigurationDialogController", function( 2 | $scope, 3 | mySQLClientService, 4 | Events, 5 | configurationService, 6 | QueryEditorWrapMode, 7 | anyQueryExecuteService, 8 | ssh2KnownHostService 9 | ) { 10 | "use strict"; 11 | 12 | var doOpen = function(activeTab) { 13 | configurationService.getDatabaseInfoAutoUpdateSpan().then(function(span) { 14 | $scope.databaseInfoAutoUpdateSpan = span / 1000; 15 | return configurationService.getRowCountPerPageInRowsPanel(); 16 | }).then(function(rowCount) { 17 | $scope.rowCountPerPageInRowsPanel = rowCount; 18 | return configurationService.getQueryEditorWrapMode(); 19 | }).then(function(mode) { 20 | $scope.queryEditorWrapMode = mode ? QueryEditorWrapMode.WRAP : QueryEditorWrapMode.NOT_WRAP; 21 | return configurationService.getStatusGraphAutoUpdateSpan(); 22 | }).then(function(span) { 23 | $scope.statusGraphAutoUpdateSpan = span / 1000; 24 | return configurationService.getErDiagramShowPrimaryKey(); 25 | }).then(function(showPrimaryKey) { 26 | $scope.erDiagramShowPrimaryKey = showPrimaryKey ? "ON" : "OFF"; 27 | return configurationService.getErDiagramShowColumnType(); 28 | }).then(function(showColumnType) { 29 | $scope.erDiagramShowColumnType = showColumnType ? "ON" : "OFF"; 30 | return configurationService.getErDiagramShowColumnNotNull(); 31 | }).then(function(showColumnNotNull) { 32 | $scope.erDiagramShowColumnNotNull = showColumnNotNull ? "ON" : "OFF"; 33 | return ssh2KnownHostService.getAll(); 34 | }).then(function(knownHosts) { 35 | $scope.knownHosts = knownHosts; 36 | }); 37 | $(".nav-pills a[href=\"#" + activeTab + "\"]").tab("show"); 38 | $("#configurationDialog").modal("show"); 39 | }; 40 | 41 | var close = function() { 42 | $("#configurationDialog").modal("hide"); 43 | }; 44 | 45 | var assignEventHandlers = function() { 46 | $scope.$on(Events.SHOW_CONFIGURATION_DIALOG, function(event, data) { 47 | doOpen(data.activeTab); 48 | }); 49 | }; 50 | 51 | $scope.initialize = function() { 52 | $scope.databaseInfoAutoUpdateSpans = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]; 53 | $scope.statusGraphAutoUpdateSpans = [1, 3, 5, 10, 15, 20, 25, 30]; 54 | $scope.rowCountsPerPageInRowsPanel = [10, 20, 50, 100, 200, 500, 1000]; 55 | $scope.queryEditorWrapModes = [QueryEditorWrapMode.WRAP, QueryEditorWrapMode.NOT_WRAP]; 56 | assignEventHandlers(); 57 | }; 58 | 59 | $scope.execute = function() { 60 | }; 61 | 62 | $scope.getQueryHistory = function() { 63 | return mySQLClientService.getQueryHistory(); 64 | }; 65 | 66 | $scope.changeDatabaseInfoSpan = function() { 67 | configurationService.setDatabaseInfoAutoUpdateSpan( 68 | Number($scope.databaseInfoAutoUpdateSpan) * 1000); 69 | }; 70 | 71 | $scope.changeStatusGraphSpan = function() { 72 | configurationService.setStatusGraphAutoUpdateSpan( 73 | Number($scope.statusGraphAutoUpdateSpan) * 1000); 74 | }; 75 | 76 | $scope.editQuery = function(query) { 77 | close(); 78 | anyQueryExecuteService.showQueryPanel(query); 79 | }; 80 | 81 | $scope.changeRowCount = function() { 82 | configurationService.setRowCountPerPageInRowsPanel( 83 | Number($scope.rowCountPerPageInRowsPanel)); 84 | }; 85 | 86 | $scope.changeQueryEditorWrapMode = function() { 87 | configurationService.setQueryEditorWrapMode( 88 | $scope.queryEditorWrapMode === QueryEditorWrapMode.WRAP); 89 | }; 90 | 91 | $scope.changeErDiagramSettings = function() { 92 | configurationService.setErDiagramShowPrimaryKey( 93 | $scope.erDiagramShowPrimaryKey === "ON").then(function() { 94 | return configurationService.setErDiagramShowColumnType( 95 | $scope.erDiagramShowColumnType === "ON"); 96 | }).then(function() { 97 | configurationService.setErDiagramShowColumnNotNull( 98 | $scope.erDiagramShowColumnNotNull === "ON"); 99 | }); 100 | }; 101 | 102 | $scope.getFingerprintString = function(value) { 103 | var disp = value.method + " " + value.fingerprint.substring(0, 2); 104 | for (var i = 2; i < value.fingerprint.length; i += 2) { 105 | disp += ":" + value.fingerprint.substring(i, i + 2); 106 | } 107 | return disp; 108 | }; 109 | 110 | }); 111 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/confirm_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("ConfirmDialogController", function( 2 | $scope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var open = function(message, yesButtonLabel, noButtonLabel, danger) { 8 | $scope.message = message; 9 | $scope.yesButtonLabel = yesButtonLabel; 10 | $scope.noButtonLabel = noButtonLabel; 11 | $scope.danger = danger; 12 | $("#confirmDialog").modal("show"); 13 | }; 14 | 15 | var assignEventHandlers = function() { 16 | $scope.$on(Events.SHOW_CONFIRM_DIALOG, function(event, data) { 17 | open(data.message, data.yesButtonLabel, data.noButtonLabel, data.danger); 18 | $scope.callbackEvent = data.callbackEvent; 19 | }); 20 | }; 21 | 22 | $scope.initialize = function() { 23 | assignEventHandlers(); 24 | }; 25 | 26 | $scope.execute = function() { 27 | $("#confirmDialog").modal("hide"); 28 | $scope.callbackFromConfirmDialog($scope.callbackEvent, true); 29 | }; 30 | 31 | $scope.isDanger = function() { 32 | return $scope.danger; 33 | }; 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/create_database_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("CreateDatabaseDialogController", function( 2 | $scope, 3 | Events, 4 | mySQLClientService, 5 | targetObjectService, 6 | mySQLQueryService 7 | ) { 8 | "use strict"; 9 | 10 | var onShowDialog = function() { 11 | resetErrorMessage(); 12 | $("#createDatabaseDialog").modal("show"); 13 | loadCharacterSet(); 14 | }; 15 | 16 | var loadCharacterSet = function() { 17 | mySQLQueryService.showCharacterSet().then(function(result) { 18 | $scope.characterSets = result.resultsetRows; 19 | $scope.characterSet = "utf8"; 20 | }, function(reason) { 21 | $scope.fatalErrorOccurred(reason); 22 | }); 23 | }; 24 | 25 | var assignEventHandlers = function() { 26 | $scope.$on(Events.SHOW_CREATE_DATABASE_DIALOG, function(event, data) { 27 | onShowDialog(); 28 | }); 29 | 30 | }; 31 | 32 | var resetErrorMessage = function() { 33 | $scope.errorMessage = ""; 34 | }; 35 | 36 | var doCreateDatabase = function() { 37 | var sql = "CREATE DATABASE `" + $scope.databaseName + "` CHARACTER SET " + $scope.characterSet; 38 | mySQLClientService.query(sql).then(function(result) { 39 | if (result.hasResultsetRows) { 40 | $scope.fatalErrorOccurred("Creating table failed."); 41 | } else { 42 | $("#createDatabaseDialog").modal("hide"); 43 | targetObjectService.refreshDatabases(); 44 | } 45 | }, function(reason) { 46 | var errorMessage = "[Error code:" + reason.errorCode; 47 | errorMessage += " SQL state:" + reason.sqlState; 48 | errorMessage += "] "; 49 | errorMessage += reason.errorMessage; 50 | $scope.errorMessage = errorMessage; 51 | }); 52 | }; 53 | 54 | $scope.isErrorMessageVisible = function() { 55 | return $scope.errorMessage.length > 0; 56 | }; 57 | 58 | $scope.initialize = function() { 59 | resetErrorMessage(); 60 | assignEventHandlers(); 61 | }; 62 | 63 | $scope.isCreateDatabaseButtonDisabled = function() { 64 | return !$scope.databaseName || $scope.databaseName.length == 0; 65 | }; 66 | 67 | $scope.createDatabase = function() { 68 | resetErrorMessage(); 69 | doCreateDatabase(); 70 | }; 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/create_table_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("CreateTableDialogController", function( 2 | $scope, 3 | targetObjectService, 4 | mySQLClientService, 5 | Events, 6 | modeService, 7 | Modes, 8 | Engines, 9 | mySQLQueryService 10 | ) { 11 | "use strict"; 12 | 13 | var loadCharacterSets = function() { 14 | mySQLQueryService.showCharacterSet().then(function(result) { 15 | $scope.characterSets = result.resultsetRows; 16 | $scope.characterSet = "utf8"; 17 | }, function(reason) { 18 | $scope.fatalErrorOccurred(reason); 19 | }); 20 | }; 21 | 22 | var resetErrorMessage = function() { 23 | $scope.errorMessage = ""; 24 | }; 25 | 26 | var assignEventHandlers = function() { 27 | $("#createTableDialog").on("show.bs.modal", onShowCreateTableDialog); 28 | }; 29 | 30 | var onShowCreateTableDialog = function(event) { 31 | resetErrorMessage(); 32 | $scope.selectedDatabase = targetObjectService.getDatabase(); 33 | $scope.tableName = ""; 34 | $scope.engine = "InnoDB"; 35 | loadCharacterSets(); 36 | }; 37 | 38 | var doCreateTable = function() { 39 | var tableName = $scope.tableName; 40 | var sql = "CREATE TABLE `" + tableName + "` ("; 41 | sql += "id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT) "; 42 | sql += "DEFAULT CHARACTER SET `" + $scope.characterSet + "` "; 43 | sql += "ENGINE = `" + $scope.engine + "`"; 44 | mySQLClientService.query(sql).then(function(result) { 45 | if (result.hasResultsetRows) { 46 | $scope.fatalErrorOccurred("Creating table failed."); 47 | } else { 48 | $("#createTableDialog").modal("hide"); 49 | targetObjectService.refreshTableList(); 50 | targetObjectService.changeTable({ 51 | name: tableName, 52 | type: "BASE TABLE", 53 | className: "glyphicon-th-large" 54 | }); 55 | modeService.changeMode(Modes.STRUCTURE); 56 | } 57 | }, function(reason) { 58 | var errorMessage = "[Error code:" + reason.errorCode; 59 | errorMessage += " SQL state:" + reason.sqlState; 60 | errorMessage += "] "; 61 | errorMessage += reason.errorMessage; 62 | $scope.errorMessage = errorMessage; 63 | }); 64 | }; 65 | 66 | $scope.initialize = function() { 67 | resetErrorMessage(); 68 | assignEventHandlers(); 69 | $scope.engines = Engines; 70 | $scope.engine = Engines[0]; 71 | }; 72 | 73 | $scope.createTable = function() { 74 | resetErrorMessage(); 75 | if (!$scope.tableName) { 76 | $scope.errorMessage = "Table name is empty."; 77 | return; 78 | } 79 | doCreateTable(); 80 | }; 81 | 82 | $scope.isErrorMessageVisible = function() { 83 | return $scope.errorMessage.length > 0; 84 | }; 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/error_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("ErrorDialogController", function( 2 | $scope, 3 | mySQLClientService, 4 | Events, 5 | ErrorLevel 6 | ) { 7 | "use strict"; 8 | 9 | var onCloseDialog = function() { 10 | if ($scope.errorLevel === ErrorLevel.FATAL) { 11 | mySQLClientService.logout().then(function() { 12 | $scope.safeApply(function() { 13 | $scope.notifyConnectionChanged(); 14 | }); 15 | }); 16 | } 17 | }; 18 | 19 | $scope.initialize = function() { 20 | $("#errorDialog").on("hidden.bs.modal", function() { 21 | onCloseDialog(); 22 | }); 23 | $scope.$on(Events.SHOW_ERROR_DIALOG, function(event, data) { 24 | $scope.errorLevel = data.errorLevel; 25 | $scope.safeApply(function() { 26 | if (data.errorLevel === ErrorLevel.FATAL) { 27 | $scope.title = "Fatal error"; 28 | $scope.message1 = "Fatal error occurred. Go back to login screen."; 29 | $scope.message2 = data.reason; 30 | } else { 31 | $scope.title = data.title || "Error"; 32 | $scope.message1 = data.message; 33 | $scope.message2 = data.reason; 34 | } 35 | $("#errorDialog").modal("show"); 36 | }); 37 | }); 38 | }; 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/favorite_list_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("FavoriteListController", function( 2 | $scope, 3 | mySQLClientService, 4 | favoriteService, 5 | Events, 6 | UIConstants 7 | ) { 8 | "use strict"; 9 | 10 | var assignWindowResizeEventHandler = function() { 11 | $(window).resize(function(evt) { 12 | adjustFavoriteListHeight(); 13 | }); 14 | }; 15 | 16 | var adjustFavoriteListHeight = function() { 17 | $("#favoriteList").height( 18 | $(window).height() - 19 | UIConstants.WINDOW_TITLE_PANEL_HEIGHT - 20 | UIConstants.NAVBAR_HEIGHT - 21 | 35); 22 | }; 23 | 24 | var loadFavorites = function() { 25 | favoriteService.getAll().then(function(favorites) { 26 | $scope.favorites = favorites; 27 | }); 28 | }; 29 | 30 | var assignEventHandlers = function() { 31 | $scope.$on(Events.FAVORITES_CHANGED, function(event, favorites) { 32 | loadFavorites(); 33 | }); 34 | }; 35 | 36 | $scope.initialize = function() { 37 | assignWindowResizeEventHandler(); 38 | adjustFavoriteListHeight(); 39 | assignEventHandlers(); 40 | loadFavorites(); 41 | }; 42 | 43 | $scope.isFavoriteListVisible = function() { 44 | return !mySQLClientService.isConnected(); 45 | }; 46 | 47 | $scope.selectFavorite = function(name) { 48 | favoriteService.select(name); 49 | }; 50 | 51 | $scope.deleteFavorite = function($event, name) { 52 | favoriteService.delete(name); 53 | $event.preventDefault(); 54 | }; 55 | 56 | $scope.connectFromFavorite = function($event, name) { 57 | favoriteService.selectAndLogin(name); 58 | }; 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/find_rows_with_the_value_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("FindRowsWithTheValueDialogController", function( 2 | $scope, 3 | Events, 4 | targetObjectService, 5 | mySQLQueryService, 6 | rowsSelectionService, 7 | anyQueryExecuteService, 8 | TableTypes, 9 | sqlExpressionService 10 | ) { 11 | "use strict"; 12 | 13 | var ONLY_SAME_COLUMN_TYPE = "same"; 14 | var ANY_COLUMN_TYPE = "any"; 15 | 16 | var MATCHING_RULE_FULL = "full"; 17 | var MATCHING_RULE_PARTIAL = "partial"; 18 | 19 | var onShowDialog = function() { 20 | resetErrorMessage(); 21 | $scope.columnType = ONLY_SAME_COLUMN_TYPE; 22 | $scope.removeEmptyResult = "true"; 23 | $scope.matchingRule = MATCHING_RULE_FULL; 24 | var table = targetObjectService.getTable(); 25 | mySQLQueryService.showFullColumns(table.name).then(function(result) { 26 | var selectedRow = rowsSelectionService.getSelectedRows(); 27 | var resultsetRows = result.resultsetRows; 28 | $scope.value = null; 29 | $scope.values = []; 30 | angular.forEach(resultsetRows, function(row, index) { 31 | var value = selectedRow.entity["column" + index]; 32 | if (value !== null) { 33 | var typeName = getColumnTypeName(row); 34 | $scope.values.push({ 35 | value: value, 36 | label: "\"" + value + "\" (" + typeName + " - " + row.values[0] + ")", 37 | type: typeName 38 | }); 39 | } 40 | }); 41 | if ($scope.values.length > 0) { 42 | $scope.value = $scope.values[0]; 43 | } 44 | $("#findRowsWithTheValueDialog").modal("show"); 45 | }, function(reason) { 46 | $scope.fatalErrorOccurred(reason); 47 | }); 48 | }; 49 | 50 | var getColumnTypeName = function(column) { 51 | var type = column.values[1]; 52 | var idx = type.indexOf("("); 53 | if (idx > -1) { 54 | type = type.substring(0, idx); 55 | } 56 | return type; 57 | }; 58 | 59 | var assignEventHandlers = function() { 60 | $scope.$on(Events.SHOW_FIND_ROWS_WITH_THE_VALUE_DIALOG, function(event, data) { 61 | onShowDialog(); 62 | }); 63 | 64 | }; 65 | 66 | var resetErrorMessage = function() { 67 | $scope.errorMessage = ""; 68 | }; 69 | 70 | var loadStructure = function(tables, sqls) { 71 | if (tables.length === 0) { 72 | $("#findRowsWithTheValueDialog").modal("hide"); 73 | anyQueryExecuteService.showAndExecuteQueryPanel( 74 | sqls.join(";\n"), 75 | $scope.removeEmptyResult === "true"); 76 | return; 77 | } 78 | var table = tables.shift(); 79 | mySQLQueryService.showFullColumns(table).then(function(result) { 80 | var resultsetRows = result.resultsetRows; 81 | angular.forEach(resultsetRows, function(resultsetRow) { 82 | var typeName = resultsetRow.values[1]; 83 | if ($scope.columnType.value === ANY_COLUMN_TYPE || 84 | typeName.indexOf($scope.value.type) === 0) { 85 | var sql = "SELECT * FROM `" + table + "` WHERE "; 86 | var target = resultsetRow.values[0]; 87 | if ($scope.matchingRule === MATCHING_RULE_FULL) { 88 | sql += "`" + target + "` = \"" + sqlExpressionService.replaceValue($scope.value.value) + "\""; 89 | } else { 90 | sql += "`" + target + "` LIKE \"%" + sqlExpressionService.replaceValue($scope.value.value) + "%\""; 91 | } 92 | sqls.push(sql); 93 | } 94 | }); 95 | loadStructure(tables, sqls); 96 | }, function(reason) { 97 | $scope.fatalErrorOccurred(reason); 98 | }); 99 | }; 100 | 101 | var setupItems = function() { 102 | $scope.columnTypes = [ 103 | {label: "Only same column type", value: ONLY_SAME_COLUMN_TYPE}, 104 | {label: "Any", value: ANY_COLUMN_TYPE} 105 | ]; 106 | $scope.columnType = ONLY_SAME_COLUMN_TYPE; 107 | }; 108 | 109 | $scope.isErrorMessageVisible = function() { 110 | return $scope.errorMessage.length > 0; 111 | }; 112 | 113 | $scope.initialize = function() { 114 | resetErrorMessage(); 115 | assignEventHandlers(); 116 | setupItems(); 117 | }; 118 | 119 | $scope.findRowsWithTheValue = function() { 120 | mySQLQueryService.showTables().then(function(result) { 121 | var resultsetRows = result.resultsetRows; 122 | var tables = []; 123 | angular.forEach(resultsetRows, function(resultsetRow) { 124 | if (resultsetRow.values[1] === TableTypes.BASE_TABLE) { 125 | tables.push(resultsetRow.values[0]); 126 | } 127 | }, tables); 128 | loadStructure(tables, []); 129 | }, function(reason) { 130 | $("#findRowsWithTheValueDialog").modal("hide"); 131 | $scope.fatalErrorOccurred(reason); 132 | }); 133 | }; 134 | 135 | }); 136 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/insert_row_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("InsertRowDialogController", function( 2 | $scope, 3 | targetObjectService, 4 | rowsPagingService, 5 | mySQLClientService, 6 | Events, 7 | sqlExpressionService, 8 | ValueTypes 9 | ) { 10 | "use strict"; 11 | 12 | var resetErrorMessage = function() { 13 | $scope.errorMessage = ""; 14 | }; 15 | 16 | var doOpen = function(columnDefinitions) { 17 | resetErrorMessage(); 18 | $scope.values = {}; 19 | $scope.valueTypes = {}; 20 | angular.forEach(columnDefinitions, function(column) { 21 | $scope.values[column.name] = ""; 22 | $scope.valueTypes[column.name] = ValueTypes.VALUE; 23 | }); 24 | $scope.columnDefinitions = columnDefinitions; 25 | $("#insertRowDialog").modal("show"); 26 | }; 27 | 28 | var doOpenWith = function(columnDefinitions, row) { 29 | resetErrorMessage(); 30 | $scope.values = {}; 31 | $scope.valueTypes = {}; 32 | angular.forEach(columnDefinitions, function(column, index) { 33 | var value = row.values[index]; 34 | $scope.values[column.name] = value; 35 | if (value === null) { 36 | $scope.valueTypes[column.name] = ValueTypes.NULL; 37 | } else { 38 | $scope.valueTypes[column.name] = ValueTypes.VALUE; 39 | } 40 | }); 41 | $scope.columnDefinitions = columnDefinitions; 42 | $("#insertRowDialog").modal("show"); 43 | } 44 | 45 | var assignEventHandlers = function() { 46 | $scope.$on(Events.SHOW_INSERT_ROW_DIALOG, function(event, columnDefinitions) { 47 | doOpen(columnDefinitions); 48 | }); 49 | $scope.$on(Events.SHOW_CLONE_ROW_DIALOG, function(event, data) { 50 | doOpenWith(data.columnDefinitions, data.row); 51 | }); 52 | }; 53 | 54 | $scope.initialize = function() { 55 | resetErrorMessage(); 56 | assignEventHandlers(); 57 | }; 58 | 59 | $scope.isErrorMessageVisible = function() { 60 | return $scope.errorMessage.length > 0; 61 | }; 62 | 63 | $scope.targetInputColumns = function() { 64 | var result = []; 65 | angular.forEach($scope.columnDefinitions, function(column) { 66 | // TODO filter 67 | result.push(column); 68 | }, result); 69 | return result; 70 | }; 71 | 72 | $scope.getExecutingQuery = function() { 73 | if (targetObjectService.getTable()) { 74 | return sqlExpressionService.createInsertStatement( 75 | targetObjectService.getTable().name, $scope.values, $scope.valueTypes); 76 | } else { 77 | return ""; 78 | } 79 | }; 80 | 81 | $scope.insertRow = function() { 82 | resetErrorMessage(); 83 | var sql = sqlExpressionService.createInsertStatement( 84 | targetObjectService.getTable().name, $scope.values, $scope.valueTypes); 85 | if (sql) { 86 | mySQLClientService.query(sql).then(function(result) { 87 | if (result.hasResultsetRows) { 88 | $scope.fatalErrorOccurred("Inserting row failed."); 89 | } else { 90 | $("#insertRowDialog").modal("hide"); 91 | rowsPagingService.refresh(); 92 | } 93 | }, function(reason) { 94 | var errorMessage = "[Error code:" + reason.errorCode; 95 | errorMessage += " SQL state:" + reason.sqlState; 96 | errorMessage += "] "; 97 | errorMessage += reason.errorMessage; 98 | $scope.errorMessage = errorMessage; 99 | }); 100 | } else { 101 | $scope.errorMessage = "Any values not filled in."; 102 | } 103 | }; 104 | 105 | $scope.onChangeValueType = function(columnName) { 106 | var valueType = $scope.valueTypes[columnName]; 107 | if (valueType !== ValueTypes.NULL) { 108 | var value = $scope.values[columnName]; 109 | if (!value) { 110 | $scope.values[columnName] = ""; 111 | } 112 | } 113 | }; 114 | 115 | $scope.isDisabledValueField = function(columnName) { 116 | return $scope.valueTypes[columnName] === ValueTypes.NULL || 117 | $scope.valueTypes[columnName] === ValueTypes.DEFAULT; 118 | }; 119 | 120 | }); 121 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/navbar_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("NavbarController", function( 2 | $scope, 3 | mySQLClientService, 4 | modeService, 5 | targetObjectService, 6 | Events, 7 | Modes, 8 | ConfigurationTabs 9 | ) { 10 | "use strict"; 11 | 12 | var loadDatabaseList = function() { 13 | mySQLClientService.getDatabases().then(function(databases) { 14 | $scope.safeApply(function() { 15 | $scope.selectedDatabase = "[Select database]"; 16 | $scope.databases = databases; 17 | modeService.changeMode(Modes.DATABASE); 18 | targetObjectService.resetDatabase(); 19 | targetObjectService.refreshTableList(); 20 | }); 21 | }); 22 | }; 23 | 24 | var onConnectionChanged = function(connectionInfo) { 25 | if (mySQLClientService.isConnected()) { 26 | $scope.safeApply(function() { 27 | targetObjectService.resetDatabase(); 28 | loadDatabaseList(); 29 | $scope.connectionInfo = connectionInfo; 30 | }); 31 | } 32 | }; 33 | 34 | var logout = function() { 35 | mySQLClientService.logout().then(function() { 36 | $scope.notifyConnectionChanged(); 37 | }); 38 | }; 39 | 40 | var assignEventHandlers = function() { 41 | $scope.$on(Events.CONNECTION_CHANGED, function(event, connectionInfo) { 42 | onConnectionChanged(connectionInfo); 43 | }); 44 | $scope.$on(Events.LOGOUT, function(event, data) { 45 | logout(); 46 | }); 47 | $scope.$on(Events.REFRESH_DATABASES, function(event, data) { 48 | loadDatabaseList(); 49 | }); 50 | $scope.$on(Events.SHOW_ER_DIAGRAM, function(event, data) { 51 | modeService.changeMode(Modes.ER_DIAGRAM); 52 | }); 53 | }; 54 | 55 | $scope.initialize = function() { 56 | assignEventHandlers(); 57 | $("[rel=tooltip]").tooltip(); 58 | $scope.selectedDatabase = "[Select database]"; 59 | }; 60 | 61 | $scope.isConnected = function() { 62 | return mySQLClientService.isConnected(); 63 | }; 64 | 65 | $scope.selectDatabase = function(event, database) { 66 | $scope.selectedDatabase = database; 67 | targetObjectService.changeDatabase(database); 68 | }; 69 | 70 | $scope.logout = function(event) { 71 | $scope.showConfirmDialog( 72 | "Would you really like to logout from MySQL server?", 73 | "Yes", 74 | "No", 75 | Events.LOGOUT, 76 | true 77 | ); 78 | }; 79 | 80 | $scope.isRowsActive = function() { 81 | return modeService.getMode() === Modes.ROWS; 82 | }; 83 | 84 | $scope.isStructureActive = function() { 85 | return modeService.getMode() === Modes.STRUCTURE; 86 | }; 87 | 88 | $scope.isQueryActive = function() { 89 | return modeService.getMode() === Modes.QUERY; 90 | }; 91 | 92 | $scope.isDatabaseActive = function() { 93 | return modeService.getMode() === Modes.DATABASE; 94 | }; 95 | 96 | $scope.isInformationActive = function() { 97 | return modeService.getMode() === Modes.INFORMATION; 98 | }; 99 | 100 | $scope.isRelationActive = function() { 101 | return modeService.getMode() === Modes.RELATION; 102 | }; 103 | 104 | $scope.isProcsFuncsActive = function() { 105 | return modeService.getMode() === Modes.PROCS_FUNCS; 106 | }; 107 | 108 | $scope.isStatusGraphActive = function() { 109 | return modeService.getMode() === Modes.STATUS_GRAPH; 110 | }; 111 | 112 | $scope.isERDiagramActive = function() { 113 | return modeService.getMode() === Modes.ER_DIAGRAM; 114 | }; 115 | 116 | $scope.selectRows = function() { 117 | modeService.changeMode(Modes.ROWS); 118 | }; 119 | 120 | $scope.selectStructure = function() { 121 | modeService.changeMode(Modes.STRUCTURE); 122 | }; 123 | 124 | $scope.selectQuery = function() { 125 | modeService.changeMode(Modes.QUERY); 126 | }; 127 | 128 | $scope.selectInformation = function() { 129 | modeService.changeMode(Modes.INFORMATION); 130 | }; 131 | 132 | $scope.showDatabaseInfo = function() { 133 | modeService.changeMode(Modes.DATABASE); 134 | }; 135 | 136 | $scope.configure = function() { 137 | $scope.showConfigurationDialog(ConfigurationTabs.BASIC); 138 | }; 139 | 140 | $scope.selectRelation = function() { 141 | modeService.changeMode(Modes.RELATION); 142 | }; 143 | 144 | $scope.selectProcsFuncs = function() { 145 | modeService.changeMode(Modes.PROCS_FUNCS); 146 | }; 147 | 148 | $scope.selectStatusGraph = function() { 149 | modeService.changeMode(Modes.STATUS_GRAPH); 150 | }; 151 | 152 | $scope.selectERDiagram = function() { 153 | $scope.showConfirmDialog( 154 | "Render an ER diagram of the DB?\nIt might take more than a minute depending on the number of the tables.", 155 | "Yes", 156 | "No", 157 | Events.SHOW_ER_DIAGRAM, 158 | false 159 | ); 160 | }; 161 | 162 | 163 | }); 164 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/progress_bar_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("ProgressBarController", function( 2 | $scope, 3 | Events, 4 | mySQLClientService 5 | ) { 6 | "use strict"; 7 | 8 | var assignWindowResizeEventHandler = function() { 9 | $(window).resize(function(evt) { 10 | adjustProgressPanelHeight(); 11 | }); 12 | }; 13 | 14 | var adjustProgressPanelHeight = function() { 15 | $("#progressPanel").height($(window).height()); 16 | }; 17 | 18 | var setVisibleProgressBar = function(newVisibleProgressBar) { 19 | $scope.safeApply(function() { 20 | if (newVisibleProgressBar) { 21 | clearQuery(); 22 | } 23 | $scope.visibleProgressBar = newVisibleProgressBar; 24 | }); 25 | }; 26 | 27 | var clearQuery = function() { 28 | $scope.query = ""; 29 | }; 30 | 31 | var assignEventHandlers = function() { 32 | $scope.$on(Events.SHOW_PROGRESS_BAR, function(event) { 33 | setVisibleProgressBar(true); 34 | }); 35 | $scope.$on(Events.HIDE_PROGRESS_BAR, function(event) { 36 | setVisibleProgressBar(false); 37 | }); 38 | $scope.$on(Events.EXECUTING_QUERY, function(event, query) { 39 | $scope.query = query; 40 | }); 41 | }; 42 | 43 | $scope.initialize = function() { 44 | assignEventHandlers(); 45 | $scope.visibleProgressBar = false; 46 | assignWindowResizeEventHandler(); 47 | adjustProgressPanelHeight(); 48 | clearQuery(); 49 | }; 50 | 51 | $scope.isVisibleProgressBar = function() { 52 | return $scope.visibleProgressBar; 53 | }; 54 | 55 | $scope.abort = function() { 56 | setVisibleProgressBar(false); 57 | mySQLClientService.logout().then(function() { 58 | $scope.notifyConnectionChanged(); 59 | }); 60 | }; 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/update_row_dialog_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("UpdateRowDialogController", function( 2 | $scope, 3 | targetObjectService, 4 | Events, 5 | sqlExpressionService, 6 | mySQLClientService, 7 | rowsPagingService, 8 | ValueTypes, 9 | anyQueryExecuteService 10 | ) { 11 | "use strict"; 12 | 13 | var resetErrorMessage = function() { 14 | $scope.errorMessage = ""; 15 | }; 16 | 17 | var doOpen = function(columnDefinitions, row) { 18 | resetErrorMessage(); 19 | $scope.values = {}; 20 | $scope.originalValues = {}; 21 | $scope.valueTypes = {}; 22 | $scope.originalRow = row; 23 | angular.forEach(columnDefinitions, function(column, index) { 24 | var value = row.values[index]; 25 | $scope.values[column.name] = value; 26 | $scope.originalValues[column.name] = value; 27 | if (value === null) { 28 | $scope.valueTypes[column.name] = ValueTypes.NULL; 29 | } else { 30 | $scope.valueTypes[column.name] = ValueTypes.VALUE; 31 | } 32 | }); 33 | $scope.columnDefinitions = columnDefinitions; 34 | $("#updateRowDialog").modal("show"); 35 | }; 36 | 37 | var assignEventHandlers = function() { 38 | $scope.$on(Events.SHOW_UPDATE_ROW_DIALOG, function(event, data) { 39 | doOpen(data.columnDefinitions, data.row); 40 | }); 41 | }; 42 | 43 | $scope.initialize = function() { 44 | resetErrorMessage(); 45 | assignEventHandlers(); 46 | }; 47 | 48 | $scope.isErrorMessageVisible = function() { 49 | return $scope.errorMessage.length > 0; 50 | }; 51 | 52 | $scope.targetInputColumns = function() { 53 | var result = []; 54 | angular.forEach($scope.columnDefinitions, function(column) { 55 | // TODO filter 56 | result.push(column); 57 | }, result); 58 | return result; 59 | }; 60 | 61 | $scope.onChangeValueType = function(columnName) { 62 | var valueType = $scope.valueTypes[columnName]; 63 | if (valueType !== ValueTypes.NULL) { 64 | var value = $scope.values[columnName]; 65 | if (!value) { 66 | $scope.values[columnName] = ""; 67 | } 68 | } 69 | }; 70 | 71 | $scope.isDisabledValueField = function(columnName) { 72 | return $scope.valueTypes[columnName] === ValueTypes.NULL; 73 | }; 74 | 75 | $scope.updateRow = function() { 76 | resetErrorMessage(); 77 | var sql = "UPDATE `" + targetObjectService.getTable().name + "` "; 78 | var sets = []; 79 | angular.forEach($scope.values, function(value, columnName) { 80 | if ($scope.valueTypes[columnName] === ValueTypes.NULL && $scope.originalValues[columnName] !== null) { 81 | sets.push("`" + columnName + "` = NULL"); 82 | } else if (value !== $scope.originalValues[columnName]) { 83 | if ($scope.valueTypes[columnName] === ValueTypes.VALUE) { 84 | sets.push("`" + columnName + "` = " + "'" + sqlExpressionService.replaceValue(value) + "'"); 85 | } else if ($scope.valueTypes[columnName] === ValueTypes.EXPRESSION) { 86 | sets.push("`" + columnName + "` = " + value); 87 | } 88 | } 89 | }); 90 | if (sets.length !== 0) { 91 | sql += "SET " + sets.join(","); 92 | var primaryKeyColumns = 93 | sqlExpressionService.getPrimaryKeyColumns($scope.columnDefinitions); 94 | var whereConditions = 95 | sqlExpressionService.createWhereConditionsForUpdate( 96 | primaryKeyColumns, 97 | $scope.columnDefinitions, 98 | $scope.originalRow); 99 | sql += " WHERE " + whereConditions.join(" AND "); 100 | sql += jQuery.isEmptyObject(primaryKeyColumns) ? " LIMIT 1" : ""; 101 | mySQLClientService.query(sql).then(function(result) { 102 | if (result.hasResultsetRows) { 103 | $scope.fatalErrorOccurred("Updating row failed."); 104 | } else { 105 | $("#updateRowDialog").modal("hide"); 106 | rowsPagingService.refresh(); 107 | } 108 | }, function(reason) { 109 | var errorMessage = "[Error code:" + reason.errorCode; 110 | errorMessage += " SQL state:" + reason.sqlState; 111 | errorMessage += "] "; 112 | errorMessage += reason.errorMessage; 113 | $scope.errorMessage = errorMessage; 114 | }); 115 | } else { 116 | $scope.errorMessage = "Any values not changed."; 117 | } 118 | }; 119 | 120 | $scope.createInsertStatement = function() { 121 | var sql = sqlExpressionService.createInsertStatement( 122 | targetObjectService.getTable().name, $scope.values, $scope.valueTypes); 123 | $("#updateRowDialog").modal("hide"); 124 | anyQueryExecuteService.showQueryPanel(sql); 125 | }; 126 | 127 | }); 128 | -------------------------------------------------------------------------------- /app/scripts/window/controllers/window_title_panel_controller.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.controller("windowTitlePanelController", function( 2 | $scope, 3 | mySQLClientService, 4 | $q, 5 | Events 6 | ) { 7 | "use strict"; 8 | 9 | var assignEventHandlers = function() { 10 | $scope.$on(Events.CONNECTION_CHANGED, function(event, connectionInfo) { 11 | onConnectionChanged(connectionInfo); 12 | }); 13 | }; 14 | 15 | var resetTitleText = function() { 16 | $scope.titleText = getAboutMe(); 17 | document.title = chrome.runtime.getManifest().name; 18 | }; 19 | 20 | var getAboutMe = function() { 21 | var manifest = chrome.runtime.getManifest(); 22 | var aboutMe = manifest.name + " version " + manifest.version; 23 | aboutMe += " (C) " + manifest.author + " 2014-" + (new Date()).getFullYear() + ", all rights reserved."; 24 | return aboutMe; 25 | }; 26 | 27 | var onConnectionChanged = function(info) { 28 | if (mySQLClientService.isConnected()) { 29 | $scope.safeApply(function() { 30 | var opt = info.useSSL ? ":SSL" : ""; 31 | opt += info.usePortForwarding ? ":SSH2" : ""; 32 | var name = ""; 33 | if (info.name) { 34 | name = "[" + info.name + "] "; 35 | } 36 | $scope.titleText = name + info.hostName + ":" + info.port + opt + 37 | " | " + info.userName + 38 | " | " + info.initialHandshakeRequest.serverVersion; 39 | document.title = $scope.titleText; 40 | }); 41 | } else { 42 | resetTitleText(); 43 | } 44 | }; 45 | 46 | $scope.initialize = function() { 47 | assignEventHandlers(); 48 | resetTitleText(); 49 | $scope.alwaysOnTop = false; 50 | }; 51 | 52 | $scope.openNewWindow = function() { 53 | chrome.runtime.getBackgroundPage(function(bg) { 54 | bg.createWindow(); 55 | }); 56 | }; 57 | 58 | $scope.getAlwaysOnTopClass = function() { 59 | return $scope.alwaysOnTop ? "fa-circle" : "fa-circle-o"; 60 | }; 61 | 62 | $scope.changeAlwaysOnTop = function() { 63 | $scope.alwaysOnTop = !$scope.alwaysOnTop; 64 | var appWindow = chrome.app.window.current(); 65 | appWindow.setAlwaysOnTop($scope.alwaysOnTop); 66 | }; 67 | 68 | $scope.moveOtherWindow = function() { 69 | $scope.showChangeWindowPanel(); 70 | }; 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /app/scripts/window/directives/add_column_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("addColumnDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/add_column_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/add_index_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("addIndexDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/add_index_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/add_relation_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("addRelationDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/add_relation_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/change_window_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("changeWindowPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/change_window_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/configuration_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("configurationDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/configuration_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/confirm_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("confirmDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/confirm_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/create_database_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("createDatabaseDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/create_database_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/create_routine_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("createRoutineDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/create_routine_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/create_table_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("createTableDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/create_table_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/database_object_list_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("databaseObjectListPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/database_object_list_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/database_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("databasePanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/database_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/edit_column_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("editColumnDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/edit_column_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/er_diagram_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("erDiagramPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/er_diagram_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/error_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("errorDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/error_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/favorite_list_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("favoriteListPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/favorite_list_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/file_drop.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("fileDrop", [function() { 2 | return { 3 | restrict: "A", 4 | scope: { 5 | onFileDrop: "&fileDrop" 6 | }, 7 | controller: ["$scope", function($scope) { 8 | $scope.onDragEnterOrOver = function(evt) { 9 | evt.stopPropagation(); 10 | evt.preventDefault(); 11 | }; 12 | 13 | $scope.onDrop = function(evt) { 14 | evt.stopPropagation(); 15 | evt.preventDefault(); 16 | $scope.onFileDrop({ 17 | files: evt.originalEvent.dataTransfer.files 18 | }); 19 | }; 20 | 21 | $scope.assignEventHandlers = function(element) { 22 | element.bind("dragover", function(evt) { 23 | $scope.onDragEnterOrOver(evt); 24 | }); 25 | element.bind("dragenter", function(evt) { 26 | $scope.onDragEnterOrOver(evt); 27 | }); 28 | element.bind("drop", function(evt) { 29 | $scope.onDrop(evt); 30 | }); 31 | }; 32 | }], 33 | link: function(scope, element, attrs) { 34 | scope.assignEventHandlers(element); 35 | } 36 | }; 37 | }]); 38 | -------------------------------------------------------------------------------- /app/scripts/window/directives/find_rows_with_the_value_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("findRowsWithTheValueDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/find_rows_with_the_value_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/find_same_rows_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("findSameRowsDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/find_same_rows_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/footer_button.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("footerButton", [function() { 2 | return { 3 | restrict: "E", 4 | templateUrl: "templates/footer_button.html", 5 | replace: true, 6 | scope: { 7 | icon: "@", 8 | title: "@", 9 | click: "&", 10 | enable: "&" 11 | }, 12 | link: function(scope, element, attrs) { 13 | if (!attrs.enable) { 14 | scope.enable = function() { 15 | return true; 16 | }; 17 | } 18 | if (attrs.align === "left") { 19 | scope.align = "Left"; 20 | } else { 21 | scope.align = "Right"; 22 | } 23 | } 24 | }; 25 | }]); 26 | -------------------------------------------------------------------------------- /app/scripts/window/directives/information_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("informationPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/information_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/insert_row_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("insertRowDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/insert_row_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/login_form.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("loginForm", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/login_form.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/main_footer.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("mainFooter", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/main_footer.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/navbar_menu_item.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("navbarMenuItem", ["mySQLClientService", function(mySQLClientService) { 2 | return { 3 | restrict: "E", 4 | templateUrl: "templates/navbar_menu_item.html", 5 | replace: true, 6 | scope: { 7 | icon: "@", 8 | title: "@", 9 | click: "&", 10 | active: "&" 11 | }, 12 | link: function(scope, element, attrs) { 13 | scope.isConnected = function() { 14 | return mySQLClientService.isConnected(); 15 | }; 16 | element.tooltip({ 17 | title: scope.title 18 | }); 19 | } 20 | }; 21 | }]); 22 | -------------------------------------------------------------------------------- /app/scripts/window/directives/navbar_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("navbarPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/navbar_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/procedures_functions_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("proceduresFunctionsPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/procs_funcs_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/progress_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("progressPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/progress_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/query_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("queryPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/query_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/relation_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("relationPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/relation_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/resize_when.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("resizeWhen", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "A", 6 | scope: false, 7 | link: function(scope, elem, attrs, ctrl) { 8 | var resizeExpr = attrs.resizeWhen; 9 | var listener = scope.$watch(resizeExpr, function(value) { 10 | if (value) { 11 | elem.resize(); 12 | listener(); 13 | } 14 | }, false); 15 | } 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /app/scripts/window/directives/rows_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("rowsPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/rows_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/sglclick.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("sglclick", ["$parse", function($parse) { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "A", 6 | link: function(scope, element, attr) { 7 | var fn = $parse(attr.sglclick); 8 | var delay = 300, clicks = 0, timer = null; 9 | element.on("click", function (event) { 10 | clicks++; //count clicks 11 | if(clicks === 1) { 12 | timer = setTimeout(function() { 13 | scope.$apply(function () { 14 | fn(scope, { $event: event }); 15 | }); 16 | clicks = 0; //after action performed, reset counter 17 | }, delay); 18 | } else { 19 | clearTimeout(timer); //prevent single-click action 20 | clicks = 0; //after action performed, reset counter 21 | } 22 | }); 23 | } 24 | }; 25 | }]); 26 | -------------------------------------------------------------------------------- /app/scripts/window/directives/status_graph.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("statusGraph", [function() { 2 | 3 | var createGraphOption = function() { 4 | return { 5 | seriesDefaults: { 6 | markerOptions: { 7 | show: false 8 | }, 9 | shadow: false, 10 | fill: true, 11 | fillAlpha: 0.3, 12 | fillAndStroke: true 13 | }, 14 | grid: { 15 | background: "#ffffff", 16 | shadow: false 17 | }, 18 | axes: { 19 | xaxis: { 20 | min: 0, 21 | max: 52, 22 | showTicks: false 23 | } 24 | } 25 | }; 26 | }; 27 | 28 | return { 29 | restrict: "E", 30 | templateUrl: "templates/status_graph.html", 31 | replace: true, 32 | scope: { 33 | normalData: "=", 34 | deltaData: "=", 35 | statusName: "=target", 36 | closedCallback: "&closed", 37 | graphType: "=" 38 | }, 39 | controller: ["$scope", "GraphTypes", function($scope, GraphTypes) { 40 | this.assignEventHandlers = function(scope, element) { 41 | window.addEventListener("resize", function(e) { 42 | scope.replot(); 43 | }); 44 | element.on("$destroy", function() { 45 | scope.destroy(); 46 | }); 47 | }; 48 | 49 | this.createGraph = function(scope, element) { 50 | var data; 51 | if ($scope.graphType === GraphTypes.NORMAL) { 52 | data = scope.normalData[scope.statusName]; 53 | } else { 54 | data = scope.deltaData[scope.statusName]; 55 | } 56 | return element.find("div.graph").jqplot( 57 | [data], 58 | createGraphOption() 59 | ).data("jqplot"); 60 | }; 61 | 62 | $scope.replot = function() { 63 | var options = createGraphOption(); 64 | var data; 65 | if ($scope.graphType === GraphTypes.NORMAL) { 66 | data = $scope.normalData[$scope.statusName]; 67 | } else { 68 | data = $scope.deltaData[$scope.statusName]; 69 | } 70 | options.data = [data]; 71 | options.clear = true; 72 | $scope.jqplot.replot(options); 73 | }; 74 | 75 | $scope.destroy = function() { 76 | $scope.jqplot.destroy(); 77 | }; 78 | 79 | $scope.removeMe = function(element) { 80 | $scope.closedCallback($scope.statusName); 81 | element.remove(); 82 | }; 83 | }], 84 | compile: function(element, attrs) { 85 | return function(scope, element, attrs, ctrl) { 86 | scope.jqplot = ctrl.createGraph(scope, element); 87 | 88 | scope.$watch("deltaData", function(newVal, oldVal) { 89 | scope.replot(); 90 | }, true); 91 | 92 | ctrl.assignEventHandlers(scope, element); 93 | 94 | scope.closeGraph = (function(e) { 95 | return function() { 96 | scope.removeMe(e); 97 | }; 98 | })(element); 99 | }; 100 | } 101 | }; 102 | 103 | }]); 104 | -------------------------------------------------------------------------------- /app/scripts/window/directives/status_graph_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("statusGraphPanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/status_graph_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/structure_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("structurePanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/structure_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/update_row_dialog.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("updateRowDialog", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/update_row_dialog.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/directives/window_title_panel.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.directive("windowTitlePanel", function() { 2 | "use strict"; 3 | 4 | return { 5 | restrict: "E", 6 | templateUrl: "templates/window_title_panel.html" 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /app/scripts/window/main.js: -------------------------------------------------------------------------------- 1 | var chromeMyAdmin = angular.module("chromeMyAdmin", ["ngGrid", "ui.ace"]); 2 | 3 | chromeMyAdmin.run(function( 4 | $rootScope, 5 | Events, 6 | ErrorLevel, 7 | mySQLClientService, 8 | $q, 9 | UIConstants, 10 | KeyCodes, 11 | pingService 12 | ) { 13 | "use strict"; 14 | 15 | $rootScope.connected = false; 16 | 17 | $rootScope.safeApply = function(fn) { 18 | var phase = this.$root.$$phase; 19 | if(phase === '$apply' || phase === '$digest') { 20 | if(fn && (typeof(fn) === 'function')) { 21 | fn(); 22 | } 23 | } else { 24 | this.$apply(fn); 25 | } 26 | }; 27 | 28 | $rootScope.fatalErrorOccurred = function(errorMessage) { 29 | $rootScope.$broadcast(Events.SHOW_ERROR_DIALOG, { 30 | errorLevel: ErrorLevel.FATAL, 31 | reason: errorMessage 32 | }); 33 | }; 34 | 35 | $rootScope.showErrorDialog = function(message, reason) { 36 | $rootScope.$broadcast(Events.SHOW_ERROR_DIALOG, { 37 | errorLevel: ErrorLevel.ERROR, 38 | message: message, 39 | reason: reason 40 | }); 41 | }; 42 | 43 | $rootScope.showCustomErrorDialog = function(title, message, reason) { 44 | $rootScope.$broadcast(Events.SHOW_ERROR_DIALOG, { 45 | errorLevel: ErrorLevel.ERROR, 46 | title: title, 47 | message: message, 48 | reason: reason 49 | }); 50 | }; 51 | 52 | $rootScope.notifyConnectionChanged = function(connectionInfo) { 53 | $rootScope.$broadcast(Events.CONNECTION_CHANGED, connectionInfo); 54 | }; 55 | 56 | $rootScope.notifyExecutingQuery = function(query) { 57 | $rootScope.$broadcast(Events.EXECUTING_QUERY, query); 58 | }; 59 | 60 | $rootScope.notifyQueryExecuted = function() { 61 | $rootScope.$broadcast(Events.QUERY_EXECUTED, null); 62 | }; 63 | 64 | $rootScope.showMainStatusMessage = function(message) { 65 | $rootScope.$broadcast(Events.SHOW_MAIN_STATUS_MESSAGE, message); 66 | }; 67 | 68 | $rootScope.showProgressBar = function() { 69 | $rootScope.$broadcast(Events.SHOW_PROGRESS_BAR, null); 70 | }; 71 | 72 | $rootScope.showChangeWindowPanel = function() { 73 | $rootScope.$broadcast(Events.SHOW_CHANGE_WINDOW_PANEL, null); 74 | }; 75 | 76 | $rootScope.hideProgressBar = function() { 77 | $rootScope.$broadcast(Events.HIDE_PROGRESS_BAR, null); 78 | }; 79 | 80 | $rootScope.callbackFromConfirmDialog = function(callbackEvent, result) { 81 | $rootScope.$broadcast(callbackEvent, result); 82 | }; 83 | 84 | $rootScope.showConfirmDialog = function( 85 | message, yesButtonLabel, noButtonLabel, callbackEvent, danger) { 86 | $rootScope.$broadcast(Events.SHOW_CONFIRM_DIALOG, { 87 | message: message, 88 | yesButtonLabel: yesButtonLabel, 89 | noButtonLabel: noButtonLabel, 90 | callbackEvent: callbackEvent, 91 | danger: danger 92 | }); 93 | }; 94 | 95 | $rootScope.showConfigurationDialog = function(activeTab) { 96 | $rootScope.$broadcast(Events.SHOW_CONFIGURATION_DIALOG, { 97 | activeTab: activeTab 98 | }); 99 | }; 100 | 101 | $rootScope.login = function() { 102 | $rootScope.$broadcast(Events.LOGIN, null); 103 | }; 104 | 105 | $rootScope.getDisplayValue = function(value) { 106 | if (value === null) { 107 | return "NULL"; 108 | } else { 109 | return value; 110 | } 111 | }; 112 | 113 | $rootScope.getDisplayValueClass = function(value) { 114 | if (value === null) { 115 | return "nullValueOnCell"; 116 | } else { 117 | return ""; 118 | } 119 | }; 120 | 121 | $rootScope.onKeyUp = function($event) { 122 | if ($event.keyCode === KeyCodes.F5) { 123 | $rootScope.$broadcast(Events.REQUEST_REFRESH, null); 124 | } 125 | }; 126 | 127 | var adjustMainPanelHeight = function() { 128 | $("#mainPanel").height( 129 | $(window).height() - 130 | UIConstants.WINDOW_TITLE_PANEL_HEIGHT - 131 | UIConstants.NAVBAR_HEIGHT - 132 | UIConstants.FOOTER_HEIGHT); 133 | }; 134 | 135 | var assignWindowResizeEventHandler = function() { 136 | $(window).resize(function(evt) { 137 | adjustMainPanelHeight(); 138 | }); 139 | }; 140 | 141 | assignWindowResizeEventHandler(); 142 | adjustMainPanelHeight(); 143 | }); 144 | 145 | chromeMyAdmin.filter("reverse", function() { 146 | "use strict"; 147 | 148 | return function(items) { 149 | return items.slice().reverse(); 150 | }; 151 | }); 152 | -------------------------------------------------------------------------------- /app/scripts/window/services/any_query_execute_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("anyQueryExecuteService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | return { 8 | showQueryPanel: function(query) { 9 | $rootScope.$broadcast(Events.SHOW_QUERY_PANEL, {query: query}); 10 | }, 11 | showAndExecuteQueryPanel: function(query, removeEmptyResult) { 12 | $rootScope.$broadcast(Events.SHOW_AND_EXECUTE_QUERY_PANEL, 13 | {query: query, removeEmptyResult: removeEmptyResult}); 14 | }, 15 | showFindSameRowsDialog: function() { 16 | $rootScope.$broadcast(Events.SHOW_FIND_SAME_ROWS_DIALOG, null); 17 | }, 18 | showFindRowsWithTheValueDialog: function() { 19 | $rootScope.$broadcast(Events.SHOW_FIND_ROWS_WITH_THE_VALUE_DIALOG, null); 20 | } 21 | }; 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /app/scripts/window/services/clipboard_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("clipboardService", function( 2 | $rootScope, 3 | columnTypeService 4 | ) { 5 | "use strict"; 6 | 7 | return { 8 | copyRow: function(queryResult, row) { 9 | var columnDefinitions = queryResult.columnDefinitions; 10 | var resultsetRows = queryResult.resultsetRows; 11 | var values = []; 12 | angular.forEach(columnDefinitions, function(column, index) { 13 | var value = row.entity["column" + index]; 14 | if (value) { 15 | if (columnTypeService.isNumeric(column.columnType)) { 16 | this.push(value); 17 | } else { 18 | this.push("\"" + value.replace(/"/g, "\"\"") + "\""); 19 | } 20 | } else { 21 | this.push(""); 22 | } 23 | }, values); 24 | var target = values.join(","); 25 | var copy = function(str, mimetype) { 26 | document.oncopy = function(event) { 27 | event.clipboardData.setData(mimetype, str); 28 | event.preventDefault(); 29 | document.oncopy = null; 30 | $rootScope.showMainStatusMessage("Copied the row to clipboard as CSV string."); 31 | }; 32 | document.execCommand("Copy", false, null); 33 | }; 34 | copy(target, "text/plain"); 35 | } 36 | }; 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /app/scripts/window/services/column_type_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("columnTypeService", function($rootScope) { 2 | "use strict"; 3 | 4 | var numericTypes = [0, 1, 2, 3, 4, 5, 8, 9, 246]; 5 | 6 | return { 7 | isNumeric: function(type) { 8 | return numericTypes.indexOf(type) > -1; 9 | } 10 | }; 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /app/scripts/window/services/connection_information_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("connectionInformationService", function( 2 | $rootScope, 3 | Events, 4 | mySQLClientService, 5 | mySQLQueryService, 6 | ) { 7 | "use strict"; 8 | 9 | var connectionId = null; 10 | 11 | $rootScope.$on(Events.CONNECTION_CHANGED, function(event, data) { 12 | if (mySQLClientService.isConnected()) { 13 | mySQLQueryService.getConnectionId().then(function(result) { 14 | connectionId = result.resultsetRows[0].values[0]; 15 | }, function(reason) { 16 | $rootScope.fatalErrorOccurred(reason); 17 | }); 18 | } else { 19 | connectionId = null; 20 | } 21 | }); 22 | 23 | return { 24 | getConnectionId: function() { 25 | return connectionId; 26 | } 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /app/scripts/window/services/favorite_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("favoriteService", function( 2 | $rootScope, 3 | $q, 4 | Events 5 | ) { 6 | "use strict"; 7 | 8 | var doSelect = function(name) { 9 | var deferred = $q.defer(); 10 | chrome.storage.local.get("favorites", function(items) { 11 | var favorites = items.favorites || {}; 12 | var result = favorites[name]; 13 | if (result) { 14 | result.name = name; 15 | $rootScope.$broadcast(Events.FAVORITE_SELECTED, result); 16 | deferred.resolve(result); 17 | } else { 18 | deferred.reject(); 19 | } 20 | }); 21 | return deferred.promise; 22 | }; 23 | 24 | return { 25 | set: function(name, hostName, port, userName, password, useSSL, caCert, checkCN, usePortForwarding, ssh2HostName, ssh2Port, ssh2AuthType, ssh2UserName, ssh2Password, ssh2PrivateKey) { 26 | var deferred = $q.defer(); 27 | chrome.storage.local.get("favorites", function(items) { 28 | var favorites = items.favorites || {}; 29 | var favorite = favorites[name] || {}; 30 | favorite.hostName = hostName; 31 | favorite.port = port; 32 | favorite.userName = userName; 33 | favorite.password = password; 34 | favorite.useSSL = useSSL; 35 | favorite.caCert = caCert; 36 | favorite.checkCN = checkCN; 37 | favorite.usePortForwarding = usePortForwarding; 38 | favorite.ssh2HostName = ssh2HostName; 39 | favorite.ssh2Port = ssh2Port; 40 | favorite.ssh2AuthType = ssh2AuthType; 41 | favorite.ssh2UserName = ssh2UserName; 42 | favorite.ssh2Password = ssh2Password; 43 | favorite.ssh2PrivateKey = ssh2PrivateKey; 44 | favorites[name] = favorite; 45 | chrome.storage.local.set({favorites: favorites}, function() { 46 | $rootScope.$broadcast(Events.FAVORITES_CHANGED, favorites); 47 | deferred.resolve(); 48 | }); 49 | }); 50 | return deferred.promise; 51 | }, 52 | select: function(name) { 53 | return doSelect(name); 54 | }, 55 | selectAndLogin: function(name) { 56 | doSelect(name).then(function(result) { 57 | $rootScope.login(); 58 | }); 59 | }, 60 | getAll: function() { 61 | var deferred = $q.defer(); 62 | chrome.storage.local.get("favorites", function(items) { 63 | var favorites = items.favorites || {}; 64 | deferred.resolve(favorites); 65 | }); 66 | return deferred.promise; 67 | }, 68 | delete: function(name) { 69 | var deferred = $q.defer(); 70 | chrome.storage.local.get("favorites", function(items) { 71 | var favorites = items.favorites || {}; 72 | delete favorites[name]; 73 | chrome.storage.local.set({favorites: favorites}, function() { 74 | $rootScope.$broadcast(Events.FAVORITES_CHANGED, favorites); 75 | deferred.resolve(); 76 | }); 77 | }); 78 | return deferred.promise; 79 | } 80 | }; 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /app/scripts/window/services/identity_keep_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("identityKeepService", function( 2 | $rootScope, 3 | $q 4 | ) { 5 | "use strict"; 6 | 7 | var getKeyLength = function(map) { 8 | var len = 0; 9 | for (var key in map) { 10 | len++; 11 | } 12 | return len; 13 | }; 14 | 15 | return { 16 | set: function(hostName, port, userName, password) { 17 | var deferred = $q.defer(); 18 | chrome.storage.local.get("identities", function(items) { 19 | var identities = items.identities || {}; 20 | if (getKeyLength(identities) >= 10) { 21 | var minKey = null; 22 | for (var k in identities) { 23 | if (minKey) { 24 | var obj = identities[k]; 25 | var prev = identities[minKey]; 26 | if (obj.created < prev.created) { 27 | minKey = k; 28 | } 29 | } 30 | } 31 | if (minKey) { 32 | delete identities[minKey]; 33 | } 34 | } 35 | var key = hostName + ":" + port + ":" + userName; 36 | var identity = identities[key] || {}; 37 | identity.password = password; 38 | identity.created = (new Date()).getTime(); 39 | identities[key] = identity; 40 | chrome.storage.local.set({identities: identities}, function() { 41 | deferred.resolve(); 42 | }); 43 | }); 44 | return deferred.promise; 45 | }, 46 | get: function(hostName, port, userName) { 47 | var deferred = $q.defer(); 48 | chrome.storage.local.get("identities", function(items) { 49 | var identities = items.identities || {}; 50 | var key = hostName + ":" + port + ":" + userName; 51 | var result = identities[key]; 52 | if (result) { 53 | deferred.resolve(result); 54 | } else { 55 | deferred.reject(); 56 | } 57 | }); 58 | return deferred.promise; 59 | } 60 | }; 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /app/scripts/window/services/mode_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("modeService", function( 2 | $rootScope, 3 | Events, 4 | Modes 5 | ) { 6 | "use strict"; 7 | 8 | var mode = Modes.DATABASE; 9 | 10 | return { 11 | changeMode: function(newMode) { 12 | mode = newMode; 13 | $rootScope.$broadcast(Events.MODE_CHANGED, mode); 14 | }, 15 | getMode: function() { 16 | return mode; 17 | } 18 | }; 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /app/scripts/window/services/ping_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("pingService", function( 2 | $rootScope, 3 | Events, 4 | $timeout, 5 | mySQLClientService, 6 | Configurations 7 | ) { 8 | "use strict"; 9 | 10 | var autoPingPromise = null; 11 | 12 | var ping = function() { 13 | mySQLClientService.ping().then(function() { 14 | startTimer(); 15 | }, function(reason) { 16 | $rootScope.fatalErrorOccurred("MySQL server connection lost."); 17 | }); 18 | }; 19 | 20 | var startTimer = function() { 21 | autoPingPromise = $timeout(ping, Configurations.DEFAULT_PING_SPAN); 22 | }; 23 | 24 | $rootScope.$on(Events.CONNECTION_CHANGED, function(event, data) { 25 | if (mySQLClientService.isConnected()) { 26 | startTimer(); 27 | } else { 28 | $timeout.cancel(autoPingPromise); 29 | } 30 | }); 31 | 32 | return { 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /app/scripts/window/services/query_history_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("queryHistoryService", function( 2 | $rootScope, 3 | $q, 4 | Events 5 | ) { 6 | "use strict"; 7 | 8 | var trim = function(str) { 9 | return str.replace(/^[  \t\r\n]+|[  \t\r\n]+$/g, ""); 10 | }; 11 | 12 | return { 13 | add: function(query) { 14 | var trimed = trim(query); 15 | var deferred = $q.defer(); 16 | chrome.storage.local.get("queries", function(items) { 17 | var queries = items.queries || []; 18 | var exists = false; 19 | angular.forEach(queries, function(target) { 20 | if (target === trimed) { 21 | exists = true; 22 | } 23 | }); 24 | if (!exists) { 25 | queries.push(trimed); 26 | if (queries.length > 30) { 27 | queries = queries.slice(-30); 28 | } 29 | chrome.storage.local.set({queries: queries}, function() { 30 | deferred.resolve(); 31 | }); 32 | } else { 33 | deferred.resolve(); 34 | } 35 | }); 36 | return deferred.promise; 37 | }, 38 | getAll: function() { 39 | var deferred = $q.defer(); 40 | chrome.storage.local.get("queries", function(items) { 41 | var queries = items.queries || []; 42 | deferred.resolve(queries); 43 | }); 44 | return deferred.promise; 45 | } 46 | }; 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /app/scripts/window/services/query_selection_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("querySelectionService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var queryResult = null; 8 | var selectedRows = null; 9 | 10 | return { 11 | resetQueryResult: function() { 12 | queryResult = null; 13 | }, 14 | resetSelectedRows: function() { 15 | selectedRows = null; 16 | }, 17 | setQueryResult: function(result) { 18 | queryResult = result; 19 | }, 20 | getQueryResult: function() { 21 | return queryResult; 22 | }, 23 | exportQueryResult: function() { 24 | $rootScope.$broadcast(Events.EXPORT_QUERY_RESULT, null); 25 | }, 26 | setSelectedRows: function(row) { 27 | selectedRows = row; 28 | }, 29 | getSelectedRows: function() { 30 | return selectedRows; 31 | }, 32 | copyRowToClipboard: function() { 33 | $rootScope.$broadcast(Events.COPY_QUERY_RESULT_ROW_TO_CLIPBOARD, selectedRows); 34 | } 35 | }; 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /app/scripts/window/services/relation_selection_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("relationSelectionService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var selectedRelation = null; 8 | 9 | return { 10 | reset: function() { 11 | selectedRelation = null; 12 | }, 13 | setSelectedRelation: function(newSelectedRelation) { 14 | selectedRelation = newSelectedRelation; 15 | $rootScope.$broadcast(Events.RELATION_SELECTION_CHANGED, selectedRelation); 16 | }, 17 | getSelectedRelation: function() { 18 | return selectedRelation; 19 | } 20 | }; 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /app/scripts/window/services/relation_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("relationService", function( 2 | $q, 3 | $rootScope, 4 | mySQLQueryService 5 | ) { 6 | "use strict"; 7 | 8 | var parseForeignKeysFromCreateTableDdl = function(ddl) { 9 | var result = []; 10 | var lines = ddl.split("\n"); 11 | angular.forEach(lines, function(line) { 12 | line = line.trim(); 13 | if (line.indexOf("CONSTRAINT") !== -1 && 14 | line.indexOf("FOREIGN KEY") !== -1) { 15 | var divided = divideWords(line); 16 | var onDelete = ""; 17 | var onUpdate = ""; 18 | for (var i = 0; i < divided.length; i++) { 19 | if (divided[i] === "ON") { 20 | var operation = divided[i + 1]; 21 | for (var j = i + 2; j < divided.length; j++) { 22 | if (divided[j] !== "ON") { 23 | if (operation === "DELETE") { 24 | onDelete += " " + divided[j]; 25 | } else if (operation === "UPDATE") { 26 | onUpdate += " " + divided[j]; 27 | } 28 | } else { 29 | break; 30 | } 31 | } 32 | i = j - 1; 33 | } 34 | } 35 | var constraint = { 36 | name: divided[1], 37 | column: divided[4].substring(1, divided[4].length - 1), 38 | fkTable: divided[6], 39 | fkColumn: divided[7].substring(1, divided[7].length - 1), 40 | onDelete: onDelete, 41 | onUpdate: onUpdate 42 | }; 43 | result.push(constraint); 44 | } 45 | }); 46 | return result; 47 | }; 48 | 49 | var divideWords = function(line) { 50 | var inStr = false; 51 | var result = []; 52 | var tmp = ""; 53 | for (var i = 0; i < line.length; i++) { 54 | var c = line.charAt(i); 55 | if (c === " " && !inStr) { 56 | result.push(tmp); 57 | tmp = ""; 58 | } else if (c === "`") { 59 | inStr = !inStr; 60 | } else { 61 | tmp += c; 62 | } 63 | } 64 | if (tmp.charAt(tmp.length - 1) === ",") { 65 | tmp = tmp.substring(0, tmp.length - 1); 66 | } 67 | result.push(tmp); 68 | return result; 69 | }; 70 | 71 | return { 72 | getRelations: function(table) { 73 | var deferred = $q.defer(); 74 | mySQLQueryService.showCreateTable(table).then(function(result) { 75 | var relations = parseForeignKeysFromCreateTableDdl(result.ddl); 76 | deferred.resolve(relations); 77 | }, function(reason) { 78 | deferred.reject(reason); 79 | }); 80 | return deferred.promise; 81 | } 82 | }; 83 | 84 | }); 85 | -------------------------------------------------------------------------------- /app/scripts/window/services/routine_selection_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("routineSelectionService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var selectedRoutine = null; 8 | 9 | return { 10 | reset: function() { 11 | selectedRoutine = null; 12 | $rootScope.$broadcast(Events.ROUTINE_SELECTION_CHANGED, selectedRoutine); 13 | }, 14 | setSelectedRoutine: function(newSelectedRoutine) { 15 | selectedRoutine = newSelectedRoutine; 16 | $rootScope.$broadcast(Events.ROUTINE_SELECTION_CHANGED, selectedRoutine); 17 | }, 18 | getSelectedRoutine: function() { 19 | return selectedRoutine; 20 | }, 21 | showCreateRoutineDialog: function() { 22 | $rootScope.$broadcast(Events.SHOW_CREATE_ROUTINE_DIALOG, null); 23 | }, 24 | executeSelectedProcedure: function() { 25 | $rootScope.$broadcast(Events.EXECUTE_SELECTED_PROCEDURE, selectedRoutine); 26 | } 27 | }; 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /app/scripts/window/services/rows_paging_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("rowsPagingService", function( 2 | $rootScope, 3 | Events, 4 | configurationService, 5 | Configurations 6 | ) { 7 | "use strict"; 8 | 9 | var rowCountPerPage = 100; 10 | var currentPageIndex = 0; 11 | var totalRowCount = 0; 12 | 13 | configurationService.addConfigurationChangeListener(function(name, value) { 14 | if (name === Configurations.ROW_COUNT_PER_PAGE_IN_ROWS_PANEL) { 15 | rowCountPerPage = value; 16 | } 17 | }); 18 | 19 | configurationService.getRowCountPerPageInRowsPanel().then(function(rowCount) { 20 | rowCountPerPage = rowCount; 21 | }); 22 | 23 | return { 24 | reset: function() { 25 | currentPageIndex = 0; 26 | totalRowCount = 0; 27 | }, 28 | updateTotalRowCount: function(newTotalRowCount) { 29 | totalRowCount = newTotalRowCount; 30 | }, 31 | hasPrevious: function() { 32 | var previousPageIndex = currentPageIndex - 1; 33 | return previousPageIndex >= 0; 34 | }, 35 | hasNext: function() { 36 | var nextPageIndex = currentPageIndex + 1; 37 | var start = nextPageIndex * rowCountPerPage; 38 | return (totalRowCount - 1) >= start; 39 | }, 40 | next: function() { 41 | currentPageIndex++; 42 | $rootScope.$broadcast(Events.ROWS_PAGING_CHANGED, currentPageIndex); 43 | }, 44 | previous: function() { 45 | if (currentPageIndex > 0) { 46 | currentPageIndex--; 47 | $rootScope.$broadcast(Events.ROWS_PAGING_CHANGED, currentPageIndex); 48 | } 49 | }, 50 | current: function() { 51 | var start = currentPageIndex * rowCountPerPage; 52 | return { 53 | offset: start, 54 | count: rowCountPerPage 55 | }; 56 | }, 57 | getTotalPageCount: function() { 58 | return Math.ceil(totalRowCount / rowCountPerPage); 59 | }, 60 | getCurrentPageIndex: function() { 61 | return currentPageIndex; 62 | }, 63 | refresh: function() { 64 | $rootScope.$broadcast(Events.ROWS_PAGING_CHANGED, currentPageIndex); 65 | } 66 | }; 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /app/scripts/window/services/rows_selection_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("rowsSelectionService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var selectedRow = null; 8 | 9 | return { 10 | reset: function() { 11 | selectedRow = null; 12 | }, 13 | setSelectedRows: function(newSelectedRow) { 14 | selectedRow = newSelectedRow; 15 | $rootScope.$broadcast(Events.ROWS_SELECTION_CHANGED, selectedRow); 16 | }, 17 | getSelectedRows: function() { 18 | return selectedRow; 19 | }, 20 | copyRowToClipboard: function() { 21 | $rootScope.$broadcast(Events.COPY_ROWS_PANEL_ROW_TO_CLIPBOARD, selectedRow); 22 | } 23 | }; 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /app/scripts/window/services/sql_expression_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("sqlExpressionService", function( 2 | $rootScope, 3 | ValueTypes 4 | ) { 5 | "use strict"; 6 | 7 | var createEqualRightExpression = function(value) { 8 | if (value !== null) { 9 | return "='" + _replaceValue(value) + "'"; 10 | } else { 11 | return " IS NULL"; 12 | } 13 | }; 14 | 15 | var _replaceValue = function(value) { 16 | return value.replace(/'/g, "''").replace(/\\/g, "\\\\"); 17 | }; 18 | 19 | return { 20 | getPrimaryKeyColumns: function(columnDefinitions) { 21 | var primaryKeyColumns = {}; 22 | angular.forEach(columnDefinitions, function(columnDefinition, index) { 23 | if (columnDefinition.isPrimaryKey()) { 24 | this[index] = columnDefinition; 25 | } 26 | }, primaryKeyColumns); 27 | return primaryKeyColumns; 28 | }, 29 | createWhereConditionsForUpdate: function( 30 | primaryKeyColumns, columnDefinitions, originalRow) { 31 | var whereConditions = []; 32 | if (jQuery.isEmptyObject(primaryKeyColumns)) { 33 | angular.forEach(columnDefinitions, function(columnDefinition, index) { 34 | var condition = 35 | "`" + columnDefinition.name + "`" + 36 | createEqualRightExpression(originalRow.values[index]); 37 | this.push(condition); 38 | }, whereConditions); 39 | } else { 40 | angular.forEach(primaryKeyColumns, function(primaryKeyColumn, index) { 41 | var condition = 42 | "`" + primaryKeyColumn.name + "`" + 43 | createEqualRightExpression(originalRow.values[index]); 44 | this.push(condition); 45 | }, whereConditions); 46 | } 47 | return whereConditions; 48 | }, 49 | createInsertStatement: function(table, values, valueTypes) { 50 | var targetColumns = []; 51 | var targetValues = []; 52 | angular.forEach(values, function(value, columnName) { 53 | var valueType = valueTypes[columnName]; 54 | if (valueType === ValueTypes.NULL) { 55 | targetColumns.push("`" + columnName + "`"); 56 | targetValues.push("NULL"); 57 | } else if (valueType === ValueTypes.DEFAULT) { 58 | // Exclude this column 59 | } else if (value != null) { 60 | targetColumns.push("`" + columnName + "`"); 61 | if (valueType === ValueTypes.VALUE) { 62 | targetValues.push("'" + _replaceValue(value) + "'"); 63 | } else if (valueType === ValueTypes.EXPRESSION) { 64 | targetValues.push(value); 65 | } 66 | } 67 | }); 68 | if (targetColumns.length !== 0) { 69 | var sql = "INSERT INTO `" + table + "` ("; 70 | sql += targetColumns.join(", "); 71 | sql += ") VALUES ("; 72 | sql += targetValues.join(", "); 73 | sql += ")"; 74 | return sql; 75 | } else { 76 | return null; 77 | } 78 | }, 79 | replaceValue: function(value) { 80 | return _replaceValue(value); 81 | }, 82 | createStringArray: function(values) { 83 | var tmp = []; 84 | angular.forEach(values, function(value) { 85 | tmp.push("\"" + _replaceValue(value) + "\""); 86 | }, tmp); 87 | return tmp.join(", "); 88 | } 89 | }; 90 | }); 91 | -------------------------------------------------------------------------------- /app/scripts/window/services/ssh2_known_host_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("ssh2KnownHostService", function( 2 | $rootScope, 3 | $q 4 | ) { 5 | "use strict"; 6 | 7 | var lastChecked = null; 8 | 9 | return { 10 | check: function(hostName, port, method, fingerprint) { 11 | lastChecked = { 12 | hostName: hostName, 13 | port: port, 14 | method: method, 15 | fingerprint: fingerprint 16 | }; 17 | var deferred = $q.defer(); 18 | chrome.storage.local.get("knownHosts", function(items) { 19 | var knownHosts = items.knownHosts || {}; 20 | var key = hostName + ":" + port; 21 | var knownHost = knownHosts[key]; 22 | if (knownHost) { 23 | if (knownHost.method === method && 24 | knownHost.fingerprint === fingerprint) { 25 | deferred.resolve({ 26 | result: "found" 27 | }); 28 | } else { 29 | deferred.resolve({ 30 | result: "not_same", 31 | method: knownHost.method, 32 | fingerprint: knownHost.fingerprint 33 | }); 34 | } 35 | } else { 36 | deferred.resolve({ 37 | result: "not_found" 38 | }); 39 | } 40 | }); 41 | return deferred.promise; 42 | }, 43 | addLastChecked: function() { 44 | var deferred = $q.defer(); 45 | chrome.storage.local.get("knownHosts", function(items) { 46 | var knownHosts = items.knownHosts || {}; 47 | var key = lastChecked.hostName + ":" + lastChecked.port; 48 | var value = { 49 | method: lastChecked.method, 50 | fingerprint: lastChecked.fingerprint 51 | }; 52 | knownHosts[key] = value; 53 | chrome.storage.local.set({knownHosts: knownHosts}, function() { 54 | deferred.resolve(); 55 | }); 56 | }); 57 | return deferred.promise; 58 | }, 59 | getAll: function() { 60 | var deferred = $q.defer(); 61 | chrome.storage.local.get("knownHosts", function(items) { 62 | var knownHosts = items.knownHosts || {}; 63 | deferred.resolve(knownHosts); 64 | }); 65 | return deferred.promise; 66 | } 67 | }; 68 | }); 69 | -------------------------------------------------------------------------------- /app/scripts/window/services/ssh2_port_forwarding_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("ssh2PortForwardingService", function( 2 | $rootScope, 3 | $q 4 | ) { 5 | "use strict"; 6 | 7 | var deferred; 8 | 9 | var listener = document.querySelector("#listener"); 10 | listener.addEventListener("message", function(evt) { 11 | if (deferred) { 12 | $rootScope.hideProgressBar(); 13 | var result = JSON.parse(evt.data); 14 | if (result.message === "error") { 15 | deferred.reject(result); 16 | } else { 17 | deferred.resolve(result); 18 | } 19 | deferred = null; 20 | } 21 | }, true); 22 | 23 | return { 24 | connect: function(ssh2ServerHostname, ssh2ServerPort) { 25 | deferred = $q.defer(); 26 | $rootScope.showProgressBar(); 27 | var module = document.querySelector("#ssh2_port_forwarding"); 28 | var obj = { 29 | command: "connect", 30 | args: [ 31 | ssh2ServerHostname, 32 | ssh2ServerPort 33 | ] 34 | }; 35 | module.postMessage(JSON.stringify(obj)); 36 | return deferred.promise; 37 | }, 38 | portForwarding: function(ssh2AuthType, ssh2Username, ssh2Password, serverHostname, serverPort, privateKey) { 39 | deferred = $q.defer(); 40 | $rootScope.showProgressBar(); 41 | var module = document.querySelector("#ssh2_port_forwarding"); 42 | var obj = { 43 | command: "forward", 44 | args: [ 45 | ssh2AuthType, 46 | ssh2Username, 47 | ssh2Password, 48 | serverHostname, 49 | serverPort, 50 | privateKey || "" 51 | ] 52 | }; 53 | module.postMessage(JSON.stringify(obj)); 54 | return deferred.promise; 55 | } 56 | }; 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /app/scripts/window/services/target_object_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("targetObjectService", function( 2 | $rootScope, 3 | Events 4 | ) { 5 | "use strict"; 6 | 7 | var database = null; 8 | var table = null; 9 | 10 | var _changeDatabase = function(newDatabase) { 11 | database = newDatabase; 12 | if (database) { 13 | $rootScope.$broadcast(Events.DATABASE_CHANGED, database); 14 | } 15 | }; 16 | 17 | var _changeTable = function(newTable) { 18 | table = newTable; 19 | _fireTableChangedEvent(table); 20 | }; 21 | 22 | var _fireTableChangedEvent = function(table) { 23 | $rootScope.$broadcast(Events.TABLE_CHANGED, table); 24 | }; 25 | 26 | return { 27 | changeDatabase: function(newDatabase) { 28 | _changeDatabase(newDatabase); 29 | }, 30 | resetDatabase: function() { 31 | _changeDatabase(null); 32 | }, 33 | getDatabase: function() { 34 | return database; 35 | }, 36 | changeTable: function(newTable) { 37 | _changeTable(newTable); 38 | }, 39 | resetTable: function() { 40 | _changeTable(null); 41 | }, 42 | getTable: function() { 43 | return table; 44 | }, 45 | requestInsertRow: function() { 46 | $rootScope.$broadcast(Events.REQUEST_INSERT_ROW, table); 47 | }, 48 | requestUpdateRow: function() { 49 | $rootScope.$broadcast(Events.REQUEST_UPDATE_ROW, table); 50 | }, 51 | requestCloneRow: function() { 52 | $rootScope.$broadcast(Events.REQUEST_CLONE_ROW, table); 53 | }, 54 | showInsertRowDialog: function(columnDefinitions) { 55 | $rootScope.$broadcast(Events.SHOW_INSERT_ROW_DIALOG, columnDefinitions); 56 | }, 57 | showCloneRowDialog: function(data) { 58 | $rootScope.$broadcast(Events.SHOW_CLONE_ROW_DIALOG, data); 59 | }, 60 | showUpdateRowDialog: function(data) { 61 | $rootScope.$broadcast(Events.SHOW_UPDATE_ROW_DIALOG, data); 62 | }, 63 | reSelectTable: function() { 64 | _fireTableChangedEvent(table); 65 | }, 66 | refreshTableList: function() { 67 | $rootScope.$broadcast(Events.REFRESH_TABLE_LIST, database); 68 | }, 69 | requestDropSelectedTable: function() { 70 | $rootScope.$broadcast(Events.REQUEST_DROP_TABLE, table); 71 | }, 72 | showAddColumnDialog: function() { 73 | $rootScope.$broadcast(Events.SHOW_ADD_COLUMN_DIALOG, table); 74 | }, 75 | showAddIndexDialog: function(columnNames) { 76 | $rootScope.$broadcast(Events.SHOW_ADD_INDEX_DIALOG, { 77 | table: table, 78 | columns: columnNames 79 | }); 80 | }, 81 | showEditColumnDialog: function(columnDefs, columnStructure) { 82 | $rootScope.$broadcast(Events.SHOW_EDIT_COLUMN_DIALOG, { 83 | table: table, 84 | columnDefs: columnDefs, 85 | columnStructure: columnStructure 86 | }); 87 | }, 88 | showCreateDatabaseDialog: function() { 89 | $rootScope.$broadcast(Events.SHOW_CREATE_DATABASE_DIALOG, null); 90 | }, 91 | refreshDatabases: function() { 92 | $rootScope.$broadcast(Events.REFRESH_DATABASES, null); 93 | }, 94 | showAddRelationDialog: function(table) { 95 | $rootScope.$broadcast(Events.SHOW_ADD_RELATION_DIALOG, { 96 | table: table 97 | }); 98 | }, 99 | refreshProceduresFunctions: function() { 100 | $rootScope.$broadcast(Events.REFRESH_PROCEDURES_FUNCTIONS, null); 101 | }, 102 | refreshErDiagram: function() { 103 | $rootScope.$broadcast(Events.REFRESH_ER_DIAGRAM, null); 104 | }, 105 | saveErDiagramImage: function() { 106 | $rootScope.$broadcast(Events.SAVE_ER_DIAGRAM_IMAGE, null); 107 | }, 108 | exportAllDatabases: function() { 109 | $rootScope.$broadcast(Events.EXPORT_ALL_DATABASES, null); 110 | } 111 | }; 112 | 113 | }); 114 | -------------------------------------------------------------------------------- /app/scripts/window/services/type_service.js: -------------------------------------------------------------------------------- 1 | chromeMyAdmin.factory("typeService", function( 2 | $rootScope, 3 | TypeMap 4 | ) { 5 | "use strict"; 6 | 7 | var types = []; 8 | 9 | for (var type in TypeMap) { 10 | types.push(type); 11 | } 12 | 13 | return { 14 | getTypes: function() { 15 | return types; 16 | }, 17 | isString: function(type) { 18 | var info = TypeMap[type]; 19 | return info.isString; 20 | }, 21 | isNumeric: function(type) { 22 | var info = TypeMap[type]; 23 | return info.isNumeric; 24 | }, 25 | isDateTime: function(type) { 26 | var info = TypeMap[type]; 27 | return info.isDateTime; 28 | } 29 | }; 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /app/styles/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], 6 | .ng-cloak, .x-ng-cloak, 7 | .ng-hide:not(.ng-hide-animate) { 8 | display: none !important; 9 | } 10 | 11 | ng\:form { 12 | display: block; 13 | } 14 | -------------------------------------------------------------------------------- /app/styles/jquery.jqplot.min.css: -------------------------------------------------------------------------------- 1 | .jqplot-target{position:relative;color:#666;font-family:"Trebuchet MS",Arial,Helvetica,sans-serif;font-size:1em}.jqplot-axis{font-size:.75em}.jqplot-xaxis{margin-top:10px}.jqplot-x2axis{margin-bottom:10px}.jqplot-yaxis{margin-right:10px}.jqplot-y2axis,.jqplot-y3axis,.jqplot-y4axis,.jqplot-y5axis,.jqplot-y6axis,.jqplot-y7axis,.jqplot-y8axis,.jqplot-y9axis,.jqplot-yMidAxis{margin-left:10px;margin-right:10px}.jqplot-axis-tick,.jqplot-xaxis-tick,.jqplot-yaxis-tick,.jqplot-x2axis-tick,.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick,.jqplot-yMidAxis-tick{position:absolute;white-space:pre}.jqplot-xaxis-tick{top:0;left:15px;vertical-align:top}.jqplot-x2axis-tick{bottom:0;left:15px;vertical-align:bottom}.jqplot-yaxis-tick{right:0;top:15px;text-align:right}.jqplot-yaxis-tick.jqplot-breakTick{right:-20px;margin-right:0;padding:1px 5px 1px 5px;z-index:2;font-size:1.5em}.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick{left:0;top:15px;text-align:left}.jqplot-yMidAxis-tick{text-align:center;white-space:nowrap}.jqplot-xaxis-label{margin-top:10px;font-size:11pt;position:absolute}.jqplot-x2axis-label{margin-bottom:10px;font-size:11pt;position:absolute}.jqplot-yaxis-label{margin-right:10px;font-size:11pt;position:absolute}.jqplot-yMidAxis-label{font-size:11pt;position:absolute}.jqplot-y2axis-label,.jqplot-y3axis-label,.jqplot-y4axis-label,.jqplot-y5axis-label,.jqplot-y6axis-label,.jqplot-y7axis-label,.jqplot-y8axis-label,.jqplot-y9axis-label{font-size:11pt;margin-left:10px;position:absolute}.jqplot-meterGauge-tick{font-size:.75em;color:#999}.jqplot-meterGauge-label{font-size:1em;color:#999}table.jqplot-table-legend{margin-top:12px;margin-bottom:12px;margin-left:12px;margin-right:12px}table.jqplot-table-legend,table.jqplot-cursor-legend{background-color:rgba(255,255,255,0.6);border:1px solid #ccc;position:absolute;font-size:.75em}td.jqplot-table-legend{vertical-align:middle}td.jqplot-seriesToggle:hover,td.jqplot-seriesToggle:active{cursor:pointer}.jqplot-table-legend .jqplot-series-hidden{text-decoration:line-through}div.jqplot-table-legend-swatch-outline{border:1px solid #ccc;padding:1px}div.jqplot-table-legend-swatch{width:0;height:0;border-top-width:5px;border-bottom-width:5px;border-left-width:6px;border-right-width:6px;border-top-style:solid;border-bottom-style:solid;border-left-style:solid;border-right-style:solid}.jqplot-title{top:0;left:0;padding-bottom:.5em;font-size:1.2em}table.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em}.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px}.jqplot-highlighter-tooltip,.jqplot-canvasOverlay-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,0.5);padding:1px}.jqplot-point-label{font-size:.75em;z-index:2}td.jqplot-cursor-legend-swatch{vertical-align:middle;text-align:center}div.jqplot-cursor-legend-swatch{width:1.2em;height:.7em}.jqplot-error{text-align:center}.jqplot-error-message{position:relative;top:46%;display:inline-block}div.jqplot-bubble-label{font-size:.8em;padding-left:2px;padding-right:2px;color:rgb(20%,20%,20%)}div.jqplot-bubble-label.jqplot-bubble-label-highlight{background:rgba(90%,90%,90%,0.7)}div.jqplot-noData-container{text-align:center;background-color:rgba(96%,96%,96%,0.3)} -------------------------------------------------------------------------------- /app/templates/add_index_dialog.html: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /app/templates/add_relation_dialog.html: -------------------------------------------------------------------------------- 1 | 69 | -------------------------------------------------------------------------------- /app/templates/change_window_panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 |
8 | -------------------------------------------------------------------------------- /app/templates/confirm_dialog.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/templates/create_database_dialog.html: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /app/templates/create_table_dialog.html: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /app/templates/database_object_list_panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |
7 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/templates/database_panel.html: -------------------------------------------------------------------------------- 1 |
2 |
{{statistics}}
3 |
4 |
5 |
6 |
7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /app/templates/er_diagram.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /app/templates/er_diagram_panel.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 8 |
9 | -------------------------------------------------------------------------------- /app/templates/error_dialog.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/templates/favorite_list_panel.html: -------------------------------------------------------------------------------- 1 |
2 | 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /app/templates/find_rows_with_the_value_dialog.html: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /app/templates/find_same_rows_dialog.html: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /app/templates/footer_button.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /app/templates/information_panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
{{tableStatus_Rows}}
6 |
7 |
{{tableStatus_Row_format}}
8 |
9 |
10 |
11 |
{{tableStatus_Avg_row_length}}
12 |
13 |
{{tableStatus_Data_length}}
14 |
15 |
16 |
17 |
{{tableStatus_Max_data_length}}
18 |
19 |
{{tableStatus_Index_length}}
20 |
21 |
22 |
23 |
{{tableStatus_Max_data_length}}
24 |
25 |
26 |
27 |
{{tableStatus_Create_time}}
28 |
29 |
30 |
31 |
{{tableStatus_Update_time}}
32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 | 51 |
52 |
53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /app/templates/insert_row_dialog.html: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /app/templates/main_footer.html: -------------------------------------------------------------------------------- 1 |
2 |
{{mainStatusMessage}}
3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
{{getPagingInfo()}}
15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 | 48 |
49 | 50 |
51 | 52 | 53 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /app/templates/navbar_menu_item.html: -------------------------------------------------------------------------------- 1 |
  • 2 | -------------------------------------------------------------------------------- /app/templates/navbar_panel.html: -------------------------------------------------------------------------------- 1 | 36 | -------------------------------------------------------------------------------- /app/templates/procs_funcs_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | -------------------------------------------------------------------------------- /app/templates/progress_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    Now communicating with MySQL server...
    5 |
    {{query}}
    6 |
    7 | 10 |
    11 |
    12 | -------------------------------------------------------------------------------- /app/templates/query_panel.html: -------------------------------------------------------------------------------- 1 |
    2 | 23 |
    24 | 25 |
    26 | 37 | 38 |
    {{selectedQuery}}
    39 |
    40 | 41 |
    42 |
    43 |
    {{queryErrorMessage}}
    44 |
    45 | -------------------------------------------------------------------------------- /app/templates/relation_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | -------------------------------------------------------------------------------- /app/templates/rows_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 | 6 |
    7 | 8 | 9 | 10 | 11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    17 | -------------------------------------------------------------------------------- /app/templates/status_graph.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | {{statusName}} ({{graphType}}) 4 | 5 |
    6 |
    7 |
    8 |
    9 |
    10 | -------------------------------------------------------------------------------- /app/templates/status_graph_panel.html: -------------------------------------------------------------------------------- 1 |
    2 | 29 |
    30 | 31 |
    32 |
    33 | -------------------------------------------------------------------------------- /app/templates/structure_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | 7 |
    8 |
    9 | 10 | 11 | 12 |
    13 |
    14 |
    Indexes
    15 |
    16 |
    17 |
    18 |
    19 | 20 | 21 |
    22 |
    23 |
    24 | -------------------------------------------------------------------------------- /app/templates/update_row_dialog.html: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /app/templates/window_title_panel.html: -------------------------------------------------------------------------------- 1 |
    2 |
    {{titleText}}
    3 | 4 |
    5 |
    6 |
    7 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-mysql-admin", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "jquery": "~2.1.1", 6 | "angular": "~1.3.0", 7 | "angular-ui-ace": "~0.1.1" 8 | }, 9 | "devDependencies": {} 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-mysql-admin", 3 | "version": "0.0.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "git@github.com:yoichiro/chrome_mysql_admin.git" 7 | }, 8 | "license": "Apache-2.0", 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "grunt": "~1.0.4", 12 | "grunt-bower-task": "^0.5.0", 13 | "grunt-chrome-manifest": "~0.3.0", 14 | "grunt-concurrent": "^2.3.1", 15 | "grunt-contrib-clean": "^2.0.0", 16 | "grunt-contrib-compass": "^1.1.1", 17 | "grunt-contrib-compress": "^1.4.3", 18 | "grunt-contrib-concat": "^1.0.1", 19 | "grunt-contrib-connect": "^2.0.0", 20 | "grunt-contrib-copy": "^1.0.0", 21 | "grunt-contrib-cssmin": "^3.0.0", 22 | "grunt-contrib-htmlmin": "^3.1.0", 23 | "grunt-contrib-jshint": "^2.1.0", 24 | "grunt-contrib-uglify": "^4.0.1", 25 | "grunt-contrib-watch": "^1.1.0", 26 | "grunt-mocha": "^1.2.0", 27 | "grunt-open": "~0.2.4", 28 | "grunt-preprocess": "~5.1.0", 29 | "grunt-shell": "^3.0.1", 30 | "grunt-svgmin": "^6.0.0", 31 | "grunt-usemin": "^3.1.1", 32 | "grunt-ng-annotate": "^3.0.0", 33 | "jshint-stylish": "^2.2.1", 34 | "load-grunt-tasks": "^4.0.0", 35 | "time-grunt": "^2.0.0" 36 | }, 37 | "engines": { 38 | "node": ">=0.8.0" 39 | }, 40 | "scripts": { 41 | "build": "./node_modules/.bin/grunt" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /test/app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "description": "__MSG_appDescription__", 4 | "version": "1", 5 | "manifest_version": 2, 6 | "default_locale": "en", 7 | "permissions": [ 8 | { 9 | "socket": [ 10 | "tcp-listen:*:*", 11 | "tcp-connect:*:*" 12 | ] 13 | }, 14 | "storage", 15 | "" 16 | ] 17 | } -------------------------------------------------------------------------------- /test/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-mysql-admin", 3 | "private": true, 4 | "dependencies": { 5 | "chai": "~1.8.0", 6 | "mocha": "~1.14.0" 7 | }, 8 | "devDependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /test/bower_components/chai/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chai", 3 | "version": "1.8.1", 4 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", 5 | "license": "MIT", 6 | "keywords": [ 7 | "test", 8 | "assertion", 9 | "assert", 10 | "testing", 11 | "chai" 12 | ], 13 | "main": "chai.js", 14 | "ignore": [ 15 | "build", 16 | "components", 17 | "lib", 18 | "node_modules", 19 | "support", 20 | "test", 21 | "index.js", 22 | "Makefile", 23 | ".*" 24 | ], 25 | "dependencies": {}, 26 | "devDependencies": {}, 27 | "homepage": "https://github.com/chaijs/chai", 28 | "_release": "1.8.1", 29 | "_resolution": { 30 | "type": "version", 31 | "tag": "1.8.1", 32 | "commit": "4107c02cb1507c8554177aeeefd9732abcfd4e64" 33 | }, 34 | "_source": "git://github.com/chaijs/chai.git", 35 | "_target": "~1.8.0", 36 | "_originalSource": "chai" 37 | } -------------------------------------------------------------------------------- /test/bower_components/chai/README.md: -------------------------------------------------------------------------------- 1 | [![Chai Documentation](http://chaijs.com/public/img/chai-logo.png)](http://chaijs.com) 2 | 3 | Chai is a BDD / TDD assertion library for [node](http://nodejs.org) and the browser that 4 | can be delightfully paired with any javascript testing framework. 5 | 6 | For more information or to download plugins, view the [documentation](http://chaijs.com). 7 | 8 | [![Build Status](https://travis-ci.org/chaijs/chai.png?branch=master)](https://travis-ci.org/chaijs/chai) [![Coverage Status](https://coveralls.io/repos/chaijs/chai/badge.png?branch=master)](https://coveralls.io/r/chaijs/chai?branch=master) 9 | 10 | [![Selenium Test Status](https://saucelabs.com/browser-matrix/chaijs.svg)](https://saucelabs.com/u/chaijs) 11 | 12 | ### Related Projects 13 | 14 | - [chaijs / assertion-error](https://github.com/chaijs/assertion-error): Custom `Error` constructor thrown upon an assertion failing. 15 | - [chaijs / deep-eql](https://github.com/chaijs/deep-eql): Improved deep equality testing for Node.js and the browser. 16 | 17 | ### Contributors 18 | 19 | project : chai 20 | repo age : 1 year, 9 months 21 | active : 139 days 22 | commits : 693 23 | files : 54 24 | authors : 25 | 518 Jake Luer 74.7% 26 | 66 Veselin Todorov 9.5% 27 | 43 Domenic Denicola 6.2% 28 | 6 Ruben Verborgh 0.9% 29 | 5 George Kats 0.7% 30 | 5 Jo Liss 0.7% 31 | 5 Juliusz Gonera 0.7% 32 | 5 Scott Nonnenberg 0.7% 33 | 4 John Firebaugh 0.6% 34 | 4 Nick Heiner 0.6% 35 | 4 josher19 0.6% 36 | 3 Jeff Barczewski 0.4% 37 | 3 Ryunosuke SATO 0.4% 38 | 2 Bartvds 0.3% 39 | 2 Edwin Shao 0.3% 40 | 2 Jakub Nešetřil 0.3% 41 | 2 Teddy Cross 0.3% 42 | 1 Anand Patil 0.1% 43 | 1 Benjamin Horsleben 0.1% 44 | 1 Brandon Payton 0.1% 45 | 1 Chris Connelly 0.1% 46 | 1 Chun-Yi 0.1% 47 | 1 DD 0.1% 48 | 1 Jeff Welch 0.1% 49 | 1 Kilian Ciuffolo 0.1% 50 | 1 Niklas Närhinen 0.1% 51 | 1 Paul Miller 0.1% 52 | 1 Sasha Koss 0.1% 53 | 1 Victor Costan 0.1% 54 | 1 Vinay Pulim 0.1% 55 | 1 piecioshka 0.1% 56 | 57 | ## License 58 | 59 | (The MIT License) 60 | 61 | Copyright (c) 2011-2013 Jake Luer 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy 64 | of this software and associated documentation files (the "Software"), to deal 65 | in the Software without restriction, including without limitation the rights 66 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 67 | copies of the Software, and to permit persons to whom the Software is 68 | furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in 71 | all copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 74 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 75 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 76 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 77 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 78 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 79 | THE SOFTWARE. 80 | -------------------------------------------------------------------------------- /test/bower_components/chai/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chai" 3 | , "version": "1.8.1" 4 | , "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic." 5 | , "license": "MIT" 6 | , "keywords": [ 7 | "test" 8 | , "assertion" 9 | , "assert" 10 | , "testing" 11 | , "chai" 12 | ] 13 | , "main": "chai.js" 14 | , "ignore": [ 15 | "build" 16 | , "components" 17 | , "lib" 18 | , "node_modules" 19 | , "support" 20 | , "test" 21 | , "index.js" 22 | , "Makefile" 23 | , ".*" 24 | ] 25 | , "dependencies": {} 26 | , "devDependencies": {} 27 | } 28 | -------------------------------------------------------------------------------- /test/bower_components/chai/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chai" 3 | , "repo": "chaijs/chai" 4 | , "version": "1.8.1" 5 | , "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic." 6 | , "license": "MIT" 7 | , "keywords": [ 8 | "test" 9 | , "assertion" 10 | , "assert" 11 | , "testing" 12 | , "chai" 13 | ] 14 | , "main": "index.js" 15 | , "scripts": [ 16 | "index.js" 17 | , "lib/chai.js" 18 | , "lib/chai/assertion.js" 19 | , "lib/chai/core/assertions.js" 20 | , "lib/chai/interface/assert.js" 21 | , "lib/chai/interface/expect.js" 22 | , "lib/chai/interface/should.js" 23 | , "lib/chai/utils/addChainableMethod.js" 24 | , "lib/chai/utils/addMethod.js" 25 | , "lib/chai/utils/addProperty.js" 26 | , "lib/chai/utils/flag.js" 27 | , "lib/chai/utils/getActual.js" 28 | , "lib/chai/utils/getEnumerableProperties.js" 29 | , "lib/chai/utils/getMessage.js" 30 | , "lib/chai/utils/getName.js" 31 | , "lib/chai/utils/getPathValue.js" 32 | , "lib/chai/utils/getProperties.js" 33 | , "lib/chai/utils/index.js" 34 | , "lib/chai/utils/inspect.js" 35 | , "lib/chai/utils/objDisplay.js" 36 | , "lib/chai/utils/overwriteMethod.js" 37 | , "lib/chai/utils/overwriteProperty.js" 38 | , "lib/chai/utils/test.js" 39 | , "lib/chai/utils/transferFlags.js" 40 | , "lib/chai/utils/type.js" 41 | ] 42 | , "dependencies": { 43 | "chaijs/assertion-error": "1.0.0" 44 | , "chaijs/deep-eql": "0.1.3" 45 | } 46 | , "development": {} 47 | } 48 | -------------------------------------------------------------------------------- /test/bower_components/chai/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '' 4 | , frameworks: [ 'mocha' ] 5 | , files: [ 6 | 'build/build.js' 7 | , 'test/bootstrap/karma.js' 8 | , 'test/*.js' 9 | ] 10 | , exclude: [] 11 | , reporters: [ 'progress' ] 12 | , port: 9876 13 | , colors: true 14 | , logLevel: config.LOG_INFO 15 | , autoWatch: false 16 | , browsers: [ 'PhantomJS' ] 17 | , captureTimeout: 60000 18 | , singleRun: true 19 | }); 20 | 21 | switch (process.env.CHAI_TEST_ENV) { 22 | case 'sauce': 23 | require('./karma.sauce')(config); 24 | break; 25 | default: 26 | // ... 27 | break; 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /test/bower_components/chai/karma.sauce.js: -------------------------------------------------------------------------------- 1 | var version = require('./package.json').version; 2 | var ts = new Date().getTime(); 3 | 4 | module.exports = function(config) { 5 | var auth; 6 | 7 | try { 8 | auth = require('./test/auth/index'); 9 | } catch(ex) { 10 | auth = {}; 11 | auth.SAUCE_USERNAME = process.env.SAUCE_USERNAME || null; 12 | auth.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY || null; 13 | } 14 | 15 | if (!auth.SAUCE_USERNAME || !auth.SAUCE_ACCESS_KEY) return; 16 | if (process.env.SKIP_SAUCE) return; 17 | 18 | var branch = process.env.TRAVIS_BRANCH || 'local' 19 | var browserConfig = require('./sauce.browsers'); 20 | var browsers = Object.keys(browserConfig); 21 | var tags = [ 'chaijs_' + version, auth.SAUCE_USERNAME + '@' + branch ]; 22 | var tunnel = process.env.TRAVIS_JOB_NUMBER || ts; 23 | 24 | if (process.env.TRAVIS_JOB_NUMBER) { 25 | tags.push('travis@' + process.env.TRAVIS_JOB_NUMBER); 26 | } 27 | 28 | config.browsers = config.browsers.concat(browsers); 29 | config.customLaunchers = browserConfig; 30 | config.reporters.push('saucelabs'); 31 | config.transports = [ 'xhr-polling' ]; 32 | 33 | config.sauceLabs = { 34 | username: auth.SAUCE_USERNAME 35 | , accessKey: auth.SAUCE_ACCESS_KEY 36 | , startConnect: true 37 | , tags: tags 38 | , testName: 'ChaiJS' 39 | , tunnelIdentifier: tunnel 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /test/bower_components/chai/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Jake Luer ", 3 | "name": "chai", 4 | "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", 5 | "keywords": [ "test", "assertion", "assert", "testing", "chai" ], 6 | "homepage": "http://chaijs.com", 7 | "license": "MIT", 8 | "contributors": [ 9 | "Jake Luer ", 10 | "Domenic Denicola (http://domenicdenicola.com)", 11 | "Veselin Todorov ", 12 | "John Firebaugh " 13 | ], 14 | "version": "1.8.1", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/chaijs/chai" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/chaijs/chai/issues" 21 | }, 22 | "main": "./index", 23 | "scripts": { 24 | "test": "make test" 25 | }, 26 | "engines": { 27 | "node": ">= 0.4.0" 28 | }, 29 | "dependencies": { 30 | "assertion-error": "1.0.0" 31 | , "deep-eql": "0.1.3" 32 | }, 33 | "devDependencies": { 34 | "component": "*" 35 | , "coveralls": "2.0.16" 36 | , "jscoverage": "0.3.7" 37 | , "karma": "canary" 38 | , "karma-mocha": "*" 39 | , "karma-sauce-launcher": "git://github.com/embarkmobile/karma-sauce-launcher.git#feature-passfail" 40 | , "mocha": "1.8.2" 41 | , "mocha-lcov-reporter": "0.0.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/bower_components/chai/sauce.browsers.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * Chrome 4 | */ 5 | 6 | exports['SL_Chrome'] = { 7 | base: 'SauceLabs' 8 | , browserName: 'chrome' 9 | }; 10 | 11 | /*! 12 | * Firefox 13 | */ 14 | 15 | /*! 16 | * TODO: Karma doesn't seem to like this, though sauce boots its up 17 | * 18 | 19 | exports['SL_Firefox_23'] = { 20 | base: 'SauceLabs' 21 | , browserName: 'firefox' 22 | , platform: 'Windows XP' 23 | , version: '23' 24 | }; 25 | 26 | */ 27 | 28 | exports['SL_Firefox_22'] = { 29 | base: 'SauceLabs' 30 | , browserName: 'firefox' 31 | , platform: 'Windows 7' 32 | , version: '22' 33 | }; 34 | 35 | /*! 36 | * Opera 37 | */ 38 | 39 | exports['SL_Opera_12'] = { 40 | base: 'SauceLabs' 41 | , browserName: 'opera' 42 | , platform: 'Windows 7' 43 | , version: '12' 44 | }; 45 | 46 | exports['SL_Opera_11'] = { 47 | base: 'SauceLabs' 48 | , browserName: 'opera' 49 | , platform: 'Windows 7' 50 | , version: '11' 51 | }; 52 | 53 | /*! 54 | * Internet Explorer 55 | */ 56 | 57 | exports['SL_IE_10'] = { 58 | base: 'SauceLabs' 59 | , browserName: 'internet explorer' 60 | , platform: 'Windows 2012' 61 | , version: '10' 62 | }; 63 | 64 | /*! 65 | * Safari 66 | */ 67 | 68 | exports['SL_Safari_6'] = { 69 | base: 'SauceLabs' 70 | , browserName: 'safari' 71 | , platform: 'Mac 10.8' 72 | , version: '6' 73 | }; 74 | 75 | exports['SL_Safari_5'] = { 76 | base: 'SauceLabs' 77 | , browserName: 'safari' 78 | , platform: 'Mac 10.6' 79 | , version: '5' 80 | }; 81 | 82 | /*! 83 | * iPhone 84 | */ 85 | 86 | /*! 87 | * TODO: These take forever to boot or shut down. Causes timeout. 88 | * 89 | 90 | exports['SL_iPhone_6'] = { 91 | base: 'SauceLabs' 92 | , browserName: 'iphone' 93 | , platform: 'Mac 10.8' 94 | , version: '6' 95 | }; 96 | 97 | exports['SL_iPhone_5-1'] = { 98 | base: 'SauceLabs' 99 | , browserName: 'iphone' 100 | , platform: 'Mac 10.8' 101 | , version: '5.1' 102 | }; 103 | 104 | exports['SL_iPhone_5'] = { 105 | base: 'SauceLabs' 106 | , browserName: 'iphone' 107 | , platform: 'Mac 10.6' 108 | , version: '5' 109 | }; 110 | 111 | */ 112 | 113 | /*! 114 | * Android 115 | */ 116 | 117 | /*! 118 | * TODO: fails because of error serialization 119 | * 120 | 121 | exports['SL_Android_4'] = { 122 | base: 'SauceLabs' 123 | , browserName: 'android' 124 | , platform: 'Linux' 125 | , version: '4' 126 | }; 127 | 128 | */ 129 | -------------------------------------------------------------------------------- /test/bower_components/mocha/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha", 3 | "version": "1.14.0", 4 | "main": "mocha.js", 5 | "ignore": [ 6 | "bin", 7 | "editors", 8 | "images", 9 | "lib", 10 | "support", 11 | "test", 12 | ".gitignore", 13 | ".npmignore", 14 | ".travis.yml", 15 | "component.json", 16 | "index.js", 17 | "Makefile", 18 | "package.json" 19 | ], 20 | "homepage": "https://github.com/visionmedia/mocha", 21 | "_release": "1.14.0", 22 | "_resolution": { 23 | "type": "version", 24 | "tag": "1.14.0", 25 | "commit": "10c65f379c4501269c83a719a04bd2fb0013f853" 26 | }, 27 | "_source": "git://github.com/visionmedia/mocha.git", 28 | "_target": "~1.14.0", 29 | "_originalSource": "mocha" 30 | } -------------------------------------------------------------------------------- /test/bower_components/mocha/LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2011-2013 TJ Holowaychuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/bower_components/mocha/Readme.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha) 2 | 3 | [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha) 4 | 5 | Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha). 6 | 7 | ## Contributors 8 | 9 | ``` 10 | 11 | project : mocha 12 | repo age : 1 year, 7 months 13 | active : 272 days 14 | commits : 1116 15 | files : 123 16 | authors : 17 | 504 TJ Holowaychuk 45.2% 18 | 389 Tj Holowaychuk 34.9% 19 | 31 Guillermo Rauch 2.8% 20 | 13 Attila Domokos 1.2% 21 | 9 John Firebaugh 0.8% 22 | 8 Jo Liss 0.7% 23 | 7 Nathan Rajlich 0.6% 24 | 6 James Carr 0.5% 25 | 6 Brendan Nee 0.5% 26 | 5 Aaron Heckmann 0.4% 27 | 4 hokaccha 0.4% 28 | 4 Xavier Antoviaque 0.4% 29 | 4 Joshua Krall 0.4% 30 | 3 Wil Moore III 0.3% 31 | 3 Jesse Dailey 0.3% 32 | 3 Nathan Bowser 0.3% 33 | 3 Tyson Tate 0.3% 34 | 3 Cory Thomas 0.3% 35 | 3 Ryunosuke SATO 0.3% 36 | 3 Paul Miller 0.3% 37 | 3 Ben Lindsey 0.3% 38 | 2 Forbes Lindesay 0.2% 39 | 2 Konstantin Käfer 0.2% 40 | 2 Brian Beck 0.2% 41 | 2 Merrick Christensen 0.2% 42 | 2 Michael Riley 0.2% 43 | 2 David Henderson 0.2% 44 | 2 Nathan Alderson 0.2% 45 | 2 Paul Armstrong 0.2% 46 | 2 Pete Hawkins 0.2% 47 | 2 Quang Van 0.2% 48 | 2 Raynos 0.2% 49 | 2 Jonas Westerlund 0.2% 50 | 2 Domenic Denicola 0.2% 51 | 2 Shawn Krisman 0.2% 52 | 2 Simon Gaeremynck 0.2% 53 | 2 FARKAS Máté 0.2% 54 | 2 Timo Tijhof 0.2% 55 | 2 Justin DuJardin 0.2% 56 | 2 Juzer Ali 0.2% 57 | 2 Ian Storm Taylor 0.2% 58 | 2 Arian Stolwijk 0.2% 59 | 2 domenic 0.2% 60 | 1 Richard Dingwall 0.1% 61 | 1 Russ Bradberry 0.1% 62 | 1 Sasha Koss 0.1% 63 | 1 Seiya Konno 0.1% 64 | 1 Standa Opichal 0.1% 65 | 1 Steve Mason 0.1% 66 | 1 Will Langstroth 0.1% 67 | 1 Yanis Wang 0.1% 68 | 1 Yuest Wang 0.1% 69 | 1 abrkn 0.1% 70 | 1 airportyh 0.1% 71 | 1 fengmk2 0.1% 72 | 1 tgautier@yahoo.com 0.1% 73 | 1 traleig1 0.1% 74 | 1 vlad 0.1% 75 | 1 yuitest 0.1% 76 | 1 Adam Crabtree 0.1% 77 | 1 Andreas Brekken 0.1% 78 | 1 Atsuya Takagi 0.1% 79 | 1 Austin Birch 0.1% 80 | 1 Bjørge Næss 0.1% 81 | 1 Brian Moore 0.1% 82 | 1 Bryan Donovan 0.1% 83 | 1 Casey Foster 0.1% 84 | 1 Corey Butler 0.1% 85 | 1 Dave McKenna 0.1% 86 | 1 Fedor Indutny 0.1% 87 | 1 Florian Margaine 0.1% 88 | 1 Frederico Silva 0.1% 89 | 1 Fredrik Lindin 0.1% 90 | 1 Gareth Murphy 0.1% 91 | 1 Gavin Mogan 0.1% 92 | 1 Greg Perkins 0.1% 93 | 1 Harry Brundage 0.1% 94 | 1 Herman Junge 0.1% 95 | 1 Ian Young 0.1% 96 | 1 Ivan 0.1% 97 | 1 Jaakko Salonen 0.1% 98 | 1 Jakub Nešetřil 0.1% 99 | 1 James Bowes 0.1% 100 | 1 James Lal 0.1% 101 | 1 Jason Barry 0.1% 102 | 1 Javier Aranda 0.1% 103 | 1 Jeff Kunkle 0.1% 104 | 1 Jonathan Creamer 0.1% 105 | 1 Jussi Virtanen 0.1% 106 | 1 Katie Gengler 0.1% 107 | 1 Kazuhito Hokamura 0.1% 108 | 1 Koen Punt 0.1% 109 | 1 Laszlo Bacsi 0.1% 110 | 1 László Bácsi 0.1% 111 | 1 Maciej Małecki 0.1% 112 | 1 Matt Robenolt 0.1% 113 | 1 Matt Smith 0.1% 114 | 1 Matthew Shanley 0.1% 115 | 1 Michael Schoonmaker 0.1% 116 | 1 Phil Sung 0.1% 117 | 1 R56 0.1% 118 | ``` 119 | 120 | ## Links 121 | 122 | - [Google Group](http://groups.google.com/group/mochajs) 123 | - [Wiki](https://github.com/visionmedia/mocha/wiki) 124 | - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki) 125 | -------------------------------------------------------------------------------- /test/bower_components/mocha/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha", 3 | "version": "1.12.0", 4 | "main": "mocha.js", 5 | "ignore": [ 6 | "bin", 7 | "editors", 8 | "images", 9 | "lib", 10 | "support", 11 | "test", 12 | ".gitignore", 13 | ".npmignore", 14 | ".travis.yml", 15 | "component.json", 16 | "index.js", 17 | "Makefile", 18 | "package.json" 19 | ] 20 | } -------------------------------------------------------------------------------- /test/bower_components/mocha/media/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | mocha 8 | 9 | -------------------------------------------------------------------------------- /test/bower_components/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, #mocha li { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | #mocha ul { 18 | list-style: none; 19 | } 20 | 21 | #mocha h1, #mocha h2 { 22 | margin: 0; 23 | } 24 | 25 | #mocha h1 { 26 | margin-top: 15px; 27 | font-size: 1em; 28 | font-weight: 200; 29 | } 30 | 31 | #mocha h1 a { 32 | text-decoration: none; 33 | color: inherit; 34 | } 35 | 36 | #mocha h1 a:hover { 37 | text-decoration: underline; 38 | } 39 | 40 | #mocha .suite .suite h1 { 41 | margin-top: 0; 42 | font-size: .8em; 43 | } 44 | 45 | #mocha .hidden { 46 | display: none; 47 | } 48 | 49 | #mocha h2 { 50 | font-size: 12px; 51 | font-weight: normal; 52 | cursor: pointer; 53 | } 54 | 55 | #mocha .suite { 56 | margin-left: 15px; 57 | } 58 | 59 | #mocha .test { 60 | margin-left: 15px; 61 | overflow: hidden; 62 | } 63 | 64 | #mocha .test.pending:hover h2::after { 65 | content: '(pending)'; 66 | font-family: arial, sans-serif; 67 | } 68 | 69 | #mocha .test.pass.medium .duration { 70 | background: #C09853; 71 | } 72 | 73 | #mocha .test.pass.slow .duration { 74 | background: #B94A48; 75 | } 76 | 77 | #mocha .test.pass::before { 78 | content: '✓'; 79 | font-size: 12px; 80 | display: block; 81 | float: left; 82 | margin-right: 5px; 83 | color: #00d6b2; 84 | } 85 | 86 | #mocha .test.pass .duration { 87 | font-size: 9px; 88 | margin-left: 5px; 89 | padding: 2px 5px; 90 | color: white; 91 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 92 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 93 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -webkit-border-radius: 5px; 95 | -moz-border-radius: 5px; 96 | -ms-border-radius: 5px; 97 | -o-border-radius: 5px; 98 | border-radius: 5px; 99 | } 100 | 101 | #mocha .test.pass.fast .duration { 102 | display: none; 103 | } 104 | 105 | #mocha .test.pending { 106 | color: #0b97c4; 107 | } 108 | 109 | #mocha .test.pending::before { 110 | content: '◦'; 111 | color: #0b97c4; 112 | } 113 | 114 | #mocha .test.fail { 115 | color: #c00; 116 | } 117 | 118 | #mocha .test.fail pre { 119 | color: black; 120 | } 121 | 122 | #mocha .test.fail::before { 123 | content: '✖'; 124 | font-size: 12px; 125 | display: block; 126 | float: left; 127 | margin-right: 5px; 128 | color: #c00; 129 | } 130 | 131 | #mocha .test pre.error { 132 | color: #c00; 133 | max-height: 300px; 134 | overflow: auto; 135 | } 136 | 137 | #mocha .test pre { 138 | display: block; 139 | float: left; 140 | clear: left; 141 | font: 12px/1.5 monaco, monospace; 142 | margin: 5px; 143 | padding: 15px; 144 | border: 1px solid #eee; 145 | border-bottom-color: #ddd; 146 | -webkit-border-radius: 3px; 147 | -webkit-box-shadow: 0 1px 3px #eee; 148 | -moz-border-radius: 3px; 149 | -moz-box-shadow: 0 1px 3px #eee; 150 | border-radius: 3px; 151 | } 152 | 153 | #mocha .test h2 { 154 | position: relative; 155 | } 156 | 157 | #mocha .test a.replay { 158 | position: absolute; 159 | top: 3px; 160 | right: 0; 161 | text-decoration: none; 162 | vertical-align: middle; 163 | display: block; 164 | width: 15px; 165 | height: 15px; 166 | line-height: 15px; 167 | text-align: center; 168 | background: #eee; 169 | font-size: 15px; 170 | -moz-border-radius: 15px; 171 | border-radius: 15px; 172 | -webkit-transition: opacity 200ms; 173 | -moz-transition: opacity 200ms; 174 | transition: opacity 200ms; 175 | opacity: 0.3; 176 | color: #888; 177 | } 178 | 179 | #mocha .test:hover a.replay { 180 | opacity: 1; 181 | } 182 | 183 | #mocha-report.pass .test.fail { 184 | display: none; 185 | } 186 | 187 | #mocha-report.fail .test.pass { 188 | display: none; 189 | } 190 | 191 | #mocha-report.pending .test.pass, 192 | #mocha-report.pending .test.fail { 193 | display: none; 194 | } 195 | #mocha-report.pending .test.pass.pending { 196 | display: block; 197 | } 198 | 199 | #mocha-error { 200 | color: #c00; 201 | font-size: 1.5em; 202 | font-weight: 100; 203 | letter-spacing: 1px; 204 | } 205 | 206 | #mocha-stats { 207 | position: fixed; 208 | top: 15px; 209 | right: 10px; 210 | font-size: 12px; 211 | margin: 0; 212 | color: #888; 213 | z-index: 1; 214 | } 215 | 216 | #mocha-stats .progress { 217 | float: right; 218 | padding-top: 0; 219 | } 220 | 221 | #mocha-stats em { 222 | color: black; 223 | } 224 | 225 | #mocha-stats a { 226 | text-decoration: none; 227 | color: inherit; 228 | } 229 | 230 | #mocha-stats a:hover { 231 | border-bottom: 1px solid #eee; 232 | } 233 | 234 | #mocha-stats li { 235 | display: inline-block; 236 | margin: 0 5px; 237 | list-style: none; 238 | padding-top: 11px; 239 | } 240 | 241 | #mocha-stats canvas { 242 | width: 40px; 243 | height: 40px; 244 | } 245 | 246 | #mocha code .comment { color: #ddd } 247 | #mocha code .init { color: #2F6FAD } 248 | #mocha code .string { color: #5890AD } 249 | #mocha code .keyword { color: #8A6343 } 250 | #mocha code .number { color: #2F6FAD } 251 | 252 | @media screen and (max-device-width: 480px) { 253 | #mocha { 254 | margin: 60px 0px; 255 | } 256 | 257 | #mocha #stats { 258 | position: absolute; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mocha Spec Runner 7 | 8 | 9 | 10 |
    11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/spec/test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | describe('Give it some context', function () { 7 | describe('maybe a bit more context here', function () { 8 | it('should run here few assertions', function () { 9 | 10 | }); 11 | }); 12 | }); 13 | })(); 14 | --------------------------------------------------------------------------------