| 
				   | 
				
| (36 intermediate revisions by the same user not shown) | 
| Line 16: | 
Line 16: | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [http://www.aosabook.org/en/index.html Architecture of major open source apps]  |  | ! [http://www.aosabook.org/en/index.html Architecture of major open source apps]  | 
 |  | |}  | 
 |  | {| class="wikitable"  | 
 |  | ! [[Security]]  | 
 | |}  |  | |}  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
| Line 21: | 
Line 24: | 
 | |}  |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 | ! String escape formatting across different languages and systems  |  | ! nosql enhances, not replaces, SQL  | 
 | |-  |  | |-  | 
 | |    |  | | Not all data should be denormalized, and not all data should be normalized.  The optimal mix considers the extent of the data.    | 
 | * c++ to JSON: always use nlohmann::json j.dump() to encode, to ensure strings are properly escaped  |  |    | 
 | * JSON to c++: always use nlohmann::json j.parse() "  |  | * Precise schemas are good when not overdone  | 
 | * c++ to Javascript: use raw_to_Javascript() to properly escape  |  | * When a container has an array with a large number of elements, it should be normalized  | 
 | * c++ to sqlite: use SqliteLocalModel::safestr(), which uses double_doublequotes(str)
  |  | * Sparse data and heterogeneous data are the best candidates for denormalization  | 
 |  |    | 
 |  | Postgres with JSON allows an elegant combination of nosql and SQL.  | 
 | |}  |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
