mixing 3rd expansion in with the rest, sorting

--- src/hubot-humanity.coffee
+++ src/hubot-humanity.coffee
@@ -16,17 +16,8 @@
 #   Jonny Campbell (@jonnycampbell)

 questions = [
-  "_____ kid tested mother approved.",
-  "_____: good to the last drop.",
-  "_____? There's an app for that.",
-  "_____. Betcha can't have just one.",
-  "_____. High five bro.",
-  "_____. It's a trap!",
-  "_____. That's how I want to die.",
+  "(Draw 2, Pick 3) I went from ______ to _____, all thanks to _____.",
   "(Draw 2, Pick 3) _____ + _____ = _____",
-  "(Draw 2, Pick 3) I went from ______ to _____, all thanks to _____.",
-  "(Pick 2) _____ would be woefully incomplete without _____.",
-  "(Pick 2) _____ is a slippery slope that leads to _____.",
   "(Pick 2) An international tribunal has found _____ guilty of _____.",
   "(Pick 2) An the Academy Award for _____ goes to _____.",
   "(Pick 2) Before _____, all we had was_____.",
@@ -35,9 +26,9 @@
   "(Pick 2) I never truly understood _____ until I encountered _____.",
   "(Pick 2) I spent my whole life working toward _____, only to have it ruined by _____.",
   "(Pick 2) If God didn't want us to enjoy _____, he wouldn't have given us _____.",
+  "(Pick 2) In M. Night Shamalan's new movie, Bruce Willis discovers that _____ had really been _____ all along."
   "(Pick 2) In a pinch, _____ can be a suitable substitute for _____.",
   "(Pick 2) In a world ravaged by _____, our only solace is _____.",
-  "(Pick 2) In M. Night Shamalan's new movie, Bruce Willis discovers that _____ had really been _____ all along."
   "(Pick 2) Lifetime presents _____, the story of _____.",
   "(Pick 2) Michael Bay's new three-hour action epic pits _____ against _____.",
   "(Pick 2) Rumor has it that Vladimir Putin's favorite dish is _____ stuffed with _____.",
@@ -46,51 +37,70 @@
   "(Pick 2) What's the next superhero/sidekick duo?",
   "(Pick 2) When I was tripping on acid _____ turned into _____.",
   "(Pick 2) You haven't truly lived until you've experienced _____ and _____ at the same time.",
+  "(Pick 2) _____ is a slippery slope that leads to _____.",
+  "(Pick 2) _____ would be woefully incomplete without _____.",
   "A romantic candlelit dinner would be incomplete without _____.",
+  "A successful job interview begins with a firm handshake and ends with _____.",
   "After blacking out during New Year's Eve, I was awoken by _____.",
   "After months of debate, the Occupy Wall Street General Assembly could only agree on “More _____!”",
+  "After months of practice with _____, I think I'm finally ready for _____.",
   "After the earthquake, Sean Penn brought ______ to the people of Haiti.",
   "Alternative medicine is now embracing the curative powers of _____.",
   "And I would have gotten away with it, too, if it hadn't been for _____.",
+  "And what did you bring for show and tell?",
   "Anthropologists have recently discovered a primitive tribe that worships _____.",
+  "As part of his contract, Prince won't perform without _____ in his dressing room.",
+  "As part of his daily regimen, Anderson Cooper sets aside 15 minutes for _____.",
+  "BILLY MAYS HERE FOR _____.",
   "Before I run for president, I must destroy all evidence of my involvement with _____.",
-  "BILLY MAYS HERE FOR _____.",
   "But before I kill you, Mr. Bond, I must show you _____.",
+  "Call the law offices of Goldstein & Goldstein, because no one should have to tolerate _____ in the workplace.",
   "Charades was ruined for me forever when my mom had to act out _____.",
   "Coming to Broadway this season, _____: The Musical.",
   "Dear Abby, I'm having some trouble with ______ and would like your advice.",
+  "During Picasso's often-overlooked Brown Period, he produced hundreds of paintings of _____.",
+  "During high school I never really fit in until I found _____ club.",
   "During his midlife crisis, my dad got really into _____.",
-  "During Picasso's often-overlooked Brown Period, he produced hundreds of paintings of _____.",
   "During sex, I like to think about _____.",
   "Every Christmas my uncle gets drunk and tells the story about _____.",
   "Everyone down on the ground! We don't want to hurt anyone. We're just here for _____.",
+  "Finally! A service that delivers _____ right to your door.",
+  "Having problems with _____? Try _____!",
   "He who controls _____ controls the world.",
+  "Hey baby, come back to my place and I'll show you _____.",
   "How am I maintaining my relationship status.",
   "I do not know with what weapons World War III will be fought, but World War IV will be fought with ______.",
   "I drink to forget _____.",
   "I got 99 problems but _____ ain't one.",
   "I learned the hard way that you can't cheer up a grieving friend with _____.",
+  "I'm not like the rest of you. I'm too rich and busy for _____.",
   "I'm sorry professor, but I couldn't complete my homework because of _____.",
   "In 1,000 years when paper money is but a distant memory, _____ will be our currency.",
+  "In L.A. county Jail, word is you can trade 200 cigarettes for _____.",
+  "In Michael Jackson's final moments, he thought about _____.",
+  "In Rome, there are whisperings that the Vatican has a secret room devoted to _____.",
   "In an attempt to reach a wider audience, the Smithsonian Museum of Natural History has opened an interactive exhibit on _____.",
   "In his new self-produced album, Kanye West raps over the sounds of _____.",
   "In his newest and most difficult stunt, David Blaine must escape from _____.",
   "In its new tourism campaign, Detroit proclaims that it has finally eliminated _____.",
-  "In L.A. county Jail, word is you can trade 200 cigarettes for _____.",
-  "In Michael Jackson's final moments, he thought about _____.",
-  "In Rome, there are whisperings that the Vatican has a secret room devoted to _____.",
   "In the distant future, historians will agree that _____ marked the beginning of America's decline.",
   "In the new Disney Channel Original Movie, Hannah Montana struggles with _____ for the first time.",
+  "In the seventh circle of Hell, sinners must endure _____ for all eternity.",
   "Instead of coal, Santa now gives the bad children _____ .",
   "It's a pity that kids these days are all getting involved with _____.",
   "Jesus is _____.",
   "Life for American Indians was forever changed when the White Man introduced them to _____.",
+  "Listen, son. If you want to get involved with _____, I won't stop you. Just steer clear of _____.",
   "Little Miss Muffet, Sat on a tuffet, Eating her curds and _____.",
+  "Loving you is easy 'cause you're _____.",
+  "MTV's new reality show features eight washed-up celebrities living with _____.",
   "Major League Baseball has banned _____ for giving players an unfair advantage.",
   "Maybe she's born with it. Maybe it's _____.",
   "Members of New York's social elite are paying thousands of dollars just to experience _____.",
-  "MTV's new reality show features eight washed-up celebrities living with _____.",
+  "Money can't buy me love, but it can buy me _____.",
   "My country, 'tis of thee, sweet land of _____.",
+  "My gym teacher got fired for adding _____ to the obstacle course.",
+  "My life is ruled by a vicious cycle of _____ and _____.",
   "My mom freaked out when she looked at my browser history and found _____.com.",
   "My new favorite porn star is Joey “_____” McGee.",
   "Next from J.K. Rowling: Harry Potter and the Chamber of _____.",
@@ -100,9 +110,11 @@
   "Only two things in life are certain: death and _____.",
   "Science will never explain the origin of _____.",
   "Studies show that lab rats navigate mazes 50% faster after being exposed to _____",
+  "TSA guidelines now prohibit _____ on airplanes.",
   "The CIA now interrogates enemy agents by repeatedly subjecting them to _____.",
+  "The Five Stages of Grief: denial, anger, bargaining, _____ acceptance.",
+  "The blind date was going horribly until we discovered our shared interest in _____.",
   "The class field trip was completely ruined by _____.",
-  "The Five Stages of Grief: denial, anger, bargaining, _____ acceptance.",
   "The healing process began when I joined a support group for victims of _____.",
   "The socialist governments of Scandanavia have declared that access to – is a basic human right.",
   "The votes are in, and the new high school mascot is _____.",
@@ -111,16 +123,17 @@
   "This is your captain speaking. Fasten your seatbelts and prepare for _____.",
   "This month's Cosmo: 'Spice up your sex life by bringing ______ into the bedroom.'",
   "This season on Man vs. Wild, Bear Grylls must survive in the depths of the Amazon with only _____ and his wits.",
+  "To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of _____.",
   "Tonight on 20/20: What you don't know about _____ could kill you.",
-  "TSA guidelines now prohibit _____ on airplanes.",
+  "Turns out that _____-Man was neither the hero we needed nor wanted.",
   "Wake up, America. Christmas is under attack by secular liberals and their _____.",
   "War! What is it good for?",
   "What am I giving up for Lent?",
   "What are my parents hiding from me?",
   "What brought the orgy to a grinding halt?",
   "What did I bring back from Mexico?",
+  "What did Vin Diesel eat for dinner?",
   "What did the US airdrop to the children of Afghanistan?",
-  "What did Vin Diesel eat for dinner?",
   "What do old people smell like?",
   "What does Dick Cheney prefer?",
   "What don't you want to find in your Chinese food?",
@@ -131,15 +144,16 @@
   "What helps Obama unwind?",
   "What is Batman's guilty pleasure?",
   "What keeps me warm during the cold, cold winter?",
+  "What left this stain on my couch?",
   "What never fails to liven up the party?",
   "What people like _____.",
+  "What will I bring back in time to convince people that I am a powerful wizard?",
   "What will always get you laid?",
-  "What will I bring back in time to convince people that I am a powerful wizard?",
   "What would grandma find disturbing, yet oddly charming?",
+  "What's Teach for America using to inspire inner city students to succeed?",
   "What's a girl's best friend?",
   "What's my anti-drug?",
   "What's my secret power?",
-  "What's Teach for America using to inspire inner city students to succeed?",
   "What's that smell?",
   "What's that sound?",
   "What's the crustiest?",
@@ -148,65 +162,56 @@
   "What's the new fad diet?",
   "What's the next Happy Meal toy?",
   "What's there a ton of in heaven?",
-  "When all else fails, I can always masturbate to _____.",
+  "When I am President of the United States, I will create the Department of _____.",
   "When I am a billionaire, I shall erect a 50-foot statue to commemorate _____.",
-  "When I am President of the United States, I will create the Department of _____.",
   "When I pooped, what came out of my butt?",
   "When Pharaoh remained unmoved, Moses called down a Plague of _____.",
+  "When all else fails, I can always masturbate to _____.",
   "When the United States raced the Soviet Union to the moon, the Mexican government funneled millions of pesos into research on _____.",
+  "When you get right down to it, _____ is just _____.",
   "Why am I sticky?",
   "Why can't I sleep at night?",
   "Why do I hurt all over?",
-  # -------------------
-  # 3rd expansion black
-  # -------------------
-  'A successful job interview begins with a firm handshake and ends with _____.',
-  'After months of practice with _____, I think I\'m finally ready for _____.',
-  'And what did you bring for show and tell?',
-  'As part of his contract, Prince won\'t perform without _____ in his dressing room.',
-  'As part of his daily regimen, Anderson Cooper sets aside 15 minutes for _____.',
-  'Call the law offices of Goldstein & Goldstein, because no one should have to tolerate _____ in the workplace.',
-  'During high school I never really fit in until I found _____ club.',
-  'Finally! A service that delivers _____ right to your door.',
-  'Having problems with _____? Try _____!',
-  'Hey baby, come back to my place and I\'ll show you _____.',
-  'I\'m not like the rest of you. I\'m too rich and busy for _____.',
-  'In the seventh circle of Hell, sinners must endure _____ for all eternity.',
-  'Listen, son. If you want to get involved with _____, I won\'t stop you. Just steer clear of _____.',
-  'Loving you is easy \'cause you\'re _____.',
-  'Money can\'t buy me love, but it can buy me _____.',
-  'My gym teacher got fired for adding _____ to the obstacle course.',
-  'My life is ruled by a vicious cycle of _____ and _____.',
-  'The blind date was going horribly until we discovered our shared interest in _____.',
-  'To prepare for his upcoming role, Daniel Day-Lewis immersed himself in the world of _____.',
-  'Turns out that _____-Man was neither the hero we needed nor wanted.',
-  'What left this stain on my couch?',
-  'When you get right down to it, _____ is just _____.',
-  'With enough time and pressure, _____ will turn into _____.',
-  '_____. Awesome in theory, kind of a mess in practice.',
-  '_____: Hours of fun. Easy to use. Perfect for _____!',
+  "With enough time and pressure, _____ will turn into _____.",
+  "_____ kid tested mother approved.",
+  "_____. Awesome in theory, kind of a mess in practice.",
+  "_____. Betcha can't have just one.",
+  "_____. High five bro.",
+  "_____. It's a trap!",
+  "_____. That's how I want to die.",
+  "_____: Hours of fun. Easy to use. Perfect for _____!"
+  "_____: good to the last drop.",
+  "_____? There's an app for that.",
 ]

 answers = [
-  "Tweeting.",
   "72 virgins.",
   "8 oz. of sweet Mexican black-tar heroin.",
   "A 55-gallon drum of lube.",
+  "A Bop It.",
+  "A Burmese tiger pit.",
+  "A Christmas stocking full of coleslaw.",
+  "A Gypsy curse.",
+  "A Hungry-Man Frozen Christmas Dinner for One.",
+  "A PowerPoint presentation.",
+  "A Super Soaker full of cat pee.",
   "A bag of magic beans.",
   "A balanced breakfast.",
   "A beached whale.",
   "A big black dick.",
   "A big hoopla about nothing.",
   "A bigger, blacker dick.",
+  "A black male in his early 20s, last seen wearing a hoodie.",
   "A bleached asshole.",
   "A bloody pacifier.",
-  "A Bop It.",
+  "A boo-boo.",
+  "A botched circumcision.",
   "A brain tumor.",
-  "A Burmese tiger pit.",
   "A can of whoop-ass.",
-  "A Christmas stocking full of coleslaw.",
+  "A cat video so cute that your eyes roll back and your spine slides out of your anus.",
   "A clandestine butt scratch.",
   "A cooler full of organs.",
+  "A cop who is also a dog.",
   "A crappy little hand.",
   "A death ray.",
   "A defective condom.",
@@ -219,10 +224,10 @@
   "A gassy antelope.",
   "A gentle caress of the inner thigh.",
   "A good sniff.",
-  "A Gypsy curse.",
+  "A greased-up Matthew McConaughey.",
   "A homoerotic volleyball montage.",
   "A hot mess.",
-  "A Hungry-Man Frozen Christmas Dinner for One.",
+  "A lamprey swimming up the toilet and latching onto your taint.",
   "A lifetime of sadness.",
   "A look-see.",
   "A low standard of living.",
@@ -237,6 +242,7 @@
   "A murder most foul.",
   "A nuanced critique.",
   "A passionate Latino lover.",
+  "A pile of squirming bodies.",
   "A pinata full of scorpions.",
   "A really cool hat.",
   "A rival dojo",
@@ -250,41 +256,49 @@
   "A slightly shittier parallel universe.",
   "A snapping turtle biting the tip of your penis.",
   "A soulful rendition of “Ol' Man River.”",
+  "A spontaneous conga line.",
   "A squadron of moles wearing aviator goggles.",
   "A stray pube.",
-  "A Super Soaker full of cat pee.",
+  "A surprising amount of hair.",
   "A sweaty, panting leather daddy.",
   "A sweet spaceship.",
   "A thermonuclear detonation.",
   "A tiny horse.",
   "A toxic family environment.",
+  "A vagina that leads into another dimension.",
   "A visually arresting turtleneck.",
   "A web of lies.",
   "A windmill full of corpses.",
   "A woman scorned.",
   "A zesty breakfast burrito.",
+  "AIDS.",
+  "AXE body spray.",
   "Aaron Burr.",
   "Active listening.",
+  "Actually getting shot, for real.",
   "Actually taking candy from a baby.",
   "Adderall.",
   "African children.",
   "Agriculture.",
-  "AIDS.",
   "Alcoholism.",
+  "All my friends dying.",
   "All of this blood.",
   "All-you-can-eat shrimp for $4.99.",
   "Altar boys.",
   "American Gladiators.",
   "Amputees.",
+  "An M. Night Shyamalan plot twist.",
+  "An Oedipus complex.",
+  "An all-midget production of Shakespeare's Richard III.",
   "An army of skeletons.",
+  "An ass disaster.",
   "An asymmetric boob job.",
   "An erection that lasts more than four hours.",
   "An ether-soaked rag.",
   "An honest cop with nothing left to lose.",
   "An icepick lobotomy.",
-  "An M. Night Shyamalan plot twist.",
-  "An Oedipus complex.",
   "An unhinged ferris wheel rolling toward the sea.",
+  "An unstoppable wave of fire ants.",
   "Anal beads.",
   "Another goddamn vampire movie.",
   "Another shitty year.",
@@ -298,11 +312,10 @@
   "Auschwitz.",
   "Authentic Mexican cuisine.",
   "Autocannibalism.",
-  "AXE body spray.",
+  "BATMAN!!!",
   "Balls.",
   "Barack Obama.",
   "Basic human decency.",
-  "BATMAN!!!",
   "Beating your wives.",
   "Beefin' over turf.",
   "Bees?",
@@ -315,11 +328,14 @@
   "Being marginalized.",
   "Being on fire.",
   "Being rich.",
+  "Bill Clinton, naked on a bearskin rug with a saxophone.",
   "Bill Nye the Science Guy.",
   "Bingeing and purging.",
   "Bitches.",
   "Black people.",
   "Bling.",
+  "Blood farts.",
+  "Blowing some dudes in an alley.",
   "Booby-trapping the house to foil burglars.",
   "Boogers.",
   "Boris the Soviet Love Hammer.",
@@ -327,6 +343,7 @@
   "Breaking out into song and dance.",
   "Britney Spears at 55.",
   "Bullshit.",
+  "Buying the right pants to be cool.",
   "Cards Against Humanity.",
   "Carnies.",
   "Catapults.",
@@ -340,11 +357,13 @@
   "Children on leashes.",
   "Chivalry.",
   "Christopher Walken.",
+  "Chugging a lava lamp.",
   "Civilian casualties.",
   "Clams.",
   "Classist Undertones.",
   "Clearing a bloody path through Walmart with a scimitar.",
   "Coat hanger abortions.",
+  "Cock.",
   "Cockfights.",
   "College.",
   "Concealing a boner.",
@@ -353,6 +372,7 @@
   "Coughing into a vagina.",
   "Count Chocula.",
   "Crippling debt.",
+  "Crying into the pages of Sylvia Plath.",
   "Crystal meth.",
   "Cuddling.",
   "Customer service representatives.",
@@ -366,21 +386,26 @@
   "Dead parents.",
   "Death by Steven Seagal.",
   "Deflowering the princess.",
+  "Demonic possession.",
   "Dental dams.",
   "Dick Cheney.",
   "Dick fingers.",
   "Dining with cardboard cutouts of the cast of “Friends.”",
+  "Disco fever.",
   "Doin' it in the butt.",
   "Doing the right thing.",
   "Domino's Oreo Dessert Pizza.",
   "Dorito breath.",
   "Double penetration.",
   "Drinking alone.",
+  "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy.",
   "Dropping a chandelier on your enemies and riding the rope up.",
   "Dry heaving.",
   "Dwarf tossing.",
+  "Dying alone and in pain.",
   "Dying of dysentery.",
   "Dying.",
+  "Eating Tom Selleck's mustache to gain his powers.",
   "Eating all of the cookies before the AIDS bake-sale.",
   "Eating an albino.",
   "Eating an entire snowman.",
@@ -410,15 +435,18 @@
   "Fetal alcohol syndrome.",
   "Fiery poops.",
   "Figgy pudding.",
+  "Filling every orifice with butterscotch pudding.",
+  "Finding Waldo.",
   "Finding a skeleton.",
-  "Finding Waldo.",
   "Finger painting.",
   "Fingering.",
   "Firing a rifle into the air while balls deep in a squealing hog.",
+  "Fisting.",
   "Five-Dollar Footlongs.",
   "Flash flooding.",
   "Flesh-eating bacteria.",
   "Flightless birds.",
+  "Flying robots that kill people.",
   "Flying sex snakes.",
   "Foreskin.",
   "Forgetting the Alamo.",
@@ -433,6 +461,7 @@
   "Fucking up “Silent Night” in front of 300 parents.",
   "Full frontal nudity.",
   "Gandalf.",
+  "Gay aliens",
   "Geese.",
   "Genetically engineered super-soldiers.",
   "Genghis Khan.",
@@ -446,9 +475,11 @@
   "Getting naked and watching Nickelodeon.",
   "Getting really high.",
   "Getting so angry that you pop a boner.",
+  "Getting your dick stuck in a Chinese finger trap with another dick.",
   "Ghandi.",
   "Ghosts.",
   "Gift-wrapping a live hamster.",
+  "Girls that always be textin'.",
   "Giving 110%.",
   "Gladitorial combat.",
   "Glenn Beck being harried by a swarm of buzzards.",
@@ -456,10 +487,11 @@
   "Glenn Beck convulsively vomiting as a brood of crab spiders hatches in his brain and erupts from his tear ducts.",
   "Global warming.",
   "Gloryholes.",
+  "GoGurt.",
   "Goats eating cans.",
   "Goblins.",
   "God.",
-  "GoGurt.",
+  "Going around punching people.",
   "Golden showers.",
   "Good grammar.",
   "Grandma.",
@@ -469,6 +501,8 @@
   "Guys who don't call.",
   "Half-assed foreplay.",
   "Harry Potter erotica.",
+  "Having sex on top of a pizza.",
+  "Having shotguns for legs.",
   "Heartwarming orphans.",
   "Her Royal Highness, Queen Elizabeth II.",
   "Heteronormativity.",
@@ -483,13 +517,14 @@
   "Horrifying laser hair removal accidents.",
   "Horse meat.",
   "Hot Cheese.",
+  "Hot Pockets.",
   "Hot people.",
-  "Hot Pockets.",
   "Hulk Hogan.",
   "Hurricane Katrina.",
   "Immaculate conception.",
   "Inappropriate yodeling.",
   "Incest.",
+  "Indescribable loneliness.",
   "Insatiable bloodlust.",
   "Intelligent design.",
   "Intimacy problems.",
@@ -503,6 +538,7 @@
   "Jibber-jabber.",
   "John Wilkes Booth.",
   "Judge Judy.",
+  "Jumping out at people.",
   "Just the tip.",
   "Justin Bieber.",
   "Kamikaze pilots.",
@@ -519,6 +555,7 @@
   "Laying an egg.",
   "Leaving an awkward voicemail.",
   "Leprosy.",
+  "Letting everyone down.",
   "Leveling up.",
   "Licking things to claim them as your own.",
   "Literally eating shit.",
@@ -551,6 +588,7 @@
   "Morgan Freeman's voice.",
   "Mouth herpes.",
   "Mr. Clean, right behind you.",
+  "Mufasa's death scene.",
   "Muhammad (Praise Be Unto Him).",
   "Multiple stab wounds.",
   "Mutually-assured destruction.",
@@ -561,6 +599,7 @@
   "My humps.",
   "My inner demons.",
   "My machete.",
+  "My manservant, Claude.",
   "My relationship status.",
   "My sex life.",
   "My soul.",
@@ -576,8 +615,11 @@
   "Nicolas Cage.",
   "Nipple blades.",
   "Nocturnal emissions.",
+  "Not contributing to society in any meaningful way.",
   "Not giving a shit about the Third World.",
+  "Not having sex.",
   "Not reciprocating oral sex.",
+  "Nothing.",
   "Nublile slave boys.",
   "Nunchuck moves.",
   "Obesity.",
@@ -626,6 +668,7 @@
   "Pulling out",
   "Pumping out a baby every nine months.",
   "Puppies!",
+  "Putting an entire peanut butter and jelly sandwich into the VCR.",
   "Queefing.",
   "Quiche.",
   "Quivering jowls.",
@@ -636,16 +679,20 @@
   "Repression,.",
   "Republicans.",
   "Revenge fucking.",
+  "Reverse cowgirl.",
   "Riding off into the sunset.",
   "Ripping into a man's chest and pulling out his still-beating heart.",
   "Rising from the grave.",
   "Road head.",
   "Robert Downey, Jr.",
   "RoboCop.",
+  "Roland the Farter, flatulist to the king.",
   "Ronald Reagan.",
   "Roofies.",
+  "Running naked through a mall, pissing and shitting everywhere.",
   "Ryan Gosling riding in on a white horse.",
   "Same-sex ice dancing.",
+  "Samuel L. Jackson.",
   "Santa Claus.",
   "Santa's heavy sack.",
   "Sarah Palin.",
@@ -653,12 +700,14 @@
   "Scalping.",
   "Science.",
   "Scientology",
+  "Screaming like a maniac.",
   "Scrotal frostbite.",
   "Scrotum tickling.",
   "Scrubbing under the folds.",
   "Sean Connery.",
   "Sean Penn.",
   "Seduction.",
+  "Self-flagellation.",
   "Self-loathing.",
   "Seppuku.",
   "Serfdom.",
@@ -672,19 +721,25 @@
   "Shapeshifters.",
   "Shaquille O'Neal's acting career.",
   "Sharing needles.",
+  "Shutting the fuck up.",
   "Skeletor.",
+  "Slapping a racist old lady.",
   "Slow motion.",
   "Smallpox blankets.",
   "Smegma.",
+  "Sneezing, farting, and coming at the same time."
   "Sniffing glue.",
   "Socks.",
   "Soiling oneself.",
+  "Some douche with an acoustic guitar.",
+  "Some kind of bird-man.",
   "Some really fucked-up shit.",
   "Soup that is too hot.",
   "Space Jam on VHS.",
   "Space muffins.",
   "Special musical guest, Cher.",
   "Spectacular abs.",
+  "Spending lots of money.",
   "Sperm whales.",
   "Spontaneous human combustion.",
   "Spring break!",
@@ -713,90 +768,107 @@
   "Teenage pregnancy.",
   "Tentacle porn.",
   "Testicular torsion.",
+  "That ass.",
   "That thing that electrocutes your abs.",
   "The American Dream.",
   "The Big Bang.",
   "The Blood of Christ.",
-  "The boners of the elderly.",
   "The Care Bear Stare.",
   "The Chinese gymnastics team.",
+  "The Dance of the Sugar Plum Fairy.",
+  "The Donald Trump Seal of Approval.",
+  "The Fanta girls.",
+  "The Force.",
+  "The Google.",
+  "The Gulags.",
+  "The Hamburglar.",
+  "The Harlem Globetrotters.",
+  "The Holy Bible.",
+  "The Hustle.",
+  "The Jews.",
+  "The KKK.",
+  "The Kool-Aid Man.",
+  "The Land of Chocolate.",
+  "The Little Engine That Could.",
+  "The Make-A-Wish foundation.",
+  "The Pope.",
+  "The Qeusadilla Explosion Salad from Chili's.",
+  "The Rapture.",
+  "The Rev. Dr. Martin Luther King, Jr.",
+  "The South.",
+  "The Star Wars Holiday Special.",
+  "The Tempur-Pedic Swedish Sleep System.",
+  "The Three-Fifths Compromise.",
+  "The Trail of Tears.",
+  "The Ubermensch.",
+  "The Virginia Tech Massacre.",
+  "The World of Warcraft.",
+  "The boners of the elderly.",
   "The chronic.",
   "The clitoris.",
   "The corporations.",
-  "The Dance of the Sugar Plum Fairy.",
   "The day the birds attacked.",
-  "The Donald Trump Seal of Approval.",
   "The economy.",
-  "The Fanta girls.",
+  "The entire Internet.",
   "The folly of man.",
   "The forbidden fruit.",
-  "The Force.",
   "The four arms of Vishnu.",
   "The gays.",
   "The glass ceiling.",
-  "The Google.",
   "The grey nutrient broth that sustains Mitt Romney.",
-  "The Gulags.",
-  "The Hamburglar.",
   "The hardworking Mexican.",
   "The harsh light of day.",
   "The heart of a child.",
   "The hiccups.",
-  "The Holy Bible.",
   "The homosexual agenda.",
   "The human body.",
-  "The Hustle.",
   "The inevitable heat death of the universe.",
   "The invisible hand.",
-  "The Jews.",
-  "The KKK.",
-  "The Kool-Aid Man.",
-  "The Little Engine That Could.",
-  "The Make-A-Wish foundation.",
   "The mere concept of Applebee's.",
   "The milk man.",
   "The miracle of childbirth.",
   "The mixing of the races.",
+  "The moist, demanding chasm of his mouth.",
   "The new Radiohead album.",
   "The placenta.",
-  "The Pope.",
+  "The primal, ball-slapping sex your parents are having right now.",
   "The profoundly handicapped.",
-  "The Rapture.",
-  "The Rev. Dr. Martin Luther King, Jr.",
   "The shambling corpse of Larry King.",
-  "The South.",
-  "The Star Wars Holiday Special.",
+  "The systematic destruction of an entire people and their way of life.",
   "The taint; the grundle; the fleshy fun-bridge.",
-  "The Tempur-Pedic Swedish Sleep System.",
   "The terrorists.",
-  "The Three-Fifths Compromise.",
+  "The thin veneer of sitautional causality that underlies porn.",
   "The tiny calloused hands of the Chinese  children that made this card.",
   "The token minority.",
-  "The Trail of Tears.",
   "The true meaning of Christmas.",
-  "The Ubermensch.",
   "The underground railroad.",
   "The violation of our most basic human rights.",
-  "The Virginia Tech Massacre.",
-  "The World of Warcraft.",
+  "The way white people is.",
   "Third base.",
+  "Three months in the hole.",
   "Tiny nipples.",
   "Tom Cruise.",
   "Tongue.",
   "Toni Morrison's vagina.",
   "Too much hair gel.",
   "Tripping balls.",
+  "Tweeting.",
   "Two midgets shitting into a bucket.",
   "Unfathomable stupidity.",
+  "Unlimited soup, salad, and breadsticks.",
   "Upgrading homeless people to mobile hotspots.",
   "Uppercuts.",
   "Vehicular manslaughter.",
+  "Velcro",
   "Viagra.",
+  "Vietnam flashbacks.",
   "Vigilante justice.",
   "Vigorous jazz hands.",
   "Vikings.",
+  "Vomiting mid-blowjob.",
   "Waiting 'til marriage.",
   "Waking up half-naked in a Denny's parking lot.",
+  "Warm, velvety muppet sex"
   "Waterboarding.",
   "Weapons-grade plutonium.",
   "Wearing an octopus for a hat.",
@@ -816,87 +888,9 @@
   "Women's suffrage.",
   "Words, words, words.",
   "World peace.",
+  "YOU MUST CONSTRUCT ADDITIONAL PYLONS.",
   "Yeast.",
-  "YOU MUST CONSTRUCT ADDITIONAL PYLONS.",
   "Zeus's sexual appetites.",
-  # -------------------
-  # 3rd expansion white
-  # -------------------
-  "A PowerPoint presentation.",
-  "A black male in his early 20s, last seen wearing a hoodie.",
-  "A boo-boo.",
-  "A botched circumcision.",
-  "A cat video so cute that your eyes roll back and your spine slides out of your anus.",
-  "A cop who is also a dog.",
-  "A greased-up Matthew McConaughey.",
-  "A lamprey swimming up the toilet and latching onto your taint.",
-  "A pile of squirming bodies.",
-  "A spontaneous conga line.",
-  "A surprising amount of hair.",
-  "A vagina that leads into another dimension.",
-  "Actually getting shot, for real.",
-  "All my friends dying.",
-  "An all-midget production of Shakespeare's Richard III.",
-  "An ass disaster.",
-  "An unstoppable wave of fire ants.",
-  "Bill Clinton, naked on a bearskin rug with a saxophone.",
-  "Blood farts.",
-  "Blowing some dudes in an alley.",
-  "Buying the right pants to be cool.",
-  "Chugging a lava lamp.",
-  "Cock.",
-  "Crying into the pages of Sylvia Plath.",
-  "Demonic possession.",
-  "Disco fever.",
-  "Drinking ten 5-hour ENERGYs to get fifty continuous hours of energy.",
-  "Dying alone and in pain.",
-  "Eating Tom Selleck's mustache to gain his powers.",
-  "Filling every orifice with butterscotch pudding.",
-  "Fisting.",
-  "Flying robots that kill people.",
-  "Gay aliens",
-  "Getting your dick stuck in a Chinese finger trap with another dick.",
-  "Girls that always be textin'.",
-  "Going around punching people.",
-  "Having sex on top of a pizza.",
-  "Having shotguns for legs.",
-  "Indescribable loneliness.",
-  "Jumping out at people.",
-  "Letting everyone down.",
-  "Mufasa's death scene.",
-  "My manservant, Claude.",
-  "Not contributing to society in any meaningful way.",
-  "Not having sex.",
-  "Nothing.",
-  "Putting an entire peanut butter and jelly sandwich into the VCR.",
-  "Reverse cowgirl.",
-  "Roland the Farter, flatulist to the king.",
-  "Running naked through a mall, pissing and shitting everywhere.",
-  "Samuel L. Jackson.",
-  "Screaming like a maniac.",
-  "Self-flagellation.",
-  "Shutting the fuck up.",
-  "Slapping a racist old lady.",
-  "Sneezing, farting, and coming at the same time."
-  "Some douche with an acoustic guitar.",
-  "Some kind of bird-man.",
-  "Spending lots of money.",
-  "That ass.",
-  "The Harlem Globetrotters.",
-  "The Land of Chocolate.",
-  "The Qeusadilla Explosion Salad from Chili's.",
-  "The entire Internet.",
-  "The moist, demanding chasm of his mouth.",
-  "The primal, ball-slapping sex your parents are having right now.",
-  "The systematic destruction of an entire people and their way of life.",
-  "The thin veneer of sitautional causality that underlies porn.",
-  "The way white people is.",
-  "Three months in the hole.",
-  "Unlimited soup, salad, and breadsticks.",
-  "Velcro",
-  "Vietnam flashbacks.",
-  "Vomiting mid-blowjob.",
-  "Warm, velvety muppet sex"
 ]

 module.exports = (robot) ->


