From eeef24b9da0c4ea3c9be4345d03a293363850694 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 28 Aug 2013 15:27:03 -0400 Subject: [PATCH] Allow basic emoticons to work too. --- .../discourse/components/autocomplete.js | 49 ++++++++++++++++++- .../discourse/dialects/bbcode_dialect.js | 10 ++-- .../dialects/bold_italics_dialect.js | 4 +- .../javascripts/discourse/dialects/dialect.js | 27 ++++++++-- .../javascripts/external/better_markdown.js | 2 +- .../assets/javascripts/discourse_emoji.js | 48 +++++++++++++----- 6 files changed, 114 insertions(+), 26 deletions(-) diff --git a/app/assets/javascripts/discourse/components/autocomplete.js b/app/assets/javascripts/discourse/components/autocomplete.js index 6e282f36a1..718c8841b7 100644 --- a/app/assets/javascripts/discourse/components/autocomplete.js +++ b/app/assets/javascripts/discourse/components/autocomplete.js @@ -3,6 +3,49 @@ @module $.fn.autocomplete **/ + +var shiftMap = []; +shiftMap[192] = "~"; +shiftMap[49] = "!"; +shiftMap[50] = "@"; +shiftMap[51] = "#"; +shiftMap[52] = "$"; +shiftMap[53] = "%"; +shiftMap[54] = "^"; +shiftMap[55] = "&"; +shiftMap[56] = "*"; +shiftMap[57] = "("; +shiftMap[48] = ")"; +shiftMap[109] = "_"; +shiftMap[107] = "+"; +shiftMap[219] = "{"; +shiftMap[221] = "}"; +shiftMap[220] = "|"; +shiftMap[59] = ":"; +shiftMap[222] = "\""; +shiftMap[188] = "<"; +shiftMap[190] = ">"; +shiftMap[191] = "?"; +shiftMap[32] = " "; + +function mapKeyPressToActualCharacter(isShiftKey, characterCode) { + if ( characterCode === 27 || characterCode === 8 || characterCode === 9 || characterCode === 20 || characterCode === 16 || characterCode === 17 || characterCode === 91 || characterCode === 13 || characterCode === 92 || characterCode === 18 ) { return false; } + + if (isShiftKey) { + if ( characterCode >= 65 && characterCode <= 90 ) { + return String.fromCharCode(characterCode); + } else { + return shiftMap[characterCode]; + } + } else { + if ( characterCode >= 65 && characterCode <= 90 ) { + return String.fromCharCode(characterCode).toLowerCase(); + } else { + return String.fromCharCode(characterCode); + } + } +} + $.fn.autocomplete = function(options) { var autocompletePlugin = this; @@ -338,11 +381,15 @@ $.fn.autocomplete = function(options) { } term = me.val().substring(completeStart + (options.key ? 1 : 0), caretPosition); if (e.which >= 48 && e.which <= 90) { - term += String.fromCharCode(e.which); + term += mapKeyPressToActualCharacter(e.shiftKey, e.which); } else if (e.which === 187) { term += "+"; } else if (e.which === 189) { term += (e.shiftKey) ? "_" : "-"; + } else if (e.which === 220) { + term += (e.shiftKey) ? "|" : "]"; + } else if (e.which === 222) { + term += (e.shiftKey) ? "\"" : "'"; } else { if (e.which !== 8) { term += ","; diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js index 7295d8de5d..8ea9825b44 100644 --- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js @@ -6,7 +6,7 @@ @param {function} emitter the function that creates JsonML for the tag **/ function replaceBBCode(tag, emitter) { - Discourse.Dialect.inlineReplace({ + Discourse.Dialect.inlineBetween({ start: "[" + tag + "]", stop: "[/" + tag + "]", emitter: emitter @@ -21,7 +21,7 @@ function replaceBBCode(tag, emitter) { @param {function} emitter the function that creates JsonML for the tag **/ function replaceBBCodeParamsRaw(tag, emitter) { - Discourse.Dialect.inlineReplace({ + Discourse.Dialect.inlineBetween({ start: "[" + tag + "=", stop: "[/" + tag + "]", rawContents: true, @@ -59,21 +59,21 @@ replaceBBCode('li', function(contents) { return ['li'].concat(contents); }); replaceBBCode('spoiler', function(contents) { return ['span', {'class': 'spoiler'}].concat(contents); }); -Discourse.Dialect.inlineReplace({ +Discourse.Dialect.inlineBetween({ start: '[img]', stop: '[/img]', rawContents: true, emitter: function(contents) { return ['img', {href: contents}]; } }); -Discourse.Dialect.inlineReplace({ +Discourse.Dialect.inlineBetween({ start: '[email]', stop: '[/email]', rawContents: true, emitter: function(contents) { return ['a', {href: "mailto:" + contents, 'data-bbcode': true}, contents]; } }); -Discourse.Dialect.inlineReplace({ +Discourse.Dialect.inlineBetween({ start: '[url]', stop: '[/url]', rawContents: true, diff --git a/app/assets/javascripts/discourse/dialects/bold_italics_dialect.js b/app/assets/javascripts/discourse/dialects/bold_italics_dialect.js index 08992e079c..b77b2e304c 100644 --- a/app/assets/javascripts/discourse/dialects/bold_italics_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bold_italics_dialect.js @@ -4,7 +4,7 @@ **/ // Support for simultaneous bold and italics -Discourse.Dialect.inlineReplace({ +Discourse.Dialect.inlineBetween({ between: '***', wordBoundary: true, emitter: function(contents) { return ['strong', ['em'].concat(contents)]; } @@ -12,7 +12,7 @@ Discourse.Dialect.inlineReplace({ // Builds a common markdown replacer var replaceMarkdown = function(match, tag) { - Discourse.Dialect.inlineReplace({ + Discourse.Dialect.inlineBetween({ between: match, wordBoundary: true, emitter: function(contents) { return [tag].concat(contents) } diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js index 820a680f60..eb26d0cade 100644 --- a/app/assets/javascripts/discourse/dialects/dialect.js +++ b/app/assets/javascripts/discourse/dialects/dialect.js @@ -128,6 +128,25 @@ Discourse.Dialect = { return parser.renderJsonML(parseTree(tree)); }, + /** + The simplest kind of replacement possible. Replace a stirng token with JsonML. + + For example to replace all occurrances of :) with a smile image: + + ```javascript + Discourse.Dialect.inlineReplace(':)', ['img', {src: '/images/smile.gif'}]) + ``` + + @method inlineReplace + @param {String} token The token we want to replace + @param {Array} jsonml The JsonML to replace it with. + **/ + inlineReplace: function(token, jsonml) { + dialect.inline[token] = function(text, match, prev) { + return [token.length, jsonml]; + }; + }, + /** Matches inline using a regular expression. The emitter function is passed the matches from the regular expression. @@ -146,7 +165,7 @@ Discourse.Dialect = { }); ``` - @method inlineReplace + @method inlineRegexp @param {Object} args Our replacement options @param {Function} [opts.emitter] The function that will be called with the contents and regular expresison match and returns JsonML. @param {String} [opts.start] The starting token we want to find @@ -178,7 +197,7 @@ Discourse.Dialect = { ```javascript - Discourse.Dialect.inlineReplace({ + Discourse.Dialect.inlineBetween({ between: '**', wordBoundary: true. emitter: function(contents) { @@ -187,7 +206,7 @@ Discourse.Dialect = { }); ``` - @method inlineReplace + @method inlineBetween @param {Object} args Our replacement options @param {Function} [opts.emitter] The function that will be called with the contents and returns JsonML. @param {String} [opts.start] The starting token we want to find @@ -197,7 +216,7 @@ Discourse.Dialect = { @param {Boolean} [opts.wordBoundary] If true, the match must be on a word boundary @param {Boolean} [opts.spaceBoundary] If true, the match must be on a sppace boundary **/ - inlineReplace: function(args) { + inlineBetween: function(args) { var start = args.start || args.between, stop = args.stop || args.between, startLength = start.length; diff --git a/app/assets/javascripts/external/better_markdown.js b/app/assets/javascripts/external/better_markdown.js index dd46e8782b..4d5da8935a 100644 --- a/app/assets/javascripts/external/better_markdown.js +++ b/app/assets/javascripts/external/better_markdown.js @@ -1057,7 +1057,7 @@ Markdown.buildInlinePatterns = function(d) { for ( var i in d ) { // __foo__ is reserved and not a pattern if ( i.match( /^__.*__$/) ) continue; - var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) + var l = i.replace( /([\\.*+?$|()\[\]{}])/g, "\\$1" ) .replace( /\n/, "\\n" ); patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); } diff --git a/vendor/gems/discourse_emoji/vendor/assets/javascripts/discourse_emoji.js b/vendor/gems/discourse_emoji/vendor/assets/javascripts/discourse_emoji.js index 2d29501526..b524de74c3 100644 --- a/vendor/gems/discourse_emoji/vendor/assets/javascripts/discourse_emoji.js +++ b/vendor/gems/discourse_emoji/vendor/assets/javascripts/discourse_emoji.js @@ -2,15 +2,36 @@ var emoji = ["+1", "-1", "100", "1234", "8ball", "a", "ab", "abc", "abcd", "accept", "aerial_tramway", "airplane", "alarm_clock", "alien", "ambulance", "anchor", "angel", "anger", "angry", "anguished", "ant", "apple", "aquarius", "aries", "arrow_backward", "arrow_double_down", "arrow_double_up", "arrow_down", "arrow_down_small", "arrow_forward", "arrow_heading_down", "arrow_heading_up", "arrow_left", "arrow_lower_left", "arrow_lower_right", "arrow_right", "arrow_right_hook", "arrow_up", "arrow_up_down", "arrow_up_small", "arrow_upper_left", "arrow_upper_right", "arrows_clockwise", "arrows_counterclockwise", "art", "articulated_lorry", "astonished", "athletic_shoe", "atm", "b", "baby", "baby_bottle", "baby_chick", "baby_symbol", "back", "baggage_claim", "balloon", "ballot_box_with_check", "bamboo", "banana", "bangbang", "bank", "bar_chart", "barber", "baseball", "basketball", "bath", "bathtub", "battery", "bear", "bee", "beer", "beers", "beetle", "beginner", "bell", "bento", "bicyclist", "bike", "bikini", "bird", "birthday", "black_circle", "black_joker", "black_large_square", "black_medium_small_square", "black_medium_square", "black_nib", "black_small_square", "black_square_button", "blossom", "blowfish", "blue_book", "blue_car", "blue_heart", "blush", "boar", "boat", "bomb", "book", "bookmark", "bookmark_tabs", "books", "boom", "boot", "bouquet", "bow", "bowling", "bowtie", "boy", "bread", "bride_with_veil", "bridge_at_night", "briefcase", "broken_heart", "bug", "bulb", "bullettrain_front", "bullettrain_side", "bus", "busstop", "bust_in_silhouette", "busts_in_silhouette", "cactus", "cake", "calendar", "calling", "camel", "camera", "cancer", "candy", "capital_abcd", "capricorn", "car", "card_index", "carousel_horse", "cat", "cat2", "cd", "chart", "chart_with_downwards_trend", "chart_with_upwards_trend", "checkered_flag", "cherries", "cherry_blossom", "chestnut", "chicken", "children_crossing", "chocolate_bar", "christmas_tree", "church", "cinema", "circus_tent", "city_sunrise", "city_sunset", "cl", "clap", "clapper", "clipboard", "clock1", "clock10", "clock1030", "clock11", "clock1130", "clock12", "clock1230", "clock130", "clock2", "clock230", "clock3", "clock330", "clock4", "clock430", "clock5", "clock530", "clock6", "clock630", "clock7", "clock730", "clock8", "clock830", "clock9", "clock930", "closed_book", "closed_lock_with_key", "closed_umbrella", "cloud", "clubs", "cn", "cocktail", "coffee", "cold_sweat", "collision", "computer", "confetti_ball", "confounded", "confused", "congratulations", "construction", "construction_worker", "convenience_store", "cookie", "cool", "cop", "copyright", "corn", "couple", "couple_with_heart", "couplekiss", "cow", "cow2", "credit_card", "crescent_moon", "crocodile", "crossed_flags", "crown", "cry", "crying_cat_face", "crystal_ball", "cupid", "curly_loop", "currency_exchange", "curry", "custard", "customs", "cyclone", "dancer", "dancers", "dango", "dart", "dash", "date", "de", "deciduous_tree", "department_store", "diamond_shape_with_a_dot_inside", "diamonds", "disappointed", "disappointed_relieved", "dizzy", "dizzy_face", "do_not_litter", "dog", "dog2", "dollar", "dolls", "dolphin", "door", "doughnut", "dragon", "dragon_face", "dress", "dromedary_camel", "droplet", "dvd", "e-mail", "ear", "ear_of_rice", "earth_africa", "earth_americas", "earth_asia", "egg", "eggplant", "eight", "eight_pointed_black_star", "eight_spoked_asterisk", "electric_plug", "elephant", "email", "end", "envelope", "envelope_with_arrow", "es", "euro", "european_castle", "european_post_office", "evergreen_tree", "exclamation", "expressionless", "eyeglasses", "eyes", "facepunch", "factory", "fallen_leaf", "family", "fast_forward", "fax", "fearful", "feelsgood", "feet", "ferris_wheel", "file_folder", "finnadie", "fire", "fire_engine", "fireworks", "first_quarter_moon", "first_quarter_moon_with_face", "fish", "fish_cake", "fishing_pole_and_fish", "fist", "five", "flags", "flashlight", "flipper", "floppy_disk", "flower_playing_cards", "flushed", "foggy", "football", "footprints", "fork_and_knife", "fountain", "four", "four_leaf_clover", "fr", "free", "fried_shrimp", "fries", "frog", "frowning", "fu", "fuelpump", "full_moon", "full_moon_with_face", "game_die", "gb", "gem", "gemini", "ghost", "gift", "gift_heart", "girl", "globe_with_meridians", "goat", "goberserk", "godmode", "golf", "grapes", "green_apple", "green_book", "green_heart", "grey_exclamation", "grey_question", "grimacing", "grin", "grinning", "guardsman", "guitar", "gun", "haircut", "hamburger", "hammer", "hamster", "hand", "handbag", "hankey", "hash", "hatched_chick", "hatching_chick", "headphones", "hear_no_evil", "heart", "heart_decoration", "heart_eyes", "heart_eyes_cat", "heartbeat", "heartpulse", "hearts", "heavy_check_mark", "heavy_division_sign", "heavy_dollar_sign", "heavy_exclamation_mark", "heavy_minus_sign", "heavy_multiplication_x", "heavy_plus_sign", "helicopter", "herb", "hibiscus", "high_brightness", "high_heel", "hocho", "honey_pot", "honeybee", "horse", "horse_racing", "hospital", "hotel", "hotsprings", "hourglass", "hourglass_flowing_sand", "house", "house_with_garden", "hurtrealbad", "hushed", "ice_cream", "icecream", "id", "ideograph_advantage", "imp", "inbox_tray", "incoming_envelope", "information_desk_person", "information_source", "innocent", "interrobang", "iphone", "it", "izakaya_lantern", "jack_o_lantern", "japan", "japanese_castle", "japanese_goblin", "japanese_ogre", "jeans", "joy", "joy_cat", "jp", "key", "keycap_ten", "kimono", "kiss", "kissing", "kissing_cat", "kissing_closed_eyes", "kissing_heart", "kissing_smiling_eyes", "koala", "koko", "kr", "lantern", "large_blue_circle", "large_blue_diamond", "large_orange_diamond", "last_quarter_moon", "last_quarter_moon_with_face", "laughing", "leaves", "ledger", "left_luggage", "left_right_arrow", "leftwards_arrow_with_hook", "lemon", "leo", "leopard", "libra", "light_rail", "link", "lips", "lipstick", "lock", "lock_with_ink_pen", "lollipop", "loop", "loudspeaker", "love_hotel", "love_letter", "low_brightness", "m", "mag", "mag_right", "mahjong", "mailbox", "mailbox_closed", "mailbox_with_mail", "mailbox_with_no_mail", "man", "man_with_gua_pi_mao", "man_with_turban", "mans_shoe", "maple_leaf", "mask", "massage", "meat_on_bone", "mega", "melon", "memo", "mens", "metal", "metro", "microphone", "microscope", "milky_way", "minibus", "minidisc", "mobile_phone_off", "money_with_wings", "moneybag", "monkey", "monkey_face", "monorail", "moon", "mortar_board", "mount_fuji", "mountain_bicyclist", "mountain_cableway", "mountain_railway", "mouse", "mouse2", "movie_camera", "moyai", "muscle", "mushroom", "musical_keyboard", "musical_note", "musical_score", "mute", "nail_care", "name_badge", "neckbeard", "necktie", "negative_squared_cross_mark", "neutral_face", "new", "new_moon", "new_moon_with_face", "newspaper", "ng", "nine", "no_bell", "no_bicycles", "no_entry", "no_entry_sign", "no_good", "no_mobile_phones", "no_mouth", "no_pedestrians", "no_smoking", "non-potable_water", "nose", "notebook", "notebook_with_decorative_cover", "notes", "nut_and_bolt", "o", "o2", "ocean", "octocat", "octopus", "oden", "office", "ok", "ok_hand", "ok_woman", "older_man", "older_woman", "on", "oncoming_automobile", "oncoming_bus", "oncoming_police_car", "oncoming_taxi", "one", "open_book", "open_file_folder", "open_hands", "open_mouth", "ophiuchus", "orange_book", "outbox_tray", "ox", "package", "page_facing_up", "page_with_curl", "pager", "palm_tree", "panda_face", "paperclip", "parking", "part_alternation_mark", "partly_sunny", "passport_control", "paw_prints", "peach", "pear", "pencil", "pencil2", "penguin", "pensive", "performing_arts", "persevere", "person_frowning", "person_with_blond_hair", "person_with_pouting_face", "phone", "pig", "pig2", "pig_nose", "pill", "pineapple", "pisces", "pizza", "point_down", "point_left", "point_right", "point_up", "point_up_2", "police_car", "poodle", "poop", "post_office", "postal_horn", "postbox", "potable_water", "pouch", "poultry_leg", "pound", "pouting_cat", "pray", "princess", "punch", "purple_heart", "purse", "pushpin", "put_litter_in_its_place", "question", "rabbit", "rabbit2", "racehorse", "radio", "radio_button", "rage", "rage1", "rage2", "rage3", "rage4", "railway_car", "rainbow", "raised_hand", "raised_hands", "raising_hand", "ram", "ramen", "rat", "recycle", "red_car", "red_circle", "registered", "relaxed", "relieved", "repeat", "repeat_one", "restroom", "revolving_hearts", "rewind", "ribbon", "rice", "rice_ball", "rice_cracker", "rice_scene", "ring", "rocket", "roller_coaster", "rooster", "rose", "rotating_light", "round_pushpin", "rowboat", "ru", "rugby_football", "runner", "running", "running_shirt_with_sash", "sa", "sagittarius", "sailboat", "sake", "sandal", "santa", "satellite", "satisfied", "saxophone", "school", "school_satchel", "scissors", "scorpius", "scream", "scream_cat", "scroll", "seat", "secret", "see_no_evil", "seedling", "seven", "shaved_ice", "sheep", "shell", "ship", "shipit", "shirt", "shit", "shoe", "shower", "signal_strength", "six", "six_pointed_star", "ski", "skull", "sleeping", "sleepy", "slot_machine", "small_blue_diamond", "small_orange_diamond", "small_red_triangle", "small_red_triangle_down", "smile", "smile_cat", "smiley", "smiley_cat", "smiling_imp", "smirk", "smirk_cat", "smoking", "snail", "snake", "snowboarder", "snowflake", "snowman", "sob", "soccer", "soon", "sos", "sound", "space_invader", "spades", "spaghetti", "sparkle", "sparkler", "sparkles", "sparkling_heart", "speak_no_evil", "speaker", "speech_balloon", "speedboat", "squirrel", "star", "star2", "stars", "station", "statue_of_liberty", "steam_locomotive", "stew", "straight_ruler", "strawberry", "stuck_out_tongue", "stuck_out_tongue_closed_eyes", "stuck_out_tongue_winking_eye", "sun_with_face", "sunflower", "sunglasses", "sunny", "sunrise", "sunrise_over_mountains", "surfer", "sushi", "suspect", "suspension_railway", "sweat", "sweat_drops", "sweat_smile", "sweet_potato", "swimmer", "symbols", "syringe", "tada", "tanabata_tree", "tangerine", "taurus", "taxi", "tea", "telephone", "telephone_receiver", "telescope", "tennis", "tent", "thought_balloon", "three", "thumbsdown", "thumbsup", "ticket", "tiger", "tiger2", "tired_face", "tm", "toilet", "tokyo_tower", "tomato", "tongue", "top", "tophat", "tractor", "traffic_light", "train", "train2", "tram", "triangular_flag_on_post", "triangular_ruler", "trident", "triumph", "trolleybus", "trollface", "trophy", "tropical_drink", "tropical_fish", "truck", "trumpet", "tshirt", "tulip", "turtle", "tv", "twisted_rightwards_arrows", "two", "two_hearts", "two_men_holding_hands", "two_women_holding_hands", "u5272", "u5408", "u55b6", "u6307", "u6708", "u6709", "u6e80", "u7121", "u7533", "u7981", "u7a7a", "uk", "umbrella", "unamused", "underage", "unlock", "up", "us", "v", "vertical_traffic_light", "vhs", "vibration_mode", "video_camera", "video_game", "violin", "virgo", "volcano", "vs", "walking", "waning_crescent_moon", "waning_gibbous_moon", "warning", "watch", "water_buffalo", "watermelon", "wave", "wavy_dash", "waxing_crescent_moon", "waxing_gibbous_moon", "wc", "weary", "wedding", "whale", "whale2", "wheelchair", "white_check_mark", "white_circle", "white_flower", "white_large_square", "white_medium_small_square", "white_medium_square", "white_small_square", "white_square_button", "wind_chime", "wine_glass", "wink", "wolf", "woman", "womans_clothes", "womans_hat", "womens", "worried", "wrench", "x", "yellow_heart", "yen", "yum", "zap", "zero", "zzz"] - Discourse.Dialect.inlineReplace({ + function imageFor(code) { + if (emoji.indexOf(code) !== -1) { + var url = Discourse.getURL('/assets/emoji/' + code + '.png'); + return ['img', {href: url, title: ':' + code + ':', 'class': 'emoji', alt: code}]; + } + } + + // Also support default emotions + var translations = { + ':)' : 'smile', + ':(' : 'frowning', + ';)' : 'wink', + ':\'(' : 'cry', + ':P' : 'stuck_out_tongue', + ':O' : 'open_mouth', + ':D' : 'grin', + ':|' : 'expressionless', + ";P" : 'stuck_out_tongue_winking_eye', + ';)' : 'wink', + ":$" : 'blush' + }; + + Object.keys(translations).forEach(function (code) { + Discourse.Dialect.inlineReplace(code, imageFor(translations[code])); + }); + + Discourse.Dialect.inlineBetween({ between: ':', rawContents: true, - emitter: function(contents) { - if (emoji.indexOf(contents) !== -1) { - var url = Discourse.getURL('/assets/emoji/' + contents + '.png'); - return ['img', {href: url, title: ':' + contents + ':', 'class': 'emoji', alt: contents}]; - } - } + emitter: imageFor }); if (Discourse && Discourse.ComposerView) { @@ -36,12 +57,15 @@ transformComplete: function(v){ return v + ":"; }, dataSource: function(term){ + var full = ":" + term; term = term.toLowerCase(); if (term === "") { - return Ember.Deferred.promise(function (promise) { - promise.resolve(["smile", "smiley", "wink", "sunny", "blush"]); - }); + return Ember.RSVP.resolve(["smile", "smiley", "wink", "sunny", "blush"]); + } + + if (translations[full]) { + return Ember.RSVP.resolve([translations[full]]); } var options = []; @@ -62,9 +86,7 @@ } } - return Ember.Deferred.promise(function (promise) { - promise.resolve(options); - }); + return Ember.RSVP.resolve(options); } }); });