| Line 59: | 
Line 64: | 
 | |-  |  | |-  | 
 | | Among competing hypotheses, the one with the fewest assumptions should be selected.  |  | | Among competing hypotheses, the one with the fewest assumptions should be selected.  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Security
  |  | 
 | |-
  |  | 
 | | Notes to review before interviews, etc:
  |  | 
 |  
  |  | 
 |     ---------------------------
  |  | 
 |     fedramp cloud certification
  |  | 
 |     ---------------------------
  |  | 
 |       strong encryption, eg AES 256-bit
  |  | 
 |       data should be encrypted before it leaves end-user "organization"
  |  | 
 |       encrypt: data-at-rest, data-in-transit, data-in-use
  |  | 
 |       encryption keys MUST be kept within end-user org
  |  | 
 |       CSP requirements: implement security, third-party assess, maintain authorization, comply with continous monitoring
  |  | 
 |       
  |  | 
 |   basics review
  |  | 
 |  
  |  | 
 |       public-private key
  |  | 
 |       ------------------
  |  | 
 |       plain -> encrypted -> plain
  |  | 
 |             ^            ^
  |  | 
 |           publickey      privatekey
  |  | 
 |           
  |  | 
 |       
  |  | 
 |       SSL HANDSHAKE
  |  | 
 |       -------------
  |  | 
 |       client                                                    server
  |  | 
 |              -> hello ->        
  |  | 
 |              < cert
  |  | 
 |       (check) 
  |  | 
 |                send secret encryped with server public key >    decrypt secret)
  |  | 
 |                (opt) send client cert >                         (check - but not otherwise used?)
  |  | 
 |                ack>
  |  | 
 |                <ack
  |  | 
 |                exchange with shared secret key
  |  | 
 |  
  |  | 
 |   best encryption to date
  |  | 
 |     ssl labs: 
  |  | 
 |       Key RSA 2048 bits (e 65537)
  |  | 
 |       signature SHA256withRSA
  |  | 
 |       certchain includes Let's Encrypt Authority X3, RSA 2048 bits, Signature: SHA256withRSA
  |  | 
 |       TLS 1.2 (not allowed: TLS 1.1, 1.0; SSL 3, SSL 2
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)  ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)  ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)     ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)      DH 2048 bits   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x67)      DH 2048 bits   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33)         DH 2048 bits   FS      
  |  | 
 |     
  |  | 
 |     review 
  |  | 
 |  
  |  | 
 |       AES 256-bit for encryption
  |  | 
 |         aka Rijndael, which won in the original NIST AES selection process (in 2001!)
  |  | 
 |         symmetric key algorithm (same key to encrypt and decrypt)
  |  | 
 |         block size 128
  |  | 
 |         key length 256 (can be 128 and 192)
  |  | 
 |  
  |  | 
 |       SHA-256 for signatures
  |  | 
 |         a bit-specific SHA-2
  |  | 
 |         deprecates SHA-1
  |  | 
 |         NOT deprecated by SHA-3 (an alternative developed through NIST competition)
  |  | 
 |         256-bit "hash values" aka digests aka signatures)
  |  | 
 |         server + client ssl certificate process
  |  | 
 |         
  |  | 
 |       HMAC is a signature with a key - what I'm doing with JWT (HMAC-SHA256)
  |  | 
 |         
  |  | 
 |      broken: RC2 RC4 DES IDEA ...
  |  | 
 |  
  |  | 
 |      next-gen: https://security.stackexchange.com/questions/135054/whats-after-aes
  |  | 
 |       TLS 1.3, which is still a draft, is going with two authenticated ciphers as its required choices:
  |  | 
 |         AES-GCM, with either 128- or 256-bit keys;
  |  | 
 |         ChaCha20/Poly1305
  |  | 
 |       The reasons we have two are the following:
  |  | 
 |         As a backup—if one of them is broken, everybody can switch to the other;
  |  | 
 |         They have different strengths:
  |  | 
 |         AES-GCM has excellent hardware support in many platforms;
  |  | 
 |         ChaCha20/Poly1305 has faster all-software implementations than AES-GCM does.
  |  | 
 |  
  |  | 
 |     good for https with SSL certs, according to SSL labs:
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)  ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)  ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)     ECDH secp256r1 (eq. 3072 bits RSA)   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)      DH 2048 bits   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x67)      DH 2048 bits   FS 128
  |  | 
 |         TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33)         DH 2048 bits   FS      
  |  | 
 |    
  |  | 
 |  
  |  | 
 |   NIST (National Institute of Standards and Technology)
  |  | 
 |   
  |  | 
 |     SHA-3 competition 2007-2012 (SHA = Secure Hash Algorithm)
  |  | 
 |       64 entries
  |  | 
 |       5 finalists: BLAKE, Grøstl, JH, Keccak and Skein
  |  | 
 |       winner: Keccak
  |  | 
 |       schneier's feedback:
  |  | 
 |         Yes, I would have rather my own Skein had won, but it was a good choice.
  |  | 
 |  
  |  | 
 |   NSA has Suite A (classified algorithms that will not be released) and Suite B cryptography algorithms
  |  | 
 |     Suite B's components are:
  |  | 
 |     
  |  | 
 |     Advanced Encryption Standard (AES) with key sizes of 128 and 256 bits. 
  |  | 
 |       For traffic flow, AES should be used with either 
  |  | 
 |         the Counter Mode (CTR) for low bandwidth traffic or 
  |  | 
 |         the Galois/Counter Mode (GCM) mode of operation for high bandwidth traffic (THIS CAN BE PARALLELIZED)
  |  | 
 |     Elliptic Curve Digital Signature Algorithm (ECDSA) – digital signatures
  |  | 
 |     Elliptic Curve Diffie–Hellman (ECDH) – key agreement
  |  | 
 |     Secure Hash Algorithm 2 (SHA-256 and SHA-384) – message digest
  |  | 
 |  
  |  | 
 |   talk about my projects
  |  | 
 |  
  |  | 
 |     nopc
  |  | 
 |       energy market
  |  | 
 |       need to measure power behind the consumers meter
  |  | 
 |       i wrote a client that lives on embedded devices
  |  | 
 |       i cross-compiled openssl, libmodbus, libcurl, libwebsockets, zlib
  |  | 
 |  
  |  | 
 |       ssl / websockets work:
  |  | 
 |         libwebsockets uses openssl, but i have played a bit with optional ssl: wolfssl
  |  | 
 |           has streaming media support, including the HC-128 and RABBIT stream ciphers
  |  | 
 |           Standard ciphers are supported, including EDH on both the client and server side.  
  |  | 
 |           wolfSSL recently added support for BLAKE2b, one of the SHA-3 finalists as well. 
  |  | 
 |           wolfSSL’s underlying cryptography library is called wolfCrypt.
  |  | 
 |         i wouldn't want to stray from openssl unless there was a more compelling reason, since it gets so many eyeballs on it
  |  | 
 |         people don't like its depth of support for other algorithms, just stick with best-in-class
  |  | 
 |           Key RSA 2048 bits (e 65537)
  |  | 
 |           signature SHA256withRSA
  |  | 
 |           pki: rsa
  |  | 
 | 
  |  | 
 |       given a guid token, it uses that to generate ssl certs on the device
  |  | 
 |       then it runs 24/7 on the embedded device
  |  | 
 |       it has a tight loop with callbacks, C is gross but you can do pretty much anything
  |  | 
 |         in factthats the problem, you have to do everything!
  |  | 
 |         again, i heavily rely on reusable helpers
  |  | 
 |       so it has a single-threaded loop with callbacks
  |  | 
 |       poll:
  |  | 
 |         if !config configure client
  |  | 
 |         else poll and store meter data, buffered to memory then volatile then nonvolatile storage
  |  | 
 |         if ws not connected initiate connection
  |  | 
 |         service the connection callbacks (sending bi-directionally as needed)
  |  | 
 |  
  |  | 
 |     just added jwt to abettertrader
  |  | 
 |       uses HMAC-SHA256 (uses a secret during signature generation)
  |  | 
 |       client sends u/p
  |  | 
 |       server builds header+payload+HMAC-SHA256 signature with its own secret
  |  | 
 |       server sends back base64-encoded token to client
  |  | 
 |       client uses token on every API call
  |  | 
 |       when token times out, user must log in again
  |  | 
 |       server can almost instantly determine user's priviledges without a db hit
  |  | 
 |       that's why they call it stateless
  |  | 
 |  
  |  | 
 |     abettertrader c++ based webserver, gets A+ rating at ssllabs
  |  | 
 |     via haproxy!
  |  | 
 |       I serve up several domains from my home
  |  | 
 |       most of them use SNI with apache
  |  | 
 |         with SNI the hostname is sent outside of the encrypted traffic
  |  | 
 |         so you can serve up different apache sites based on the requested hostname
  |  | 
 |         it's fantastic - for years i had to serve up my different sites on different ports
  |  | 
 |         and it was hard to do https that way
  |  | 
 |       but! ... i'm running a C++ https server as well
  |  | 
 |       and i had to solve the problem of getting incoming https traffic to that server running on that port
  |  | 
 |       haproxy is THE BOMB for these kinds of things
  |  | 
 |       i configured haproxy to read the domain name and redirect traffic to the c++ http server and port
  |  | 
 |       but i am so excited about this, i got these bonuses with zero effort:
  |  | 
 |         ALL ssl handshaking is now done by haproxy!  i just give it ALL my certs, and it does the negotiations
  |  | 
 |           i was able to limit availabe ciphers to those listed as secure at ssllabs
  |  | 
 |             ssl-default-bind-options no-sslv3 no-tls-tickets force-tlsv12
  |  | 
 |             ssl-default-bind-ciphers AES128+EECDH:AES128+EDH
  |  | 
 |         ALSO i was able to turn on HSTS - this forces all http requests into https requests
  |  | 
 |           and that got me an A+ rating on ssllabs - for ALL My sites - in one fell swoop!
  |  | 
 |           i felt like that giant that killed 7 flies or whatever
  |  | 
 |       
  |  | 
 |     abettertrader uses a map of lambas and regexs
  |  | 
 |       when a url comes in, it plays it against all the registered regexs
  |  | 
 |       if it finds a match, it calls the lambda
  |  | 
 |       it's really fast and really fun
  |  | 
 |       
  |  | 
 |     i set up node.js scripting for my continuous integration
  |  | 
 |       i have an open source package called radscripts - it does an automatic semver bump on every commit
  |  | 
 |       i'm totally addicted to that
  |  | 
 |       you can turn it off and still get all the benefits - 
  |  | 
 |       i turned off auto-tagging at causam because they wanted to control the specific numbers of releases
  |  | 
 |       but semver dictates: ...
  |  | 
 |       
  |  | 
 |     i am always tinkering, i keep track of my projects in phabricator, an agile ticket tool that sprang out of facebook          
  |  | 
 |       do you want to see it?
  |  | 
 |       willcodeforcoffee.org moodboom/G
  |  | 
 |  
  |  | 
 |   write a c++ app that sorts an array then encrypts it then decrypts it
  |  | 
 |     int main() {
  |  | 
 |       return 0;
  |  | 
 |     }
  |  | 
 |     
  |  | 
 |   review c++11, c++14, interview questions
  |  | 
 |     RAII resource acquisition is initializaztion- constructor acquires, destructor releases
  |  | 
 |  
  |  | 
 |   c++ 
  |  | 
 |     c++11 features I love:
  |  | 
 |       automatic type detection - this is great esp for iterators so you don't have to type as much
  |  | 
 |       for loops
  |  | 
 |         if you don't need to walk forwards or backwards as you loop, these greatly simplify code profile
  |  | 
 |         but if you need the iterator as you loop, you can stick with the old way
  |  | 
 |       lambda expressions - really fast to write inline functions
  |  | 
 |       move semantics - you don't have to copy out results when you're done in a function, you can move them - and it's largely automatic, really nice
  |  | 
 |       initialization syntax - i love this for creating test data, you can easily initialize big arrays, whatever, right in code
  |  | 
 |       delegating constructors - this si great, so you don't have to rewrite all the base class constructors to be able to add a new one
  |  | 
 |       threading - a lot of this is available with boost - but it's nice to have the standard incorporate all the best boost work
  |  | 
 |     c++14 seems much more incremental than 11 was
  |  | 
 |       you can use auto for function return types - that was already how lambdas worked in c++11, i thought...
  |  | 
 |       lamdba parameters can be auto - kind of like templating, seems really crazy and cool - haven't played with this much yet
  |  | 
 |       and closures, how cool is that!!!  "lambda captures"  it carries along the scope from where it was called.  
  |  | 
 |         javascript of course makes us a huge fan of this.
  |  | 
 |         but i wonder about the performance penalty involved. - again, haven't played with it much
  |  | 
 |         deprecated keyword, binary literals
  |  | 
 |     and then c++17 is on the way - c++ has always made me happy, i find it incredibly elegant compared to lower C or higher java/C# arenas
  |  | 
 |       i think javascript actually hits a sweeter spot than those
  |  | 
 |       so i'm enjoying C++ and javascript more than anything these days
  |  | 
 |       i wrote some utilities in node so i can use node for scripting, which has been really productive
  |  | 
 |       but i have to say, python would be a welcome addition
  |  | 
 |  
  |  | 
 |   c++ containers
  |  | 
 |     
  |  | 
 |     i use hashmaps of pointers via unordered_map
  |  | 
 |     unordered set lets you contain pointers, and specify the hash and equals values for the object pointed to
  |  | 
 |     typedef std::unordered_set<AutotradeParameterSet*,PersistentIDObject_hash,PersistentIDObjects_equal > AutotradeParameterSets;
  |  | 
 |     then you can set up a second "index" into the object store
  |  | 
 |     you have to maintain all indexes as you add and remove, of course
  |  | 
 |     
  |  | 
 |     unordered_map uses the hash to find the right bucket O(1)
  |  | 
 |     map uses a binary tree and a comparison operator O(log(n))
  |  | 
 |     std::find() on vector uses quicksort O(N*log(n)) or insertsort which uses heapsort for worst case
  |  | 
 |       mergesort is good to preserve order of equal items (in-place)
  |  | 
 |     
  |  | 
 |     python has TimSort, pretty cool - looks for presorted sections, then merges those, COOL
  |  | 
 |       in use in python since about 2002
  |  | 
 |     
  |  | 
 |     i also use a sorted vector class i derived from the standard vector class
  |  | 
 |       it too can use pointers instead of objects, allowing for multiple indexes on a set of objects
  |  | 
 |       it has push_unsorted(), bSorted() and sort(sort_function) functions
  |  | 
 |       usually with vectors you use lower_bound to find things
  |  | 
 |       sorted vector has find helpers, and they always sort if unsorted
  |  | 
 |       it's really useful when you have a huge amount of objects and only sort on occasion
  |  | 
 |  
  |  | 
 |   - nagging question: how do you add functionality to a product that's new?  
  |  | 
 |       this was a very valuable question that I didn't answer well
  |  | 
 |       can i ammend my answer?
  |  | 
 |         i mentioned that I would diagram function flows - that would definitely be a key strategy
  |  | 
 |         and to read any and all available documentation on the software
  |  | 
 |       what should have also been part of my answer: 
  |  | 
 |         generate my own documentation as needed
  |  | 
 |         a fantastic tool for that is doxygen
  |  | 
 |         i ran it yesterday against a recent project, and it did a nice job creating class hierarchies etc.
  |  | 
 |   
  |  | 
 |       clang-tidy is suggested to be the best on reddit
  |  | 
 |         even has a -fix flag to fix in place, ha
  |  | 
 |         cmake can call it for you!!
  |  | 
 |       cppcheck
  |  | 
 |       coverity for Paid solutions
  |  | 
 |  
  |  | 
 |       jsonlint
  |  | 
 | |}  |  | |}  | 
 | |}  |  | |}  | 