clean

clean

--- test/eq.coffee
+++ test/eq.coffee
@@ -1,20 +1,5 @@
 query = require("../coffee/index")
 assert = require("assert")
-
-# mongoose = require('mongoose')
-# Schema = mongoose.Schema
-# ObjectId = Schema.Types.ObjectId
-
-# model = mongoose.model 'test', new Schema
-#   str: String
-#   num: Number
-#   dat: Date
-#   bl: Boolean
-#   ar: Array
-#   obId: ObjectId
-#   t:
-#     id:Number
-

 describe "Query", ->
 #   describe "#OR /?name=+", ->


Display full account info instead of nickname

--- client/landing/app/Applications/Chat.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Chat.kdapplication/AppController.coffee
@@ -119,9 +119,10 @@
     @view = options.view

   addOnlineUser: (name) ->
-    viewInstance = @view.addRosterItem {name:name, status: "online"}
-    @participants[name] = viewInstance
-    viewInstance
+    bongo.api.JAccount.one "profile.nickname" : name, (err, account)=>
+      viewInstance = @view.addRosterItem account
+      @participants[name] = viewInstance
+      viewInstance

   removeOfflineUser: (name) ->
     viewInstance = @participants[name]
@@ -237,19 +238,30 @@
     """

 class ChannelListItemView extends KDListItemView
+  constructor: (options, data)->
+    options.cssClass = "clearfix member-suggestion-item"
+    super
+    @avatar = new AvatarView {size : width : 16, height : 16},data
+    @profileText = new ProfileTextView {shouldShowNick: yes},data
+
   viewAppended: ->
     @setTemplate @pistachio()
     @template.update()

   pistachio: ->
-    "
{{#(name)}} - {{#(status)}}
"
+    """
+      {{> @avatar}}
+      {{> @profileText}} (@#{@getData().profile.nickname})
+    """

 class ChatInputForm extends KDFormView
   constructor: ->
     super
-    @input = new KDInputView
+    @input = new KDAutoComplete
       placeholder: "Click here to reply"
       name: "chatInput"
+      label: new KDLabelView
+        title: "chatInput"
       cssClass: "fl"
       validate      :
         rules       :
@@ -261,7 +273,7 @@
       title: "Send"
       style: "clean-gray inside-button"
       callback: =>
-        input = @recipient.getView()
+        input = @mentionController.getView()
         chatMsg = input.getValue()

         input.setValue ""
@@ -270,8 +282,8 @@

         @getDelegate().emit 'ChatMessageSent', chatMsg

-    @recipient = new MentionAutoCompleteController
-      name                : "recipient"
+    @mentionController = new MentionAutoCompleteController
+      view                : @input
       itemClass           : MemberAutoCompleteItemView
       form                : @
       itemDataPath        : "profile.nickname"
@@ -279,10 +291,10 @@
       submitValuesAsText  : yes
       dataSource          : (args, callback)=>
         {inputValue} = args
-        blacklist = (data.getId() for data in @recipient.getSelectedItemData())
+        blacklist = (data.getId() for data in @mentionController.getSelectedItemData())
         @getDelegate().propagateEvent KDEventType : "AutoCompleteNeedsMemberData", {inputValue,blacklist,callback}

-    @recipientAutoComplete = @recipient.getView()
+    @mentionAutoCompleteView = @mentionController.getView()

   viewAppended: ->
     @setTemplate @pistachio()
@@ -292,7 +304,7 @@
     """

-        {{> @recipientAutoComplete}}
+        {{> @mentionAutoCompleteView}}
         {{> @sendButton}}


Add direction support

Add direction support

--- client/app/Applications/Demos.kdapplication/AppView.coffee
+++ client/app/Applications/Demos.kdapplication/AppView.coffee
@@ -28,19 +28,28 @@

   [X_COORD, Y_COORD] = [1, 2]

+  @directions        =
+    leftToRight      : ['left', 'top']
+    topToBottom      : ['top', 'left']
+
   constructor:(options={}, data)->
-    options.cssClass = 'slider'
+
+    options.cssClass   = 'slider'
     super options, data

     @pages     = []
     @_coordsY  = []
     @_currentX = 0

+    [@_pd, @_spd] = options.directions ? @constructor.directions.leftToRight
+
   addPage:(page)->
     @addSubView page

     stack = 100 * (@pages.length)
-    page.setStyle left : "#{stack}%"
+    pref  = {}
+    pref[@_pd] = "#{stack}%"
+    page.setStyle pref
     @pages.push [page]
     @_coordsY.push 0

@@ -49,16 +58,19 @@

     lastAddedPage = @pages.last
     stack = 100 * lastAddedPage.length
-    page.setStyle top : "#{stack}%"
+    pref  = {}
+    pref[@_spd] = "#{stack}%"
+    page.setStyle pref
     lastAddedPage.push page

   nextPage:->
     @jump @_currentX + 1, X_COORD
   previousPage:->
     @jump @_currentX - 1, X_COORD
-  downPage:->
+
+  nextSubPage:->
     @jump @_coordsY[@_currentX] + 1, Y_COORD
-  upPage:->
+  previousSubPage:->
     @jump @_coordsY[@_currentX] - 1, Y_COORD

   jump:(pageIndex, coord)->
@@ -66,11 +78,11 @@
     if coord is X_COORD
       pages   = @pages
       current = @_currentX
-      key     = 'left'
+      key     = @_pd
     else
       pages   = @pages[@_currentX]
       current = @_coordsY[@_currentX]
-      key     = 'top'
+      key     = @_spd

     return if pages.length <= 1

@@ -108,6 +120,7 @@
     KD.getSingleton("mainView").enableFullscreen()

     @addSubView @slider = new Slider
+      # direction : Slider.directions.topToBottom

     @slider.addPage page1 = new Page
       content  : 'Page 1'
@@ -157,22 +170,22 @@
       left     : '10px'
       bottom   : '10px'

-    @addSubView downButton = new KDButtonView
+    @addSubView previousSubPageButton = new KDButtonView
       cssClass : 'Down'
-      title    : 'Down Page'
-      callback : => @slider.downPage()
+      title    : 'Previous SubPage'
+      callback : => @slider.previousSubPage()

-    downButton.setStyle
+    previousSubPageButton.setStyle
+      position : 'absolute'
+      left     : '200px'
+      bottom   : '10px'
+
+    @addSubView nextSubPageButton = new KDButtonView
+      cssClass : 'up'
+      title    : 'Next SubPage'
+      callback : => @slider.nextSubPage()
+
+    nextSubPageButton.setStyle
       position : 'absolute'
       right    : '200px'
       bottom   : '10px'
-
-    @addSubView upButton = new KDButtonView
-      cssClass : 'up'
-      title    : 'Up Page'
-      callback : => @slider.upPage()
-
-    upButton.setStyle
-      position : 'absolute'
-      left     : '200px'
-      bottom   : '10px'


video/participants: add status to participant type

--- client/app/lib/videocollaboration/types/participant.coffee
+++ client/app/lib/videocollaboration/types/participant.coffee
@@ -1,5 +1,6 @@
 kd = require 'kd'
 helper = require '../helper'
+constants = require '../constants'

 ###*
  * Base class for representing a video participant.
@@ -21,13 +22,25 @@
     # working.
     super {}

-    @nick = nick
-    @videoData = videoData
-    @type = null
+    @nick      = nick
+    @videoData = videoData or null
+    @type      = null
+
+    @setOffline()
+
+    @subscribeToAudioChanges()  if @videoData
+
+
+  subscribeToAudioChanges: ->

     helper.subscribeToAudioChanges @videoData,
       started : => @emit 'TalkingDidStart'
       stopped : => @emit 'TalkingDidStop'
+
+
+  setOffline    : -> @status = constants.PARTICIPANT_STATUS_OFFLINE
+  setConnected  : -> @status = constants.PARTICIPANT_STATUS_CONNECTED
+  setPublishing : -> @status = constants.PARTICIPANT_STATUS_PUBLISHING

   getType: -> @type


HomeAccountIntegrationsView: simplify handling, show correct labels

--- client/home/lib/account/homeaccountintegrationsview.coffee
+++ client/home/lib/account/homeaccountintegrationsview.coffee
@@ -15,11 +15,11 @@

     super options, data

-    @linked  = {}
-    @fetched = {}
+    @supportedProviders = Object.keys @providers =
+      github: 'GitHub'
+      gitlab: 'GitLab'

-    @supportedProviders = ['gitlab', 'github']
-
+    @linked     = {}
     @switches   = {}
     @containers = {}

@@ -46,29 +46,25 @@
         cssClass: 'container hidden'

       @containers[provider].addSubView new kd.CustomHTMLView
-        partial: "#{provider.capitalize()} Integration"
+        partial: "#{@providers[provider]} Integration"

       @containers[provider].addSubView @switches[provider]

   handleForeignAuth: (provider) ->

-    @whenOauthInfoFetched provider, =>
-      kd.utils.defer -> new kd.NotificationView
-        title: "Your #{provider.capitalize()} integration is now enabled."
-      @linked[provider] = yes
-      @switches[provider].setOn no
-
-
-  whenOauthInfoFetched: (provider, callback) ->
-
-    if @fetched[provider]
-      do callback
-    else
-      @once "OauthInfoFetched.#{provider}", callback
+    @fetchOAuthInfo =>
+      if @linked[provider]
+        kd.utils.defer => new kd.NotificationView
+          title: "Your #{@providers[provider]} integration is now enabled."

   viewAppended: ->
+
+    @fetchOAuthInfo()
+
+
+  fetchOAuthInfo: (callback = kd.noop) ->

     me = whoami()
     me.fetchOAuthInfo (err, foreignAuth) =>
@@ -79,9 +75,9 @@
         @linked[provider] = foreignAuth?[provider]?
         @switches[provider].setDefaultValue @linked[provider]
         @switches[provider].makeEnabled()
-        @fetched[provider] = yes

-        @emit "OauthInfoFetched.#{provider}"
+
+      do callback

   show: ->
@@ -97,6 +93,8 @@
   link: (provider) ->

     kd.singletons.oauthController.redirectToOauthUrl { provider }
+    new kd.NotificationView
+      title: "Redirecting to #{@providers[provider]}..."

   unlink: (provider) ->
@@ -106,7 +104,7 @@
       return showError err  if err

       new kd.NotificationView
-        title: "Your #{provider.capitalize()} integration is now disabled."
+        title: "Your #{@providers[provider]} integration is now disabled."

       @linked[provider] = no


ContentSearch: Don't rely frontApp. Use passed machine and workspace instance.

--- client/ide/lib/views/contentsearch/idecontentsearch.coffee
+++ client/ide/lib/views/contentsearch/idecontentsearch.coffee
@@ -109,10 +109,9 @@

     command = "grep #{flags.join ' '} #{exclureDirs} --include=#{include} '#{searchText}' \"#{@escapeShell @rootPath}\""

-    appManager = kd.getSingleton 'appManager'
-    appManager.tell 'IDE', 'getMountedMachine', (err, machine) =>
-      machine.getBaseKite().exec({ command })
-      .then  (res) => @formatOutput machine, res.stdout, @bound 'createResultsView'
+    { machine } = @getData()
+    machine.getBaseKite().exec({ command })
+      .then  (res) => @formatOutput res.stdout, @bound 'createResultsView'
       .catch (err) =>
         @showWarning 'Something went wrong, please try again.'
         kd.warn err
@@ -125,11 +124,9 @@
   grepEscapeRegExp: (str) -> str.replace /[[\]{}()*+?.,\\^$|#\s"']/g, '\\$&'

-  formatOutput: (machine, output, callback = kd.noop) ->
+  formatOutput: (output, callback = kd.noop) ->

     return @showWarning 'Something went wrong, please try again.', yes  if output.stderr
-
-    @machine = machine

     # Regexes
     mainLineRegex           = /^:?([\s\S]+):(\d+):([\s\S]*)$/
@@ -177,9 +174,10 @@

   createResultsView: (result, stats) ->

-    {searchText}    = this
+    { searchText }  = this
     isCaseSensitive = @caseToggle.getValue()
-    resultsView     = new IDEContentSearchResultView { result, stats, searchText, isCaseSensitive, @machine }
+    { machine }     = @getData()
+    resultsView     = new IDEContentSearchResultView { result, stats, searchText, isCaseSensitive, machine }

     @emit 'ViewNeedsToBeShown', resultsView
     @destroy()


virtualization controller: begin reimplementing delete vm functionality

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -62,46 +62,56 @@
   reinitialize:(vm, callback)->
     @_runWrapper 'vm.reinitialize', vm, callback

-  remove: do->
-
-    deleteVM = (vm, cb)->
-      KD.remote.api.JVM.removeByHostname vm, (err)->
-        return cb err  if err
-        KD.getSingleton("finderController").unmountVm vm
-        KD.getSingleton("vmController").emit 'VMListChanged'
-        cb null
-
-    (vm, callback=noop)->
-      KD.remote.api.JVM.fetchVmInfo vm, (err, vmInfo)=>
-        if vmInfo
-
-          if vmInfo.underMaintenance is yes
-            message = "Your VM is under maintenance, not allowed to delete."
-            new KDNotificationView title : message
-            callback {message}
-
-          else if vmInfo.planCode is 'free'
-            {hostnameAlias} = vmInfo
-            vmPrefix = (@parseAlias hostnameAlias)?.prefix or hostnameAlias
-
-            modal = new VmDangerModalView
-              name     : vmInfo.hostnameAlias
-              title    : "Destroy '#{hostnameAlias}'"
-              action   : "Destroy my VM"
-              callback : =>
-                deleteVM vm, callback
-                new KDNotificationView title:'Successfully destroyed!'
-                modal.destroy()
-            , vmPrefix
-          else
-            paymentController = KD.getSingleton('paymentController')
-            paymentController.deleteVM vmInfo, (state)->
-              return callback null  unless state
-              deleteVM vm, callback
+  fetchVmInfo: (vm, callback) ->
+    { JVM } = KD.remote.api
+
+    JVM.fetchVmInfo vm, callback
+
+  confirmVmDeletion: (vmInfo, callback = (->)) ->
+    { hostnameAlias } = vmInfo
+
+    vmPrefix = (@parseAlias hostnameAlias)?.prefix or hostnameAlias
+
+    modal = new VmDangerModalView
+      name     : vmInfo.hostnameAlias
+      title    : "Destroy '#{hostnameAlias}'"
+      action   : "Destroy my VM"
+      callback : =>
+        @deleteVmByHostname hostnameAlias, callback
+        new KDNotificationView title:'Successfully destroyed!'
+        modal.destroy()
+    , vmPrefix
+
+  deleteVmByHostname: (hostnameAlias, callback) ->
+    { JVM } = KD.remote.api
+
+    JVM.removeByHostname hostnameAlias, (err)->
+      return callback err  if err
+
+      KD.getSingleton("finderController").unmountVm hostnameAlias
+      KD.getSingleton("vmController").emit 'VMListChanged'
+      callback null
+
+  remove: (vm, callback=noop)->
+    @fetchVmInfo vm, (err, vmInfo)=>
+      return  if KD.showError err
+
+      if vmInfo
+
+        if vmInfo.underMaintenance is yes
+          message = "Your VM is under maintenance, not allowed to delete."
+          new KDNotificationView title: message
+          callback { message }
+
+        else if vmInfo.planCode is 'free'
+          @confirmVmDeletion vmInfo
+
         else
-          new KDNotificationView
-            title: 'Failed to remove!'
-          callback message: "No such VM!"
+          @confirmVmDeletion vmInfo, -> debugger
+
+      else
+        new KDNotificationView title: 'Failed to remove!'
+        callback { message: "No such VM!" }

   info:(vm, callback)->
     [callback, vm] = [vm, callback]  if 'function' is typeof vm
@@ -193,24 +203,24 @@
     group = KD.getSingleton("groupsController").getCurrentGroup()
     group.createVM {type, planCode}, vmCreateCallback

-  fetchVMs: do (waiting = []) ->
-    (force, callback)->
-      [callback, force] = [force, callback]  unless callback?
-      return  unless callback?
-
-      if @vms.length
-        return @utils.defer => callback null, @vms
-
-      return if not force and (waiting.push callback) > 1
-
-      KD.remote.api.JVM.fetchVms (err, vms)=>
-        @vms = vms  unless err
-        if force
-        then callback err, vms
-        else
-          cb err, vms  for cb in waiting
-          waiting = []
-
+  fetchVMs: do (waiting = []) -> (force, callback)->
+    [callback, force] = [force, callback]  unless callback?
+
+    return  unless callback?
+
+    if @vms.length
+      @utils.defer => callback null, @vms
+      return
+
+    return  if not force and (waiting.push callback) > 1
+
+    KD.remote.api.JVM.fetchVms (err, vms)=>
+      @vms = vms  unless err
+      if force
+      then callback err, vms
+      else
+        cb err, vms  for cb in waiting
+        waiting = []

   fetchGroupVMs:(callback = noop)->
     if @groupVms.length > 0
@@ -341,13 +351,9 @@

     productForm = new VmProductForm

-    pay = KD.getSingleton 'paymentController'
-
-    KD.whoami().fetchPlansAndSubscriptions ['vm'], (err, plansAndSubs) =>
-      return  if KD.showError err
-
-      { subscriptions } = pay.groupPlansBySubscription plansAndSubs
-
+    payment = KD.getSingleton 'paymentController'
+
+    payment.fetchSubscriptionsWithPlans ['vm'], (err, subscriptions) ->
       productForm.setCurrentSubscriptions subscriptions

     productForm.on 'PackOfferingRequested', (subscription) ->
@@ -382,15 +388,19 @@
     { JVM } = KD.remote.api

     { plan, pack } = productData
-
+
+    payment = KD.getSingleton 'paymentController'
+
     if paymentMethod and not subscription

-      return plan.subscribe paymentMethod.paymentMethodId, (err, subscription) =>
+      plan.subscribe paymentMethod.paymentMethodId, (err, subscription) =>
         return  if KD.showError err

         @provisionVm { subscription, productData }

-    subscription.debit pack, (err, nonce) =>
+      return
+
+    payment.debitSubscription subscription, pack, (err, nonce) =>
       return  if KD.showError err

       JVM.createVmByNonce nonce, (err, vm) =>
@@ -543,8 +553,8 @@
     # fix this one on radios value cannot have some chars and that's why i keep index as the value
     descriptions = plans.map (plan) -> plan.description
     hostTypes    = plans.map (plan, i)->
-      title       : plan.description.title
-      value       : i
+      title      : plan.description.title
+      value      : i
       feeAmount  : (plan.feeAmount / 100).toFixed 0

     { descriptions, hostTypes }


add voip to list of banned software to run on vms

--- client/landing/site.landing/coffee/legal/userpolicy.coffee
+++ client/landing/site.landing/coffee/legal/userpolicy.coffee
@@ -72,7 +72,7 @@

           Malicious content;

-
+

           Any type of proxy services including but not exclusively VPN servers, TOR servers, etc.

@@ -103,6 +103,11 @@

           Pornography and Sexually Explicit Content;
+

+
+

+          Use of any kind of VOIP related software, including but not limited to Teamspeak,
+          Asterisk, FreePBX;


Zoom the camera the best way we can to support all kind of meshes

--- lib/gltf-editor-view.coffee
+++ lib/gltf-editor-view.coffee
@@ -36,12 +36,20 @@
       scene.clearColor = new BABYLON.Color4 0, 0, 0, 0

       if not scene.activeCamera
-        scene.activeCamera = new BABYLON.ArcRotateCamera('Camera', Math.PI, Math.PI / 8, 150, BABYLON.Vector3.Zero(), scene)
-        scene.activeCamera.attachControl(@canvas.context, true)
-        scene.activeCamera.zoomOn scene.meshes, false
-        scene.activeCamera.allowUpsideDown = false
-        scene.activeCamera.wheelPrecision = 1000 / scene.activeCamera.maxZ
-        scene.activeCamera.maxZ *= 4
+        camera = new BABYLON.ArcRotateCamera('Camera', 3 * Math.PI / 2, -Math.PI / 2, 50, BABYLON.Vector3.Zero(), scene)
+        camera.attachControl(@canvas.context, true)
+
+        camera.zoomOn scene.meshes, false
+        if (camera.maxZ < camera.minZ)
+          [camera.maxZ, camera.minZ] = [camera.minZ * 10, camera.maxZ / 10]
+        else
+          camera.maxZ = Math.max(10, camera.maxZ * 10)
+
+        camera.wheelPrecision = 20000 / camera.maxZ
+        camera.allowUpsideDown = false
+        camera.lowerRadiusLimit = camera.minZ * 2
+
+        scene.activeCamera = camera

       light = new BABYLON.HemisphericLight('hemi', new BABYLON.Vector3(0, 1, 0), scene)


change name of test cancelation-view -> cancelation-options-view

--- services/web/public/coffee/main/subscription-dashboard.coffee
+++ services/web/public/coffee/main/subscription-dashboard.coffee
@@ -123,7 +123,7 @@
 			$scope.inflight = true
 			$http.post("/user/subscription/cancel", body)
 				.success ->
-					sixpack.convert 'cancelation-view', ->
+					sixpack.convert 'cancelation-options-view', ->
 						location.reload()
 				.error ->
 					console.log "something went wrong changing plan"
@@ -131,7 +131,7 @@

 		$scope.switchToCancelationView = ->
-			sixpack.participate 'cancelation-view', ['basic', 'downgrade-options'], (view, rawResponse)->
+			sixpack.participate 'cancelation-options-view', ['basic', 'downgrade-options'], (view, rawResponse)->
 				$scope.view = "cancelation"
 				$scope.sixpackOpt = view


handle cancelled selector correctly

handle cancelled selector correctly

--- lib/indent-detective.coffee
+++ lib/indent-detective.coffee
@@ -78,6 +78,7 @@
     items = [{text: "1 Space", length: 1}]
     items.push(({text: "#{n} Spaces", length: n} for n in [2, 3, 4, 6, 8])...)
     items.push {text: "Tabs"}
-    s = selector.show items, ({length}) =>
+    s = selector.show items, ({length}={}) =>
+      return unless length?
       @setSettings atom.workspace.getActiveTextEditor(), [length, length?]
       status.update()


find property implementation

find property implementation

--- src/jquery_adapter.coffee
+++ src/jquery_adapter.coffee
@@ -29,4 +29,4 @@
     jQuery(element).trigger(event, params)

   get_property(event, properties...) ->
-    for p in properties then $(event.target).prop(p)+    for p in properties then jQuery(event.target).prop(p)


More refactoring

More refactoring

--- src/map-generation.coffee
+++ src/map-generation.coffee
@@ -52,17 +52,17 @@

 exports.generateCellularAutomata = (game, w, h) ->
 	{border, randomTiles} = exports.mapGen
+	generation = exports.cellularAutomataGeneration
+	_randomTile = randomTiles (-> game.random.mersenneTwister.rnd()), initProb

 	map = new Map game, w, h

 	initProb = 0
 	rules = []

-	_randomTile = randomTiles (-> game.random.mersenneTwister.rnd()), initProb
-
 	mapData = exports.createMapData w, h, (a...) -> (border a...) ? (_randomTile a...)

 	for rule in rules
-		mapData = exports.cellularAutomataGeneration mapData, w, h, rule
+		mapData = generation mapData, w, h, rule

 	map.data = mapData


Refactor

Refactor

--- src/marbles/dom.js.coffee
+++ src/marbles/dom.js.coffee
@@ -150,10 +150,8 @@
       @setStyle(el, name, value)

   setInnerText: (el, value) ->
-    if typeof el.innerText == undefined
-      el.textContent = value
-    else
-      el.innerText = value
+    el.textContent = value
+    el.innerText = value
     value

   parentNodes: (el) ->


fix class name in ejs

fix class name in ejs

--- root/lib/index.coffee
+++ root/lib/index.coffee
@@ -7,7 +7,7 @@
 # http://roots.readthedocs.org/en/latest/extensions.html

 module.exports = ->
-  class <%= S("-#{str}").collapseWhitespace().camelize().s %>
+  class <%= S('-' + name).collapseWhitespace().camelize().s %>

     constructor: (@roots) ->
       # console.log @roots


Removed dead code

Removed dead code

--- core/app/assets/javascripts/base/registration_forms.coffee
+++ core/app/assets/javascripts/base/registration_forms.coffee
@@ -1,69 +0,0 @@
-# WARNING: This code is only used for the registration forms
-#
-# Though it was originally generic, it isn't anymore. It should be phased out
-# along with Bootstrap
-
-setErrorsOnFields = (formId, fieldErrors) ->
-  _.forEach fieldErrors, (errorMessage, field) ->
-    el = $("#" + formId + "_user_" + field)
-
-    el.addClass "error"
-    showErrorPopover el, "#{errorMessage}", formId
-
-showErrorPopover = (element, errorMessage, formId) ->
-  if formId == "bottom_registration_form"
-    placement = "top"
-  else
-    placement = "right"
-
-  options =
-    title: ""
-    content: errorMessage
-    trigger: "manual"
-    placement: placement
-
-  element.popover options
-  element.popover "show"
-
-  reposition_elements_popover(element, placement)
-
-reposition_elements_popover = (element, placement)->
-  position = element.position()
-  popover = element.data('popover').$tip
-  popover.css(color: 'black')
-  if placement == 'right'
-    popover.css
-      top: 0 + position.top - 5
-      left: element.width() + position.left + 10
-  else if placement == 'top'
-    if element.selector == "#bottom_registration_form_user_username"
-      popover.css
-        top: position.top-popover.height() + 4
-        left: position.left - 30
-    else
-      popover.css
-        top: position.top-popover.height() + 4
-        left: position.left + 0
-  else
-    console.error 'placement not supported'
-  element.after(popover)
-
-bindRegistrationForm = (formId) ->
-  form = $('#' + formId)
-
-  form.on "ajax:success", (event, data, status, response) ->
-    window.location = data.location
-
-  form.on "ajax:error", (event, response, error) ->
-    clearPreviousErrorsForForm(form)
-
-    fieldErrors = JSON.parse(response.responseText)
-    setErrorsOnFields formId, fieldErrors
-
-  clearPreviousErrorsForForm = (form) ->
-    _.each (form.find "input.text"), (el) ->
-      $(el).removeClass 'error'
-      $(el).popover "destroy"
-
-bindRegistrationForm "top_registration_form"
-bindRegistrationForm "bottom_registration_form"


Added support for localStorage["sync_delay"]

localStorage["sync_delay"] can either be: `true`, `"random"` or a number

--- core/app/backbone/contrib/monkey_backbone_sync_json.coffee
+++ core/app/backbone/contrib/monkey_backbone_sync_json.coffee
@@ -1,10 +1,35 @@
+DEFAULT_SYNC_DELAY = 0
+
 getValue = (object, prop) ->
-    return null if (!(object && object[prop]))
+  return null if (!(object && object[prop]))

-    if _.isFunction(object[prop])
-      object[prop]()
-    else
-      object[prop]
+  if _.isFunction(object[prop])
+    object[prop]()
+  else
+    object[prop]
+
+wrap_success_and_error = (options) ->
+  sync_delay = get_sync_delay()
+
+  return if sync_delay < 1
+
+  old_success = options.success
+  old_error = options.error
+
+  options.success = (args...) -> _.delay old_success, sync_delay, args...
+  options.error = (args...) -> _.delay old_error, sync_delay, args...
+
+get_sync_delay = ->
+  sync_delay = getLocalStorage("sync_delay")
+
+  if not sync_delay
+    0
+  else if parseInt(sync_delay, 10) > 0
+    parseInt(sync_delay, 10)
+  else if sync_delay == "random"
+    Math.random() * 1000
+  else
+    DEFAULT_SYNC_DELAY

 old_sync = Backbone.sync
 new_sync = (method, model, options) ->
@@ -16,6 +41,8 @@
   unless url.match /\.json($|\?)/
     url = url + '.json'

+  wrap_success_and_error(options)
+
   old_sync(method, model, _.extend({},options, {url: url}))

 Backbone.sync = new_sync


add command 'julia-client:reset-workspace'

--- lib/package/commands.coffee
+++ lib/package/commands.coffee
@@ -30,6 +30,11 @@
         @withInk ->
           boot()
           juno.runtime.evaluation.toggleMeta 'methods'
+      'julia-client:reset-workspace': =>
+        if juno.connection.client.isActive()
+          juno.connection.client.rpc('clear-workspace')
+        else
+          boot()
       'julia:select-block': =>
         juno.misc.blocks.select()


remove arg childIndex in applyTextWithOrg() in preferences-util.coffee

--- lib/preferences-util.coffee
+++ lib/preferences-util.coffee
@@ -4,16 +4,12 @@
     localized = elem.getAttribute('data-localized') if elem
     return localized == 'true'

-  @applyTextWithOrg = (elem, text, childIndex) ->
-    childIndex = if (parseInt(atom.getVersion().split('.')[1]) > 15) then null else childIndex
+  @applyTextWithOrg = (elem, text) ->
     return unless text
     return unless elem
-    before = if not childIndex then String(elem.textContent) else String(elem.childNodes[childIndex].textContent)
+    before = String(elem.textContent)
     return if before == text
-    if not childIndex
-      elem.innerHTML = text    # NOTE text may contain HTML
-    else
-      elem.childNodes[childIndex].textContent = text
+    elem.innerHTML = text    # NOTE text may contain HTML
     elem.setAttribute('title', text)
     elem.setAttribute('data-localized', 'true')
     elem.setAttribute('data-before-localized', before)
@@ -33,7 +29,7 @@
       el = @getTextMatchElement(sv, '.section-heading', sh._label)
       continue unless el
       if !@isAlreadyLocalized(el) || force
-        @applyTextWithOrg(el, sh.value, sh._childIndex)
+        @applyTextWithOrg(el, sh.value)
     for sh in window.I18N.defS.Settings.subSectionHeadings
       el = @getTextMatchElement(sv, '.sub-section-heading', sh._label)
       continue unless el


fix a bug in an error message in compute that I noticed in production logs

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -897,7 +897,7 @@
                         state = {state:@_state, time:@_state_time, error:@_state_error ? null}
                         @compute_server.database.table('projects').get(@project_id).update(state:state).run (err) =>
                             if err
-                                dbg("Error setting state of #{project_id} in database -- #{err}")
+                                dbg("Error setting state of #{@project_id} in database -- #{err}")

                         f = () =>
                             dbg("clearing cache due to timeout")


compute: add function for bulk setting of quotas

--- salvus/compute.coffee
+++ salvus/compute.coffee
@@ -679,6 +679,33 @@
                     f1_timer = setTimeout(f1, opts.timeout)
                 async.mapLimit(target, opts.limit, f, cb)
         ], opts.cb)
+
+    ###
+    projects = require('misc').split(fs.readFileSync('/home/salvus/work/2015-amath/projects').toString())
+    require('compute').compute_server(db_hosts:['smc0-us-central1-c'],keyspace:'salvus',cb:(e,s)->console.log(e); s.set_quotas(projects:projects, cores:8, cb:(e)->console.log("DONE",e)))
+    ###
+    set_quotas: (opts) =>
+        opts = defaults opts,
+            projects     : required    # array of project id's
+            disk_quota   : undefined
+            cores        : undefined
+            memory       : undefined
+            cpu_shares   : undefined
+            network      : undefined
+            mintime      : undefined  # in seconds
+            cb           : required
+        projects = opts.projects
+        delete opts.projects
+        cb = opts.cb
+        delete opts.cb
+        f = (project_id, cb) =>
+            o = misc.copy(opts)
+            o.cb = cb
+            @project
+                project_id : project_id
+                cb         : (err, project) =>
+                    project.set_quotas(o)
+        async.mapLimit(projects, 10, f, cb)

     # require('compute').compute_server(db_hosts:['smc0-us-central1-c'],keyspace:'salvus',cb:(e,s)->console.log(e);s.move_course_to_host(host:'compute0-amath-us', course:"2015_Spring.course",cb:(e)->console.log("DONE",e)))
     move_course_to_host: (opts) =>