| Line 829: | 
Line 576: | 
 | |-  |  | |-  | 
 | |  |  | |  | 
 |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 |  | ! String escape formatting across different languages and systems  | 
 |  | |-  | 
 |  | |   | 
 |  | * c++ to JSON: always use nlohmann::json j.dump() to encode, to ensure strings are properly escaped  | 
 |  | * JSON to c++: always use nlohmann::json j.parse() "  | 
 |  | * c++ to Javascript: use raw_to_Javascript() to properly escape  | 
 |  | * c++ to sqlite: use SqliteLocalModel::safestr(), which uses double_doublequotes(str)  | 
 |  | |}  | 
 |  | [[Postgres]]  | 
 |  |  | 
 | [[Simple-Web-Server]]  |  | [[Simple-Web-Server]]  | 
 |  |  | 
 |  | [[Robot Operating System]]  | 
 | 
  |  | 
  | 
 | [[C++ https libraries]]  |  | [[C++ https libraries]]  | 
| Line 889: | 
Line 649: | 
 |   CC=gcc-4.9  |  |   CC=gcc-4.9  | 
 | |}  |  | |}  | 
 |  | |}  | 
 |  | <!--   | 
 |  |  | 
 |  |  | 
 |  | ===========================================================================================================================================================================================================================================================================================  | 
 |  |  | 
 |  |  | 
 |  | -->  | 
 |  | {| class="wikitable"  | 
 |  | ! [[git]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 898: | 
Line 668: | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 | ! C/C++ debugging  |  | ! Debugging  | 
 | |-  |  | |-  | 
 | |  |  | |  | 
 |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 |  | ! Chrome capture large JSON variable  | 
 |  | |-   | 
 |  | | This is just pointlessly bizarre:  | 
 |  | * hit a breakpoint in the chrome debugger  | 
 |  | * right-click a variable and say "copy to global variable" (console will show name, typically "temp1")  | 
 |  | * push the variable to the clipboard by typing this in the console:  | 
 |  |  copy(temp1)  | 
 |  | |}  | 
 |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 |  | ! Visual Studio Code capture large string variable  | 
 |  | |-   | 
 |  | | While debugging, you can use the Debug Console to print memory, including the content of strings that are clipped by default in the variables and watch windows.  | 
 |  |  View > Open View > Debug Console  | 
 |  | From there, send gdb a command to print memory – 300 characters of a string in this example:  | 
 |  |  -exec x/300sb Query.c_str()  | 
 |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 | ! Qt Creator conditional breakpoint  |  | ! Qt Creator conditional breakpoint  | 
| Line 1,017: | 
Line 804: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="wikitable"  | 
 | ! Bootstrap  |  | ! [[Javascript]]  | 
 | |-  |  | |}<!--   | 
 | |
  |  |    | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  |    | 
 | ! Single Page Application  |  | ===========================================================================================================================================================================================================================================================================================  | 
 | |-
  |  |    | 
 | | css:  |  |    | 
 | <pre>
  |  | -->  | 
 |   .mdm-scrollable-div
  |  | {| class="wikitable"  | 
 |   {
  |  | ! [[Node.js]]  | 
 |     height: 100%; 
  |  | |}<!--    | 
 |     overflow: auto;
  |  | 
 |   }
  |  | 
 | </pre>
  |  | 
 | html:
  |  | 
 | <pre>
  |  | 
 |   <-- header -->
  |  | 
 | 
  |  | 
  | 
 |   <div class="frame" id="frame1top">
  |  | 
 |     <div class="mdm-scrollable-div">
  |  | 
 |       ...
  |  | 
 |     </div>
  |  | 
 |   </div>
  |  | 
 | 
  |  | 
  | 
 |   <-- footer -->
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 | </pre>
  |  | 
 | js:
  |  | 
 | <pre>
  |  | 
 |   // We need to adjust layout on resize, in ready handler, and programmatically as needed.
  |  | 
 |   $(window).resize(adjustLayout).resize();
  |  | 
 | 
  |  | 
  | 
 |   $( document ).ready(function() {
  |  | 
 | 
  |  | 
  | 
 |     // We need to adjust in ready handler, on resize, and programmatically as needed
  |  | -->  | 
 |     adjustLayout();
  |  | {| class="wikitable"  | 
 |  | ! [[React]]  | 
 |  | |}<!--   | 
 | 
  |  | 
  | 
 |     /* your other page load code here*/
  |  | 
 | 
  |  | 
  | 
 |   });
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 | 
  |  | 
  | 
 |   function adjustLayout(){
  |  | 
 | 
  |  | 
  | 
 |       // ----------------------
  |  | -->  | 
 |       // Fundamental dimensions
  |  | {| class="wikitable"  | 
 |       var hh = $('#theheader').outerHeight();
  |  | ! [[Vite]]  | 
 |       var fh = $('#thefooter').outerHeight();
  |  | 
 |       var workspace_height = $(window).height() - hh - fh;
  |  | 
 |       var workspace_width = $(window).width(); 
  |  | 
 |       // ----------------------
  |  | 
 |       
  |  | 
 |       var cols = 1;
  |  | 
 |       var col_gap = 16;
  |  | 
 |       
  |  | 
 |       // Margin is based on percent of one column width, so it goes to zero before column width does.  :-)
  |  | 
 |       // Width is based on total width minus margins, then split between columns.
  |  | 
 |       var margin = ($(window).width() / cols) * .04;
  |  | 
 |       var w = ($(window).width() - (margin * (cols + 1))) / cols;
  |  | 
 |       
  |  | 
 |       var h1 = workspace_height;
  |  | 
 |       
  |  | 
 |       $('#frame1top').css({
  |  | 
 |           display:'block', 
  |  | 
 |           position:'absolute',
  |  | 
 |           left: margin * 1 + w * 0,
  |  | 
 |           top: hh,
  |  | 
 |           width: w,
  |  | 
 |           height: h1
  |  | 
 |       });
  |  | 
 |   }
  |  | 
 | </pre>
  |  | 
 | |}  |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | <!--    | 
 | ! Button handlers  |  |    | 
 | |-
  |  | 
 | | Buttons can be represented with labels; in that case, use a click handler:
  |  | 
 | <pre>
  |  | 
 |     jQuery('<label id="'+cycle.apsID+'" class="btn">MahButt</label>')
  |  | 
 |     .appendTo('#'+chartid+'-apsbar')
  |  | 
 |     .on('click', function(){
  |  | 
 |       $(location).attr('href','/v1/parameters/'+this.id+'.html');
  |  | 
 |     });
  |  | 
 | </pre>
  |  | 
 | You can also get the checked state, and prevent the button from being pressed, among other things:
  |  | 
 | <pre>
  |  | 
 |       jQuery('<label id="'+cycle.run+'" ...
  |  | 
 |       .on('click', function(e){
  |  | 
 |         var run = this.id;
  |  | 
 |         // Look for the (label->input)
  |  | 
 |         if (this.firstChild.checked)
  |  | 
 |         ...
  |  | 
 |         // To prevent checked/pressed state if desired:
  |  | 
 |         e.stopPropagation();
  |  | 
 |       }  
  |  | 
 | </pre>
  |  | 
 | Button bars are represented by labels wrapped around inputs:
  |  | 
 | <pre>
  |  | 
 |     <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn"><input type="checkbox">text...
  |  | 
 | </pre>
  |  | 
 | In that case use a change handler on the input:
  |  | 
 | <pre>
  |  | 
 |       var label = jQuery('<label class="btn btn-sm btn-'+color+'"></label>').appendTo(action_button_bar);
  |  | 
 |       var input = jQuery('<input class="run-'+cmd+'" type="checkbox" autocomplete="off" value="'+cycle.run+'">').appendTo(label)
  |  | 
 |       .on('change', cycle.owned? function(){
  |  | 
 |         patchPick(this.value,'{ "action" : "run-hold"       }');
  |  | 
 |       } : function() {
  |  | 
 |         patchPick(this.value,'{ "action" : "run-buy"        }');
  |  | 
 |       });
  |  | 
 |       var text = jQuery('<span class="glyphicon glyphicon-'+glyph+'"></span> <span class="hidden-xs">'+cmd+'</span>').appendTo(label);
  |  | 
 | </pre>
  |  | 
 | If you just need clicks from the button bar, you do NOT NEED input:
  |  | 
 | <pre>
  |  | 
 |     <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn">text...
  |  | 
 | </pre>
  |  | 
 | Then you can put the change handler right on the label:
  |  | 
 | <pre>
  |  | 
 |     var applybar = jQuery('<div id="applybar-'+cycle.run+'" class="btn-group pull-right" data-toggle="buttons" />');
  |  | 
 |     var apply_button = jQuery('<label id="apply-'+cycle.run+'" class="btn btn-sm btn-moneygreen"><span class="glyphicon glyphicon-ok"></span><span class="hidden-xs"> Apply</span></input></label>')
  |  | 
 |     .on('click', function(e) {    
  |  | 
 |     
  |  | 
 |         // Do work
  |  | 
 | 
  |  | 
  | 
 |         e.stopPropagation();
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 |     })
  |  | 
 |     .appendTo(applybar)
  |  | 
 |     ;
  |  | 
 | </pre>
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Programmatically pressing a button
  |  | 
 | |-
  |  | 
 | | You MUST DO TWO THINGS:
  |  | 
 | * Set the active class on the button/label
  |  | 
 | * Set the input to checked
  |  | 
 |  <label class="btn active">
  |  | 
 |    <input type="checkbox" autocomplete="off" checked>
  |  | 
 |    Click me
  |  | 
 |  </label>
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Handling the UNCHECK event on a pushbutton
  |  | 
 | |-
  |  | 
 | | Again you MUST SET BOTH active and checked STATES PROPERLY when creating the button (see above).  Do not set either if the button is unpressed; set both if it is pressed.
  |  | 
 | 
  |  | 
  | 
 | Then you can use a single change event:
  |  | 
 |    $("input[type='checkbox'][class='run-hold-on-buy'     ]").change(function() { patchPick(this.value,'{ "action" : "'+(this.checked?'hold-on-buy-on'     :'hold-on-buy-off'     )+'"}');  });
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Collapsible panel
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 | * Use a unique id on content div, data-target to connect panel header to content, mdm-panel-collapser so javascript can find icon, abt-scrollable-panel for margin.
  |  | 
 | * HTML for each panel (starts collapsed)
  |  | 
 | <pre>
  |  | 
 |     <div class="panel panel-default abt-scrollable-panel">
  |  | 
 |       <div class="panel-heading collapsed" data-toggle="collapse" data-target="#my-content-block-one">Software development skills<span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-down pull-right"></span></div>
  |  | 
 |       <div class="collapse" id="my-content-block-one">
  |  | 
 |         <div class="mdm-panel-body">
  |  | 
 |   
  |  | 
 |          <!-- CONTENT, can include another nested panel, just add .mdm-nested-panel to class; example: -->
  |  | 
 |   
  |  | 
 |           <div class="panel panel-default mdm-scrollable-panel mdm-nested-panel">
  |  | 
 |             <div class="panel-heading collapsed mdm-job-panel-heading" data-toggle="collapse" data-target="#toshiba-job"><p><strong>Senior Developer and Offshore Manager</strong><i> - Toshiba Global Commerce Solutions, Inc.</i><span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-up pull-right"></span></p><p><small>April 2014 – August 2015</small></p></div>
  |  | 
 |             <div class="collapse in" id="toshiba-job">
  |  | 
 |               <div class="mdm-panel-body">
  |  | 
 |                 <!-- SUBCONT(IN)ENT im so funny -->
  |  | 
 |               </div>
  |  | 
 |             </div>
  |  | 
 |           </div>
  |  | 
 |           
  |  | 
 | 
  |  | 
  | 
 |         </div>
  |  | -->  | 
 |       </div>
  |  | {| class="wikitable"  | 
 |     </div>
  |  | ! [[JSON]]  | 
 | </pre>
  |  | 
 | * For an expanded panel, simply change icon from down to up, and add "in" to content div:
  |  | 
 | <pre>
  |  | 
 |     ... glyphicon-chevron-up ...
  |  | 
 |     <div class="collapse in mdm-panel-body" id="collapseOrderItems1">
  |  | 
 | </pre>
  |  | 
 | * Define Javascript collapser once on load
  |  | 
 |   $('.collapse').on('hide.bs.collapse show.bs.collapse', 
  |  | 
 |     toggleCollapser
  |  | 
 |   );
  |  | 
 | * CSS
  |  | 
 |     .mdm-nested-panel {
  |  | 
 |       margin-top: 1em;
  |  | 
 |       margin-left: 1em;
  |  | 
 |     }
  |  | 
 |     collapse.mdm-panel-body collapsing.mdm-panel-body {
  |  | 
 |         margin: 1em;
  |  | 
 |     }
  |  | 
 | * Common reusable function:
  |  | 
 |   function toggleCollapser(e) {
  |  | 
 |       $(e.target)
  |  | 
 |           .prev('.panel-heading')
  |  | 
 |           .find('.mdm-panel-collapser')
  |  | 
 |           .toggleClass('glyphicon-chevron-down glyphicon-chevron-up');
  |  | 
 |   
  |  | 
 |     // Prevent bubble up to any parent collapsers.
  |  | 
 |     // This allows nested collapsers, whoop.
  |  | 
 |     e.stopPropagation();
  |  | 
 |   }
  |  | 
 | |}
  |  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,229: | 
Line 851: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="wikitable"  | 
 | ! D3  |  | ! [[HTML]]  | 
 | |-
  |  | 
 | |
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! ALWAYS initialize incoming data
  |  | 
 | |-
  |  | 
 | | Hard-earned data-import lessons:
  |  | 
 | * tsv() will do complete date formatting, so when you remove it to use JSON directly, you HAVE TO convert JSON timestamps of ANY kind to full Javascript date objects:
  |  | 
 |   var data = [{"date":"2017-04-01T04:00:00.000Z",...
  |  | 
 |   // MDM WE MUST MASSAGE DATA HERE
  |  | 
 |   // JSON VALUES ARE ALWAYS STRINGS, we need to change to Javascript DATE!
  |  | 
 |   // Incoming datasets need a bit of massaging.
  |  | 
 |   // We do that in a function so we can reuse it on incoming dataset updates.
  |  | 
 |   function initializeDataset(dataset) {
  |  | 
 |       // TIME ON X
  |  | 
 |       // Convert date strings to actual date values.
  |  | 
 |       // MDM MAKE SURE this matches the incoming format.
  |  | 
 |       // Adding date so we can run the chart across days without trouble.
  |  | 
 |       // var parseDate = d3.time.format("%H:%M:%S %m-%d-%Y").parse;
  |  | 
 |       // var parseDate = d3.timeParse("%Y %b %d");
  |  | 
 |       var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
  |  | 
 |       dataset.forEach(function(d) {
  |  | 
 |           // there is NO SKIPING THIS STEP, you have to get valid Javascript date objects out of JSON strings
  |  | 
 |           d.date = parseDate(d.date);
  |  | 
 |           // we WOULD have to divide all data by zero,
  |  | 
 |           // but we already grabbed post-data that was already converted
  |  | 
 |           // This WORKS but makes data / 10000 (very small numbers)
  |  | 
 |           //for (var i = 1, n = dataset.columns.length; i < n; ++i) d[dataset.columns[i]] = d[dataset.columns[i]] / 100;
  |  | 
 |       });
  |  | 
 |   }
  |  | 
 |   initializeDataset(data);
  |  | 
 |  
  |  | 
 | * tsv() will create an ARRAY but it also jams in a columns PROPERY; it takes two steps to duplicate that:
  |  | 
 |    var data = [
  |  | 
 |      {"date":"2015-06-15T04:00:00.000Z","Google Chrome":0.48090000000000005,...},
  |  | 
 |      {"date":"2015-06-22T04:00:00.000Z","Google Chrome":0.48979999999999996,...),
  |  | 
 |   ];
  |  | 
 |   data.columns = ["date","Google Chrome","Internet Explorer",...  ];
  |  | 
 | |}  |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | <!--    | 
 | ! ALWAYS set range and domain properly
  |  |    | 
 | |-
  |  |    | 
 | | You have to know your display size (range) and the min and max of your data (domain), on each axis.  From [https://bost.ocks.org/mike/bar/ Mike's docs]:
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 |  |    | 
 | 
  |  | 
  | 
 | D3’s scales specify a mapping from data space (domain) to display space (range).
  |  | -->  | 
 |         D3’s scales can also be used to interpolate many other 
  |  | {| class="wikitable"  | 
 |         types of display-space values, such as paths, color spaces 
  |  | ! [[CSS]]  | 
 |         and geometric transforms.
  |  | 
 |         
  |  | 
 |           var x = d3.scale.linear()
  |  | 
 |               .domain([0, d3.max(data)])
  |  | 
 |               .range([0, 420]);
  |  | 
 |   
  |  | 
 |         Although x here looks like an object, it is also a function 
  |  | 
 |         that returns the scaled display value in the range 
  |  | 
 |         for a given data value in the domain. 
  |  | 
 |         For example, an input value of 4 returns 40, and an input value of 16 
  |  | 
 |         returns 160. To use the new scale, simply replace the 
  |  | 
 |         hard-coded multiplication by calling the scale function:
  |  | 
 |         
  |  | 
 |         d3.select(".chart")
  |  | 
 |           .selectAll("div")
  |  | 
 |             .data(data)
  |  | 
 |           .enter().append("div")
  |  | 
 |             .style("width", function(d) { return x(d) + "px"; })
  |  | 
 |             .text(function(d) { return d; });
  |  | 
 | Watch out for Mike's examples where he (trickily) doesn't bother to set a domain because his data is in the [0..1] set, and that's the default domain, apparently.
  |  | 
 | |}  |  | |}  | 
 | |}<!--  
  |  | <!--    | 
 | 
  |  | 
  | 
 | 
  |  | 
  | 
| Line 1,306: | 
Line 872: | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 | ! RESTFul http  |  | ! SQL  | 
 | |-  |  | |-  | 
 | |  |  | |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="mw-collapsible mw-collapsed wikitable"  | 
 | ! Verbs  |  | ! Count records within a range  | 
 | |-  |  | |-  | 
 | |    |  | | This groups records into ranges, sorts by them, and gives a count, sweet:  | 
 | * get - to make requests that are simple -response may be complex  |  |     select count(*), id/1000000 as groupid from AccountHistory group by groupid;  | 
 | * post - to push a form or JSON to the server
  |  | 
 | |}  |  | |}  | 
 |  | [[postgres]] - [[sqlite]] - [[mysql]] - [[SQL Server]] - [[Robo 3T]] - [[DBeaver]] - [[pgadmin4]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,324: | 
Line 890: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="wikitable"  | 
 | ! Javascript and JQuery
  |  | ! [[Meteor]]  | 
 | |-
  |  | |}  | 
 | |
  |  | <!--    | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  |    | 
 | ! Formatting examples (numbers dates etc.)  |  | 
 | |-  |  | 
 | | 
  |  | 
 |   // Javascript sucks at dates.
  |  | 
 |   // d3 to the rescue!  
  |  | 
 |   var parseInitDate = d3.utcParse("%Y-%m-%dT%H:%M:%SZ");
  |  | 
 |   var initDate = parseInitDate(initial_managed_value_date);
  |  | 
 |   var initDateString = 
  |  | 
 |       initDate.getUTCFullYear() 
  |  | 
 |       +"/"+ (initDate.getUTCMonth()+1) 
  |  | 
 |       +"/"+ initDate.getUTCDate()
  |  | 
 |       + " " + initDate.getUTCHours() 
  |  | 
 |       + ":" + initDate.getUTCMinutes() 
  |  | 
 |       + ":" + initDate.getUTCSeconds();
  |  | 
 | 
  |  | 
  | 
 |   var rightNow = new Date;
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 |   var initToNow_DecimalDays = parseFloat(rightNow - initDate) / 864e5;  // 86,400,000 ms/day
  |  | 
 |   var initToNow_PercentOfYear = initToNow_DecimalDays / 365.0;
  |  | 
 |   var change_in_value = (parseFloat(total_managed_value) - parseFloat(initial_managed_value))/parseFloat(initial_managed_value);
  |  | 
 | 
  |  | 
  | 
 |   $('#initial_managed_value').val(initial_managed_value.toFixed(2)); 
  |  | 
 |   $('#initial_managed_value_date').val(initDateString);
  |  | 
 |   $('#change_in_value').val((change_in_value*100.0).toFixed(1)+ "%");
  |  | 
 |   $('#equivalent_apr').val((change_in_value/initToNow_PercentOfYear*100.0).toFixed(1)+ "%");
  |  | 
 |   $('#total_managed_value_readonly').val(total_managed_value.toFixed(2));
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Array vs Object lifetime
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 |         // If the JSON data were an array, we would need to slice-to-clone to keep it around:
  |  | 
 |         //     var dataCopy = data.slice();
  |  | 
 |         //     updateStockData(run,dataCopy);
  |  | 
 |         // We have an object so we can pass a reference, and it will keep the object around.
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Event handlers
  |  | 
 | |-
  |  | 
 | | Three basic methods:
  |  | 
 | * Attach a function to an event of a specific element; charts.js example:
  |  | 
 |       var label = jQuery('<label class="btn btn-sm btn-'+color+'"></label>').appendTo(action_button_bar);
  |  | 
 |       var input = jQuery('<input class="run-'+cmd+'" type="checkbox" autocomplete="off" value="'+cycle.run+'">').appendTo(label)
  |  | 
 |       .on('change', cycle.owned? function(){
  |  | 
 |         patchPick(this.value,'{ "action" : "run-hold"       }');
  |  | 
 |       } : function() {
  |  | 
 |         patchPick(this.value,'{ "action" : "run-buy"        }');
  |  | 
 |       });
  |  | 
 |       var text = jQuery('<span class="glyphicon glyphicon-'+glyph+'"></span> <span class="hidden-xs">'+cmd+'</span>').appendTo(label);
  |  | 
 | 
  |  | 
  | 
 | * Add a function handler once, for a class of elements; picks.js example:
  |  | -->  | 
 |   $("input[type='checkbox'][class='run-top'    ]").change(function() { patchPick(this.value,'{ "action" : "run-move-top"   }');  });
  |  | {| class="wikitable"  | 
 | * Event delegation: Javascript takes events and "bubbles them up" through the chain of parents; set up a handler on a parent to listen for these bubbles.
  |  | ! [[Android]]  | 
 | * At end of handler, prevent further bubbling with stopPropagation() - still finishes the current event (eg following the href of an <a>):
  |  | 
 | <pre>
  |  | 
 |     var datepicker = jQuery(
  |  | 
 |       '<div class="input-group date form-inline pull-left" data-provide="datepicker" id="datepicker'+cycle.run+'">'+
  |  | 
 |         '<input type="text" class="input-sm form-control">'+
  |  | 
 |         '<div class="input-group-addon">'+
  |  | 
 |             '<span class="glyphicon glyphicon-th"></span>'+
  |  | 
 |         '</div>'+
  |  | 
 |       '</div>'
  |  | 
 |     )
  |  | 
 |     .on('changeDate', function(e){
  |  | 
 |       alert('next '+cycle.run);
  |  | 
 |       e.stopPropagation();
  |  | 
 |     })
  |  | 
 |     ;    
  |  | 
 | </pre>
  |  | 
 | * Or use preventDefault() to stop completely:
  |  | 
 | <pre>
  |  | 
 |     var datepicker_next = jQuery(
  |  | 
 |       '<a href="">My javascript action</a>'
  |  | 
 |     )
  |  | 
 |     .on('click', function(e){
  |  | 
 |       alert('next '+cycle.run);
  |  | 
 |       e.preventDefault();
  |  | 
 |     })
  |  | 
 |     ;
  |  | 
 | </pre>
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | 
 | ! Debug clicks on ANY PAGE  |  | 
 | |-
  |  | 
 | | 
  |  | 
 | * Press F12 => Sources => Event Listener Breakpoints righthand pane => Mouse => [x] click
  |  | 
 | B O O M  we have it captured and can step into ANYTHING.
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! JWT flow
  |  | 
 | |-
  |  | 
 | | Client is easy!
  |  | 
 | <pre>
  |  | 
 |    Client application                                            API
  |  | 
 |    --------                                              -----------
  |  | 
 |         |                                                      |
  |  | 
 |         |                   GET /api/employees                 |
  |  | 
 |         |----------------------------------------------------->|
  |  | 
 |         |                     403 Forbidden                    |
  |  | 
 |         |<-----------------------------------------------------|
  |  | 
 |         |                                                      |
  |  | 
 |         |                                                      |
  |  | 
 |         |                 POST /api/authenticate               |
  |  | 
 |         |     { login: "john.doe", password: "password" }      |
  |  | 
 |         |----------------------------------------------------->|
  |  | 
 |         |                      200 Success                     |
  |  | 
 |         |             { token: "my.personal.token" }           |
  |  | 
 |         |<-----------------------------------------------------|
  |  | 
 |         |                                                      |
  |  | 
 |         |                                                      |
  |  | 
 |         |                 GET /api/employees                   |
  |  | 
 |         | Header { "Authorization: Token "my.personal.token" } |
  |  | 
 |         |----------------------------------------------------->|
  |  | 
 |         |                      200 Success                     |
  |  | 
 |         |<-----------------------------------------------------|
  |  | 
 |         |                                                      |
  |  | 
 | </pre>
  |  | 
 | |}
  |  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,454: | 
Line 910: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Node.js
  |  | 
 | |-
  |  | 
 | |
  |  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [[Node.js]] installation  |  | ! [[Arduino]]  | 
 | |}  |  | |}  | 
 |  | <!--   | 
 |  |  | 
 |  |  | 
 |  | ===========================================================================================================================================================================================================================================================================================  | 
 |  |  | 
 |  | -->  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [[Node create a new module]]  |  | ! [[Raspberry Pi]]  | 
 | |}  |  | |}  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | <!--    | 
 | ! Register with npm  |  |    | 
 | |-
  |  |    | 
 | | A one-time registration is required on a new machine if you want to publish from it:
  |  | ===========================================================================================================================================================================================================================================================================================  | 
 |  npm adduser
  |  |    | 
 |  Username: moodboom
  |  | -->  | 
 |  Password: (see private)
  |  | {| class="wikitable"  | 
 |  |  | ! [[iOS]]  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Publish a node module
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 |  sudo npm install -g # keep doing this until you are happy with local install
  |  | 
 |  # update version in package.json
  |  | 
 |  # this creates a FULL "annotated" tag, not a "lightweight" tag that doesn't show up for [git describe] - it also removes the need for a separate commit
  |  | 
 |  git tag -a 1.0.5 -m "changes include..."
  |  | 
 |  git push && git push --tags  # NOTE: bitpost has a git hook to push changes all the way up to github
  |  | 
 |  npm publish
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Update a node module's dependencies
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 |  # make sure dependency in package.json has a carat at the beginning of its version (^x means "at least" version x)
  |  | 
 |  # make sure the dependency has a new version available - completely publish it first if it is your own
  |  | 
 |  # then you can simply reinstall from within the module folder to get all dependencies upgraded
  |  | 
 |  sudo npm install -g
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Develop several node modules at once
  |  | 
 | |-
  |  | 
 | | Convert dependencies to use local packages instead of published versions, eg:
  |  | 
 |  cd ~/development/mah-haus
  |  | 
 |  npm install -S /home/m/development/thedigitalage/rad-scripts
  |  | 
 | Then reinstall everything (local dependent modules, then parent modules, pita - consider links if doing longer-term dev)
  |  | 
 |  sudo npm install -g
  |  | 
 | Then convert back to published versions as they become available (it's up to me to stabilize and publish new module versions):
  |  | 
 |  cd ~/development/mah-haus
  |  | 
 |  npm install -S rad-scripts
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! auto AWS
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 | * npm install -g aws-sdk
  |  | 
 | * Add credentials here: C:\Users\Administrator\.aws
  |  | 
 | * see existing scripts, anything is possible
  |  | 
 | |}
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | 
 | ! install bootstrap  |  | 
 | |-
  |  | 
 | | 
  |  | 
 | * npm install -g grunt-cli
  |  | 
 | * mkdir mysite && cd mysite
  |  | 
 | * npm install bootstrap
  |  | 
 | * cd node_modules/bootstrap
  |  | 
 | * npm install # to actually pull down dependencies
  |  | 
 | * grunt dist # builds and minifies so you're good to go!
  |  | 
 | |}  |  | |}  | 
 |  | <!--   | 
 |  |  | 
 |  |  | 
 |  | ===========================================================================================================================================================================================================================================================================================  | 
 |  |  | 
 |  |  | 
 |  | -->  | 
 |  | {| class="wikitable"  | 
 |  | ! [[.NET Core]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,532: | 
Line 948: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="wikitable"  | 
 | ! css  |  | ! [[Java]]  | 
 | |-
  |  | 
 | | 
  |  | 
 |   /* class="first second" */
  |  | 
 |   .first.second {}
  |  | 
 |   /* class="first" OR class="second" */
  |  | 
 |   .first, .second {}
  |  | 
 |   /* class="first second", or class="second", or class="third second", or class="second third" */
  |  | 
 |   .second {}
  |  | 
 |   /* apply to any .child at any depth under .parent */
  |  | 
 |   .parent .child {}
  |  | 
 |   /* apply to .child if it is DIRECTLY under .parent */
  |  | 
 |   .parent > .child {}
  |  | 
 |    |  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,555: | 
Line 958: | 
 | 
  |  | 
  | 
 | -->  |  | -->  | 
 | {| class="mw-collapsible mw-collapsed wikitable"  |  | {| class="wikitable"  | 
 | ! SQL  |  | ! [[Kotlin]]  | 
 | |-
  |  | 
 | |
  |  | 
 | {| class="mw-collapsible mw-collapsed wikitable"
  |  | 
 | ! Sqlite timestamp-to-readable-date query
  |  | 
 | |-
  |  | 
 | | 
  |  | 
 |  select quote, timestamp, strftime('%Y-%m-%d %H:%M:%S', datetime(timestamp, 'unixepoch')) from StockQuotes as s where s.symbol="TSLA" order by timestamp;
  |  | 
 | |}
  |  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,574: | 
Line 969: | 
 | -->  |  | -->  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [[Android]]  |  | ! [[Maven]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,584: | 
Line 979: | 
 | -->  |  | -->  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [[Java]]  |  | ! [[Scala]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,604: | 
Line 999: | 
 | -->  |  | -->  | 
 | {| class="wikitable"  |  | {| class="wikitable"  | 
 | ! [[Scala]]  |  | ! [[Go]]  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    | 
| Line 1,629: | 
Line 1,024: | 
 |   /etc/init.d/apache restart  |  |   /etc/init.d/apache restart  | 
 | |}  |  | |}  | 
 | |}
  |  | 
 | <!-- 
  |  | 
 | 
  |  | 
 | 
  |  | 
 | ===========================================================================================================================================================================================================================================================================================
  |  | 
 | 
  |  | 
 | 
  |  | 
 | -->
  |  | 
 | {| class="wikitable"
  |  | 
 | ! [[git]]
  |  | 
 | |}  |  | |}  | 
 | <!--    |  | <!--    |