make solarized the default console theme...

--- salvus/message.coffee
+++ salvus/message.coffee
@@ -527,7 +527,7 @@
     connect_Google     : ''
     connect_Dropbox    : ''
     autosave           : 180
-    terminal           : {font_size:14, color_scheme:'default', font:'droid-sans-mono'}
+    terminal           : {font_size:14, color_scheme:'solarized-light', font:'droid-sans-mono'}

 # client <--> hub
 message(


fix little mistake in temporary migration code

--- salvus/storage.coffee
+++ salvus/storage.coffee
@@ -3914,7 +3914,7 @@
         (cb) ->
             f = (project_id, c) ->
                 s = {project_id:project_id}
-                status.push(s)
+                opts.status.push(s)
                 cs = db.chunked_storage(id:project_id)
                 t = misc.walltime()
                 cs.sync


Subscribe to buffer

Subscribe to buffer

--- lib/spell-check-view.coffee
+++ lib/spell-check-view.coffee
@@ -39,7 +39,7 @@
     @destroyViews()

     if @buffer?
-      @buffer.off 'contents-modified', @updateMisspellings
+      @unsubscribe(@buffer)
       @buffer = null

   subscribeToBuffer: =>
@@ -47,7 +47,7 @@

     if @spellCheckCurrentGrammar()
       @buffer = @editorView.getEditor().getBuffer()
-      @buffer.on 'contents-modified', @updateMisspellings
+      @subscribe @buffer, 'contents-modified', @updateMisspellings
       @updateMisspellings()

   spellCheckCurrentGrammar: ->


cleanup

cleanup

--- lib/text-object-pair.coffee
+++ lib/text-object-pair.coffee
@@ -17,7 +17,7 @@
   else
     new RegExp("(#{_.escapeRegExp(open)})|(#{_.escapeRegExp(close)})", 'g')

-findPair = (editor, from, pair, direction, fn) ->
+scanPair = (editor, from, pair, direction, fn) ->
   pattern = getPatternForPair(pair)
   switch direction
     when 'forward'
@@ -26,6 +26,23 @@
     when 'backward'
       scanRange = new Range([0, 0], from)
       editor.backwardsScanInBufferRange(pattern, scanRange, fn)
+
+findPairRange = ({editor, from, pair, which, direction, filters}, fn) ->
+  stack = []
+  range = null
+
+  scanPair editor, from, pair, direction, (event) ->
+    if filters? and filters.some((filter) -> filter(event))
+      return
+
+    if getPairState(editor, event) isnt which
+      stack.push(event)
+    else
+      topEvent = stack.pop()
+      if fn(stack, topEvent)
+        range = event.range
+        event.stop()
+  range

 # Take start point of matched range.
 backSlashPattern = _.escapeRegExp('\\')
@@ -39,6 +56,28 @@
       escaped = true
   escaped

+# Return 'open' or 'close'
+getPairState = (editor, event) ->
+  {matchText, range, match} = event
+  switch match.length
+    when 2
+      pairStateInBufferRange(editor, event)
+    when 3
+      switch
+        when match[1] then 'open'
+        when match[2] then 'close'
+
+pairStateInBufferRange = (editor, {matchText, range}) ->
+  text = getLineTextToBufferPosition(editor, range.end)
+  escapedChar = _.escapeRegExp(matchText)
+  bs = backSlashPattern
+  patterns = [
+    "#{bs}#{bs}#{escapedChar}"
+    "[^#{bs}]?#{escapedChar}"
+  ]
+  pattern = new RegExp(patterns.join('|'))
+  ['close', 'open'][(countChar(text, pattern) % 2)]
+
 # -------------------------
 class Pair extends TextObject
   @extend(false)
@@ -50,27 +89,6 @@
   pair: null
   wise: 'characterwise'
   supportCount: true
-
-  # Return 'open' or 'close'
-  getPairState: ({matchText, range, match}) ->
-    switch match.length
-      when 2
-        @pairStateInBufferRange(range, matchText)
-      when 3
-        switch
-          when match[1] then 'open'
-          when match[2] then 'close'
-
-  pairStateInBufferRange: (range, char) ->
-    text = getLineTextToBufferPosition(@editor, range.end)
-    escapedChar = _.escapeRegExp(char)
-    bs = backSlashPattern
-    patterns = [
-      "#{bs}#{bs}#{escapedChar}"
-      "[^#{bs}]?#{escapedChar}"
-    ]
-    pattern = new RegExp(patterns.join('|'))
-    ['close', 'open'][(countChar(text, pattern) % 2)]

   getFilters: (from) ->
     filters = []
@@ -91,46 +109,18 @@
     filters

   findOpen: (from) ->
-    stack = []
-    openRange = null
-    filters = @getFilters(from)
-
-    findPair @editor, from, @pair, 'backward', (event) =>
-      return if filters.some((filter) -> filter(event))
-
-      {range, stop} = event
-      if @getPairState(event) is 'close'
-        stack.push(event)
-      else
-        stack.pop()
-        openRange = range if stack.length is 0
-      stop() if openRange?
-    openRange
+    options = {@editor, from, @pair, which: 'open', direction: 'backward', filters: @getFilters(from)}
+    findPairRange options, (stack, openEvent) ->
+      stack.length is 0

   findClose: (from) ->
-    stack = []
-    closeRange = null
-    filters = @getFilters(from)
-
     isValidOpen = ({range}) =>
-      openStart = range.start
-      if @allowForwarding
-        openStart.row is from.row
-      else
-        openStart.isEqual(from)
-
-    findPair @editor, from, @pair, 'forward', (event) =>
-      return if filters.some((filter) -> filter(event))
-
-      {range, stop} = event
-      if @getPairState(event) is 'open'
-        stack.push(event)
-      else
-        openEvent = stack.pop()
-        if stack.length is 0 and ((not openEvent?) or isValidOpen(openEvent))
-          closeRange = range
-          stop()
-    closeRange
+      {start} = range
+      start.isEqual(from) or (@allowForwarding and start.row is from.row)
+
+    options = {@editor, from, @pair, which: 'close', direction: 'forward', filters: @getFilters(from)}
+    findPairRange options, (stack, openEvent) ->
+      stack.length is 0 and ((not openEvent?) or isValidOpen(openEvent))

   getPairInfo: (from) ->
     pairInfo = null


alans the grammar police

alans the grammar police

--- scripts/frover.coffee
+++ scripts/frover.coffee
@@ -1,3 +1,3 @@
 module.exports = (robot) ->
   robot.hear /frover /i, (msg) ->
-    msg.send 'The names Grover BITCH!'+    msg.send 'The name\'s Grover BITCH!'


Inspect points more.

Inspect points more.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -116,7 +116,7 @@

             pointcount = robot.brain.get 'winners'
             savescore = ["#{username} has #{attrs.points}" for username, attrs of pointcount]
-            msg.send "#{Util.inspect(points[username])}"
+            msg.send "#{Util.inspect(points)}"

     # robot.respond /top (\d*)$/i, (msg) ->
     #     if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])


Stick to username standard.

Stick to username standard.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -89,6 +89,6 @@
             timerId = setInterval(timerMethod, 10000)

             timerMethod = ->
-                for person in people
-                    points[person] =+ 5
+                for username in people
+                    points[username] =+ 5
                 return


No idea if that's even a thing.

No idea if that's even a thing.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -103,9 +103,7 @@
             chat = JSON.parse(body)
             people = flatten([chat.chatters.moderators, chat.chatters.staff, chat.chatters.admins, chat.chatters.global_mods, chat.chatters.viewers])

-            for username in people
-                if username = 'awebot'
-                    continue
+            for username in people not 'awebot'
                 points[username] ?= 0
                 points[username] += 5
                 save(robot)


member reputation is moved to another branch

--- workers/social/lib/social/models/graph/graph.coffee
+++ workers/social/lib/social/models/graph/graph.coffee
@@ -434,18 +434,6 @@
         LIMIT #{limit}
         """
     @queryMembers query, {}, callback
-
-  fetchMembersReputation:(options, callback)->
-    {memberId} = options
-    query = """
-      START member=node:koding("id:#{memberId}")
-      MATCH member-[:author]-items-[r:like]-likedItems
-      RETURN count(likedItems) as count
-    """
-    @db.query query, options, (err, results) ->
-      if err then throw err
-      count = if results and results[0]['count'] then results[0]['count'] else 0
-      callback null, count

   fetchInvitations:(options, callback)->
     {groupId, status, timestamp, requestLimit, search} = options


added server side check that group creator needs to be logged in

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -259,10 +259,14 @@
           queue.next()

     create = secure (client, formData, callback)->
+      JAccount = require '../account'
+      {delegate} = client.connection
+      unless delegate instanceof JAccount
+        return callback new KodingError 'Access denied.'
+
       JPermissionSet = require './permissionset'
       JMembershipPolicy = require './membershippolicy'
       JName = require '../name'
-      {delegate} = client.connection
       group                 = new this formData
       permissionSet         = new JPermissionSet
       defaultPermissionSet  = new JPermissionSet


Social: added a new function for creatign default channel for teams

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1439,6 +1439,13 @@

     return @createSocialAPIChannel client, options, callback

+  createDefaultChannel:(client, options, callback) ->
+    options.name = @slug
+    options.varName = 'socialApiDefaultChannelId'
+    options.typeConstant = 'topic'
+
+    return @createSocialAPIChannel client, options, callback
+

   createSocialAPIChannel:(client, options, callback) ->
     { varName, name, typeConstant, creatorId, privacyConstant } = options


We don’t use email anymore!

We don’t use email anymore!

--- scripts/moca.coffee
+++ scripts/moca.coffee
@@ -1,5 +1,5 @@
 # Description:
-#   A start on some basic MoCA customisation
+#   A start on some basic MoCA customisation
 #
 # Notes:
 #   they are pretty basic at the moment, please improve by reading the scripting documentation at:
@@ -20,3 +20,6 @@

    robot.hear /journal club/i, (res) ->
      res.send "I want pizza"
+
+   robot.hear /email/i, (res) ->
+     res.send "We don't use email anymore!"


indentation fix

indentation fix

--- scripts/ping.coffee
+++ scripts/ping.coffee
@@ -17,16 +17,19 @@
   robot.respond /TIME$/i, (msg) ->
     msg.send "Server time is: #{new Date()}"

+
+  robot.respond /hello$/i, (msg) ->
+    msg.send "Hello, there."
+
+
+  robot.respond /Good morning$/i, (msg) ->
+    msg.send "Good morning there."
+
   robot.respond /DIE$/i, (msg) ->
     msg.send "Goodbye, cruel world."
     process.exit 0

-    robot.respond /hello$/i, (msg) ->
-    msg.send "Hello, there."
-
-
-    robot.respond /Good morning$/i, (msg) ->
-    msg.send "Good morning there."
+


Deploy history JSON on Request page should only show deploy info, not entire task info

--- SingularityUI/app/views/request.coffee
+++ SingularityUI/app/views/request.coffee
@@ -42,16 +42,9 @@
         id = $target.data 'id'
         collectionName = $target.data 'collection'

-        if collectionName is 'deployHistory'
-            deploy = new Deploy {},
-                requestId: @model.id
-                deployId:  id
-
-            utils.viewJSON deploy
-        else
-            # Need to reach into subviews to get the necessary data
-            collection = @subviews[collectionName].collection
-            utils.viewJSON collection.get id
+        # Need to reach into subviews to get the necessary data
+        collection = @subviews[collectionName].collection
+        utils.viewJSON collection.get id

     viewObjectJson: (e) =>
         utils.viewJSON @model


Remove unneed try block

Remove unneed try block

--- src/cson.coffee
+++ src/cson.coffee
@@ -90,10 +90,8 @@
     fs.writeFileSync(cachePath, JSON.stringify(object))

 writeCacheFile = (cachePath, object) ->
-  try
-    mkdirp path.dirname(cachePath), (error) ->
-      unless error?
-        fs.writeFile(cachePath, JSON.stringify(object))
+  mkdirp path.dirname(cachePath), (error) ->
+    fs.writeFile(cachePath, JSON.stringify(object)) unless error?

 parseObject = (objectPath, contents) ->
   if path.extname(objectPath) is '.cson'


Increase twss threshold

Increase twss threshold

--- scripts/twss.coffee
+++ scripts/twss.coffee
@@ -1,4 +1,5 @@
 twss = require 'twss'
+twss.threshold = 0.9

 module.exports = (robot) ->
   robot.hear /(.*)/, (msg)->


Surprise levels holding steady

Surprise levels holding steady

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -153,7 +153,7 @@
           (if result then "totally counted." else "was absolutely worthless!")
         msg.reply reply

-      when "random"
+      when "random","rando"
        return msg.reply "You can't randomly vote on nothing!" unless Vote.current?
        voteResult = msg.random ["yes", "no"]
        result = if voteResult == "yes"


Add 'Select All' to context menu

Add 'Select All' to context menu

--- menus/markdown-preview.cson
+++ menus/markdown-preview.cson
@@ -11,6 +11,7 @@

 'context-menu':
   '.markdown-preview': [
+    {label: 'Select All', command: 'markdown-preview:select-all'}
     {label: 'Save As HTML\u2026', command: 'core:save-as'}
   ]
   '.markdown-preview.has-selection': [


Cannot flag is game not started

Cannot flag is game not started

--- src/game.coffee
+++ src/game.coffee
@@ -26,20 +26,22 @@
   mouseUpHandler: (evt) =>
     return if game.state.is StateManager.GameOver
     if game.state.is StateManager.Empty
-      game.initGame @x, @y
-
-    if evt.button is 2 # Right click
-      casePosition = game.positionFromCoord @x, @y
-      game.board.cases[casePosition].g.remove()
-      if game.board.cases[casePosition].constructor is Flag
-        game.flags--
-        game.board.cases[casePosition] = new Tile @ctx, @x, @y
+      if evt.button is 0 # Left click
+        game.initGame @x, @y
+
+    if game.state.is StateManager.Playing
+      if evt.button is 2 # Right click
+        casePosition = game.positionFromCoord @x, @y
+        game.board.cases[casePosition].g.remove()
+        if game.board.cases[casePosition].constructor is Flag
+          game.flags--
+          game.board.cases[casePosition] = new Tile @ctx, @x, @y
+        else
+          game.flags++
+          game.board.cases[casePosition] = new Flag @ctx, @x, @y
+        game.board.flagsLbl.attr text: "Flags: #{game.flags}/#{game.nbMines}"
       else
-        game.flags++
-        game.board.cases[casePosition] = new Flag @ctx, @x, @y
-      game.board.flagsLbl.attr text: "Flags: #{game.flags}/#{game.nbMines}"
-    else
-      @showTile()
+        @showTile()

   showTile: () ->
@@ -256,8 +258,6 @@
     else if game.state.is StateManager.GameOver
       game.reset()
       game.state.set StateManager.Empty
-    else if game.state.is StateManager.Empty
-      @board.init()

   createBoard: () ->


Small refactor

Small refactor

--- src/game.coffee
+++ src/game.coffee
@@ -28,7 +28,6 @@
           @disabledThroughPause = yes
           @disableControl()

-      Crafty.trigger('GamePause', @paused)
     else
       @setGameSpeed(1.0)
       Crafty('Delay').each -> @resumeDelays()
@@ -39,7 +38,7 @@
         if @disabledThroughPause
           @disabledThroughPause = null
           @enableControl()
-      Crafty.trigger('GamePause', @paused)
+    Crafty.trigger('GamePause', @paused)

   setGameSpeed: (speed) ->
     @gameSpeed = speed


Store songs as object, bower update please.

This is a good pattern. We need to deal with songs as arrays on client,
but as object on server because concurrent access. este.labs.Store
provides helper methods for that.

https://www.firebase.com/docs/web/guide/saving-data.html#section-push

--- app/client/js/users/store.coffee
+++ app/client/js/users/store.coffee
@@ -93,7 +93,7 @@
   ###
   toJson: ->
     newSong: @newSong
-    songs: @songs
+    songs: @asObject @songs
     user: @getJsonUser @user

   ###*
@@ -103,7 +103,7 @@
     @newSong = @instanceFromJson app.songs.Song, json.newSong
     @user = @getJsonUser json.user
     # NOTE(steida): Because JSON stringify and parse ignore empty array.
-    @songs = (json.songs || []).map @instanceFromJson app.songs.Song
+    @songs = (@asArray(json.songs) || []).map @instanceFromJson app.songs.Song

   getJsonUser: (user) ->
     return null if !user


provide setting to override child_process.execFile for synctex

--- app/coffee/CompileManager.coffee
+++ app/coffee/CompileManager.coffee
@@ -174,7 +174,8 @@
 	_runSynctex: (args, callback = (error, stdout) ->) ->
 		bin_path = Path.resolve(__dirname + "/../../bin/synctex")
 		seconds = 1000
-		child_process.execFile bin_path, args, timeout: 10 * seconds, (error, stdout, stderr) ->
+		synctex = Settings.clsi?.synctex?.command?(__dirname, child_process) || child_process
+		synctex.execFile bin_path, args, timeout: 10 * seconds, (error, stdout, stderr) ->
 			if error?
 				logger.err err:error, args:args, "error running synctex"
 				return callback(error)


fbgraph

fbgraph

--- app/controllers/fb/FbDemo.coffee
+++ app/controllers/fb/FbDemo.coffee
@@ -34,6 +34,10 @@
   # @return [None]
   #
   indexAction: ->
+    console.log "===================="
+    console.log @req.query.code
+    console.log "===================="
+
     @res.render 'demo/index',
       title: 'Express',
       loginUrl: muninn.config.fb.redirectUri


Put notice on the left, on the right didn't *feel* right (pun intended).

If someone who owns a site opens Factlink in the proxy and is all like, what the heck is this doing on my site, they should always see some kind of explanation. Hiding away the explanation under the sidebar seems therefore not a good idea.

--- js-library/app/js/jail_iframe/classes/proxy_message.coffee
+++ js-library/app/js/jail_iframe/classes/proxy_message.coffee
@@ -13,8 +13,7 @@

   frame.$el.css
     top: '10px'
-    left: 'auto'
-    right: '10px'
+    left: '10px'
     position: 'fixed'

   frame.fadeIn()


Disable owner role checkbox and add owner back

I think it was confusing not seeing the project owner on the Roles list
at all, so this just adds them back with all the checkboxes
disabled. This also disables the owner checkbox when creating new roles
since there can only be a single owner.

Closes #548

--- app/pages/lab/collaborators.cjsx
+++ app/pages/lab/collaborators.cjsx
@@ -66,7 +66,7 @@

             {for role, _ of POSSIBLE_ROLES

-                <input id={ID_PREFIX + role} type="checkbox" name="role" value={role} />
+                <input id={ID_PREFIX + role} type="checkbox" name="role" value={role} disabled={role is 'owner'}/>
                 {ROLES_INFO[role].label}
                 {ROLES_INFO[role].description}

}
@@ -155,8 +155,10 @@

{@state.error.toString()}
}
       <PromiseRenderer promise={@fetchAllRoles()} then={(projectRoleSets) =>

+          {ownerSet = projectRoleSets.filter((set) -> 'owner' in set.roles)[0]
+          <PromiseRenderer key={ownerSet.id} promise={ownerSet.get 'owner'} then={@renderUserRow.bind this, ownerSet} />}
           {if projectRoleSets.length > 1
-            for projectRoleSet in projectRoleSets
+            for projectRoleSet in projectRoleSets when 'owner' not in projectRoleSet.roles
               <PromiseRenderer key={projectRoleSet.id} promise={projectRoleSet.get 'owner'} then={@renderUserRow.bind this, projectRoleSet} />
           else
             None yet}
@@ -170,24 +172,21 @@

   renderUserRow: (projectRoleSet, user) ->
-    if 'owner' in projectRoleSet.roles
-      null
-    else
-

-        {user.display_name}{' '}
-        <button type="button" className="secret-button" onClick={@removeRoleSet.bind this, projectRoleSet}>×</button>
-

-
-
-          {for role, _ of POSSIBLE_ROLES
-            toggleThisRole = @toggleRole.bind this, projectRoleSet, role
-            # TODO: Translate this.
-
-              <input type="checkbox" name={role} checked={role in projectRoleSet.roles} disabled={role is 'owner' or projectRoleSet.id in @state.saving} onChange={toggleThisRole} />{' '}
-              {role[...1].toUpperCase()}{role[1...]}
-            }
-
-

+

+      {user.display_name}{' '}
+      <button type="button" className="secret-button" onClick={@removeRoleSet.bind this, projectRoleSet}>×</button>
+

+
+
+        {for role, _ of POSSIBLE_ROLES
+          toggleThisRole = @toggleRole.bind this, projectRoleSet, role
+          # TODO: Translate this.
+
+            <input type="checkbox" name={role} checked={role in projectRoleSet.roles} disabled={role is 'owner' or projectRoleSet.id in @state.saving or 'owner' in projectRoleSet.roles} onChange={toggleThisRole} />{' '}
+            {ROLES_INFO[role].label}
+          }
+
+

   toggleRole: (projectRoleSet, role) ->
     index = projectRoleSet.roles.indexOf role


Allow { log } option in up.emit(event, { log })

--- lib/assets/javascripts/unpoly/classes/event_emitter.coffee
+++ lib/assets/javascripts/unpoly/classes/event_emitter.coffee
@@ -8,7 +8,8 @@
       'target',
       'event',
       'base',
-      'callback'
+      'callback',
+      'log',
       # 'boundary'
     ]

@@ -50,7 +51,7 @@
   logEmission: ->
     return unless up.log.isEnabled()

-    message = u.pluckKey(@event, 'log')
+    message = @log

     if u.isArray(message)
       [message, messageArgs...] = message
@@ -94,7 +95,7 @@
     else
       # In this branch we receive an Event name and props object.
       # The props object may also include options for the emission, such as
-      # { layer }, { target } or { base }.
+      # { layer }, { target }, { base } or { log }.
       # up.emit([target], eventName, [eventPropsAndEmitOptions])
       options.event = up.event.build(args[0], u.except(options, 'target'))


resource tests

resource tests

--- spec/Generator.spec.coffee
+++ spec/Generator.spec.coffee
@@ -18,8 +18,34 @@
     @testHelper.cleanUp()

+  describe 'usage', ->
+    it "prints usage instructions when no parameters", ->
+      @testHelper.createProjectSync()
+
+      cmd = @testHelper.runInProjectSync "generate",
+        args: []
+
+      runs ()=>
+        expect( cmd.code ).toBe(0)
+
+  describe "resource", ->
+    it "creates a resource", ->
+      @testHelper.createProjectSync()
+
+      cmd = @testHelper.runInProjectSync "generate",
+        args: ["resource", "car"]
+
+      runs ()=>
+        expect( cmd.code ).toBe(0)
+
+        ctrlPath = path.join(@testHelper.testAppPath, "app", "controllers", "carsController.coffee")
+        expect(fs.existsSync ctrlPath).toBe true
+        expect(fs.readFileSync(ctrlPath).toString()).toMatch(/class window.CarsController/)
+
+        expect(fs.existsSync path.join(@testHelper.testAppPath, "app", "views", "cars", "index.html")).toBe true
+
+
   describe 'ng-scaffold', ->
-
     it 'should be created', ->
       @testHelper.createProjectSync()


Different port for client-side tests so they can be run together

--- spec/RemoteRuntimes.coffee
+++ spec/RemoteRuntimes.coffee
@@ -17,6 +17,7 @@

   before (done) ->
     if noflo.isBrowser()
+      port = 3889
       console.log "WebSocket runtime should have been set up on #{port}"
       done()
     else
@@ -41,11 +42,12 @@
     meta = {}
     readyEmitted = false

-    it 'should be instantiable', ->
+    it 'should be instantiable', (done) ->
       c = (RemoteSubGraph.getComponentForRuntime def)(meta)
       chai.expect(c).to.be.an.instanceof noflo.Component
       c.on 'ready', () ->
         readyEmitted = true
+        done()
     it 'should set description', ->
       chai.expect(c.description).to.equal def.description
     it 'should populate ports and go ready after connecting to remote', (done) ->
@@ -61,8 +63,10 @@
     it 'sending data into local port should be echoed back', (done) ->
       input = noflo.internalSocket.createSocket()
       output = noflo.internalSocket.createSocket()
+      chai.expect(c.inPorts.in).to.be.an 'object'
       c.inPorts['in'].attach input
-      c.outPorts.out.attach output
+      chai.expect(c.outPorts.out).to.be.an 'object'
+      c.outPorts.out.attach output

       output.on 'data', (data) ->
         chai.expect(data).to.deep.equal { test: true }


Cleaner error handling

Cleaner error handling

--- spec/cli.coffee
+++ spec/cli.coffee
@@ -38,7 +38,7 @@
     it 'should exit with 0 code', (done) ->
       @timeout pyTimeout
       fbpSpec example('simple-passing.yaml'), (err) ->
-        chai.expect(err).to.not.exist
+        return done err if err
         done()

   describe "with multiple suites and some failing cases", ->


Fix dk spec that would fail, even if dk worked

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -316,7 +316,7 @@
         it "deletes the bottom two lines", ->
           keydown('d')
           keydown('k')
-          expect(editor.getText()).toBe("ABCDE")
+          expect(editor.getText()).toBe("12345")

       describe "on the beginning of the file", ->
         beforeEach ->
@@ -332,7 +332,7 @@
         it "deletes the first two lines", ->
           keydown('d')
           keydown('k')
-          expect(editor.getText()).toBe("12345")
+          expect(editor.getText()).toBe("ABCDE")

   describe "the D keybinding", ->


Remove spec now that $::view always works

--- spec/space-pen-spec.coffee
+++ spec/space-pen-spec.coffee
@@ -213,16 +213,6 @@
         expect(view.beforeRemove.callCount).toBe 1
         expect(subviewParentViewDuringRemove).toBe view

-      it "the view instance is no longer accessible by calling view()", ->
-        content = $('#jasmine-content')
-        parent = $$ -> @div()
-        parent.append(view)
-        content.append(parent)
-
-        expect($(view[0]).view()).toBe view
-        parent.remove()
-        expect($(view[0]).view()).toBeFalsy()
-
     describe "when the view constructs a new jQuery wrapper", ->
       it "constructs instances of jQuery rather than the view class", ->
         expect(view.eq(0) instanceof jQuery).toBeTruthy()


Fix test for deprecation fix

Fix test for deprecation fix

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -3521,7 +3521,7 @@
         gammaDir = $(treeView).find('.project-root:contains(gamma):first')
         [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0])
         treeView.onDragStart(dragStartEvent)
-        treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow(gammaDir.index())
+        treeView.projectFolderDragAndDropHandler.onDropOnOtherWindow({}, gammaDir.index())

         expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath]
         expect('.placeholder').not.toExist()


Some progress on specs

Some progress on specs

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -1353,7 +1353,7 @@
               expect(fs.existsSync(numberedFileName1)).toBeTruthy()
               expect(fs.existsSync(filePath)).toBeTruthy()

-        xdescribe "when a file containing two or more periods is selected", ->
+        describe "when a file containing two or more periods has been copied", ->
           describe "when a file is selected", ->
             it "creates a copy of the original file in the selected file's parent directory", ->
               dotFilePath = path.join(dirPath, "test.file.txt")
@@ -1374,8 +1374,7 @@
                 fs.writeFileSync(dotFilePath, "doesn't matter .")
                 LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath])

-                dotFileView = treeView.find('.file:contains(test.file.txt)')
-                dotFileView.click()
+                fileView.click()
                 atom.commands.dispatch(treeView.element, "tree-view:paste")
                 atom.commands.dispatch(treeView.element, "tree-view:paste")

@@ -1413,31 +1412,36 @@
               else
                 fileArr = /(.+)\.(?!.*\/)(.+)/.exec(filePath)

-              numberedFileName0 = "#{fileArr[0]}0.#{fileArr[1]}"
-              numberedFileName1 = "#{fileArr[0]}1.#{fileArr[1]}"
+              numberedFileName0 = "#{fileArr[1]}0.#{fileArr[2]}"
+              numberedFileName1 = "#{fileArr[1]}1.#{fileArr[2]}"
               expect(fs.existsSync(numberedFileName0)).toBeTruthy()
               expect(fs.existsSync(numberedFileName1)).toBeTruthy()
               expect(fs.existsSync(filePath)).toBeTruthy()

         xdescribe "when a directory with a period is selected", ->
           it "creates a copy of the original file in the selected directory", ->
-            dotFilePath = path.join(dirPath, "test.file.txt")
-            fs.writeFileSync(dotFilePath, "doesn't matter .")
-            LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath])
-
-            dirView2.click()
+            dotDirPath = path.join(rootDirPath, "test.dir")
+            fs.makeTreeSync(dotDirPath)
+            LocalStorage['tree-view:copyPath'] = JSON.stringify([filePath])
+
+            dotDirView = $(treeView.roots[0].entries).find('.directory:contains(test\\.dir):first')
+            dotDirView.click()
+            console.log dotDirView
             atom.commands.dispatch(treeView.element, "tree-view:paste")

-            expect(fs.existsSync(path.join(dirPath2, path.basename(dotFilePath)))).toBeTruthy()
-            expect(fs.existsSync(dotFilePath)).toBeTruthy()
+            expect(fs.existsSync(path.join(dotDirPath, path.basename(filePath)))).toBeTruthy()
+            expect(fs.existsSync(filePath)).toBeTruthy()

           describe "when the target already exists", ->
             it "appends a number to the destination directory name", ->
-              dotFilePath = path.join(dirPath, "test.file.txt")
+              dotDirPath = path.join(rootDirPath, "test.dir")
+              dotFilePath = path.join(dotDirPath, "test.file.txt")
+              fs.makeTreeSync(dotDirPath)
               fs.writeFileSync(dotFilePath, "doesn't matter .")
               LocalStorage['tree-view:copyPath'] = JSON.stringify([dotFilePath])

-              dirView.click()
+              dotDirView = $(treeView.roots[0].entries).find('.directory:contains(test\\.dir):last')
+              dotDirView.click()
               atom.commands.dispatch(treeView.element, "tree-view:paste")
               atom.commands.dispatch(treeView.element, "tree-view:paste")

@@ -1446,12 +1450,11 @@
               else
                 fileArr = /(.+)\.(?!.*\/)(.+)/.exec(dotFilePath)

-              numberedFileName0 = "#{fileArr[0]}0.#{fileArr[1]}"
-              numberedFileName1 = "#{fileArr[0]}1.#{fileArr[1]}"
+              numberedFileName0 = "#{fileArr[1]}0.#{fileArr[2]}"
+              numberedFileName1 = "#{fileArr[1]}1.#{fileArr[2]}"
               expect(fs.existsSync(numberedFileName0)).toBeTruthy()
               expect(fs.existsSync(numberedFileName1)).toBeTruthy()
               expect(fs.existsSync(dotFilePath)).toBeTruthy()
-

         describe "when pasting into a different root directory", ->
           it "creates the file", ->


explicit mock #270

explicit mock #270

--- spec/vim-state-spec.coffee
+++ spec/vim-state-spec.coffee
@@ -1,6 +1,6 @@
 # SPEC_MIGRATION: P1 DONE #270
 _ = require 'underscore-plus'
-{getVimState, TextData} = require './spec-helper'
+{getVimState, TextData, mockPlatform} = require './spec-helper'
 settings = require '../lib/settings'

 describe "VimState", ->
@@ -198,8 +198,9 @@
         mode: 'normal'

     it "puts the editor into normal mode when <ctrl-c> is pressed", ->
-      ensure [{platform: 'platform-darwin'}, 'ctrl-c'],
-        mode: 'normal'
+      mock = mockPlatform(editorElement, 'platform-darwin')
+      ensure 'ctrl-c', mode: 'normal'
+      mock.dispose()

   describe "replace-mode", ->
     describe "with content", ->
@@ -228,8 +229,9 @@
         mode: 'normal'

     it "puts the editor into normal mode when <ctrl-c> is pressed", ->
-      ensure [{platform: 'platform-darwin'}, 'R ctrl-c'],
-        mode: 'normal'
+      mock = mockPlatform(editorElement, 'platform-darwin')
+      ensure 'R ctrl-c', mode: 'normal'
+      mock.dispose()

   describe "visual-mode", ->
     beforeEach ->


Add some more hashing functions

Add some more hashing functions

--- lib/hashingFunctions.coffee
+++ lib/hashingFunctions.coffee
@@ -5,5 +5,11 @@
   @sum: (array) =>
     array.reduce(@_addition, 0)

+  @reverseSum: (array) =>
+    array.reverse().reduce(@_addition, 0)
+
   @product: (array) =>
     array.reduce(@_multiplication, 0)
+
+  @reverseProduct: (array) =>
+    array.reverse().reduce(@_multiplication, 0)


re-add `input` handler

re-add `input` handler

--- lib/runtime/frontend.coffee
+++ lib/runtime/frontend.coffee
@@ -13,6 +13,8 @@

   activate: ->
     client.handle select: (items) -> selector.show items
+
+    client.handle input: () -> selector.show([], {allowCustom: true})

     client.handle syntaxcolors: (selectors) -> colors.getColors(selectors)


added todo to fix chrome plain url -> hashbang

--- lib/web/views/router.coffee
+++ lib/web/views/router.coffee
@@ -19,6 +19,7 @@
 "use strict"

 # Missing features:
+# - chrome fails when converting plain url to hashbang: %23
 # - querystring parameters as part of params
 # - relative url support
 # - copy build configs from bacon.js


utils/dump: Update require to msgflo-nodejs

--- src/utils/dump.coffee
+++ src/utils/dump.coffee
@@ -1,5 +1,5 @@
 program = require 'commander'
-msgflo = require '../msgflo'
+msgflo_nodejs = require 'msgflo-nodejs'

 onError = (err) ->
   console.log err
@@ -16,7 +16,7 @@
     .parse(process.argv)

   received = 0
-  messaging = msgflo.transport.getClient program.broker,
+  messaging = msgflo_nodejs.transport.getClient program.broker,
     prefetch: 1
   messaging.connect (err) ->
     return onError err if err


Add internal offset, fix the contentFrame

--- src/views/view.coffee
+++ src/views/view.coffee
@@ -15,6 +15,7 @@
 	constructor: (args) ->

 		args ?= {}
+		@_offset = {x:0, y:0}

 		# Keep a generic list of all current views
 		View.Views.push @
@@ -38,6 +39,8 @@
 		# Overridable creation hook
 		@_postCreate?()

+
+
 		if not args.superView
 			@_insertElement()

@@ -90,14 +93,18 @@

 	contentFrame: ->
 		# Get the total size of all subviews
+
+		minX = utils.min _.pluck @subViews, "minX"
+		maxX = utils.max _.pluck @subViews, "maxX"
+		minY = utils.min _.pluck @subViews, "minY"
+		maxY = utils.max _.pluck @subViews, "maxY"
+
 		frame =
-			x: utils.min _.pluck(@subViews, "minX")
-			y: utils.min _.pluck(@subViews, "minY")
-
-		frame.width  = utils.max(_.pluck(@subViews, "maxX")) - frame.x
-		frame.height = utils.max(_.pluck(@subViews, "maxY")) - frame.y
-
-		frame
+			x: minX
+			y: minY
+			width: maxX - minX
+			height: maxY - minY
+

 	centerX: -> @midX = @superView.width  / 2.0 if @superView
 	centerY: -> @midY = @superView.height / 2.0 if @superView
@@ -125,16 +132,23 @@
 			@emit "change:frame"

 	@define "x",
-		get: -> @_matrix.x
-		set: (value) -> @_setMatrixValue "x", value
+		get: ->
+			@_matrix.x - @_offset.x
+		set: (value) ->
+			@_setMatrixValue "x", value + @_offset.x
+			@emit "change:frame"

 	@define "y",
-		get: -> @_matrix.y
-		set: (value) -> @_setMatrixValue "y", value
+		get: ->
+			@_matrix.y - @_offset.y
+		set: (value) ->
+			@_setMatrixValue "y", value + @_offset.y
+			@emit "change:frame"

 	@define "z",
 		get: -> @_matrix.z
-		set: (value) -> @_setMatrixValue "z", value
+		set: (value) ->
+			@_setMatrixValue "z", value

 	#############################################################################
 	## Scale
@@ -253,9 +267,9 @@

 	@define "superView",
 		get: -> @_superView or null
-		set: (value) ->
-
-			return if value is @_superView
+		set: (view) ->
+
+			return if view is @_superView

 			# Cancel previous pending insertions
 			utils.domCompleteCancel @__insertElement
@@ -264,18 +278,18 @@
 			if @_superView
 				@_superView._element.removeChild @_element
 				@_superView._subViews = _.without @_superView._subViews, @
-				@_superView.emit "change:subViews"
+				@_superView.emit "change:subViews", {added:[], removed:[@]}

 			# Either insert the element to the new superview or into dom
-			if value
-				value._element.appendChild @_element
-				value._subViews.push @
-				value.emit "change:subViews"
+			if view
+				view._element.appendChild @_element
+				view._subViews.push @
+				view.emit "change:subViews", {added:[@], removed:[]}
 			else
 				@_insertElement()

 			# Set the superview
-			@_superView = value
+			@_superView = view

 			# Place this view on top of it's siblings
 			@bringToFront()
@@ -403,7 +417,8 @@
 		document.body.appendChild @_element

 	destroy: ->
-		@_element.parentNode.removeChild @_element
+		if @_element.parentNode
+			@_element.parentNode.removeChild @_element

 	#############################################################################