steveathon / bootstrap-wysiwyg

Tiny bootstrap-compatible WYSIWYG rich text editor
MIT License
661 stars 1.71k forks source link

Support for insert code #71

Open 1993hzh opened 8 years ago

1993hzh commented 8 years ago

Hi,

I am using this great editor in my own blog, however, I didn't see any button or command for me to insert code, like <code>code here.</code> or <pre>code here</pre>. Without this, I could not use google code prettify, so I have to add insertCode() by myself...(even I am not good at javascript)

So, my question is: is there any button or command for users to insert code now?

Best Regards! Leo

codewithtyler commented 8 years ago

No, there is currently no way to do this at this time. Per #66 this is something that I've already been looking in to, but have not yet come up with a solution.

techmag commented 8 years ago

I'll add a ditto to this.

Based on what I thought I read in the README this would have been as simple as adding:

<a class="btn btn-default" data-edit="code" title="" data-original-title="Code"><i class="fa fa-code"></i></a>

README claims: "Does not force any styling - it's all up to (me)." -- Question then becomes HOW? :)

Note I've moved to the Font Awesome icons...

Unsure how <code> and <pre> are much different at a basic level than <b> or <u>.

I'm not seeing where in the code these elements are "defined" -- curious that bold is defined/implemented as follows:

<a class="btn btn-default" data-edit="bold" title="" data-original-title="Bold (Ctrl/Cmd+B)"><i class="fa fa-bold"></i></a>

Note that "bold" is the word in data-edit and "b" is the actual tag inserted. So I expected to see something in the code that reference either (both) of those. Guess I'm still in need of more JS education myself as well.

What would it take - either work-around or otherwise -- to make CODE and PRE functional tags in the toolbar?

codewithtyler commented 8 years ago

@techmag I'll just go through and answer each of your questions/comments one at a time.

The goal is to eventually be able to support code snippets using the data-edit="code" syntax. However, the only way we'll be able to do this is to create our own custom implementation.

By styling the README refers to how you layout your editor. You can use Bootstrap, Foundation, roll your own CSS, etc. You can use the Bootstrap icons or Font Awesome. You get the picture. You style it how you want. The only thing we need is that you set the data attributes correctly so we know what to apply to the text (i.e. bold, italics, etc.)

The way the project works is we utilize the document.execCommand() method. You can find information about it here. We are limited to the commands that are supported by that function. The commands we're referring to you've already found. They are the values of the data-edit attribute. For a full list of commands you can find them here. Anything not in that list such as pre and code tags we have to implement ourselves.

From what I understand there's a way that you can get the pre tag to work using the formatblock command but the trick is combining that with a code element. So this would have to be a custom implementation and my focus lately have been on restructuring the code so we can add unit tests. I'm about to work on the unit tests and also work on #83 while I'm at it. So this has been put on the back burner. I think what will end up happening is we'll make this into a plugin one the plugin system is created. Ideally I'd like to start on the plugin system next month. It just depends on how available @steveathon is to help me work on that. My current goals are to have all the unit tests done and #83 closed by the end of the month.(All work on this in my free time). However, if you get any ideas or would like to try and take this one just let me and I'll be happy to help any way I can.

techmag commented 8 years ago

Extremely helpful - thank you!

If only insertHTML would have a similar companion like wrapWithHTML we'd be all set :)

A generic routine that finds both end points of a selection and allow for insertion of pairs of "things" would do nicely as a catch-all for any "missing stuff" until it was explicitly included.

Ah - oh wait - that is pretty much what format block might do (sans any white/blacklisting it has built in).

Appreciate the pointer -- will see what I can figure out.

techmag commented 8 years ago

Ha!

<a class="btn btn-default" data-edit="formatBlock pre" title="" data-original-title="Pre"><i class="fa fa-hmm"></i></a> works!

It is "block-level" based NOT selection based so it's a little funky depending on what blocks are (almost) in your current selection. It grabs outwards to the nearest block end points.

code however does not...

So close...

codewithtyler commented 8 years ago

This snippet is a few months old but it should give you an ideas of how I was thinking this could be implemented.

http://pastebin.com/Wx26H7Rm

At the time I was looking into cominging the insertHTML with document.getSelection().

techmag commented 8 years ago

There's likely a million things wrong with this as I'm not really all that well versed in JavaScript but this worked:

bootstrap-wysiwyg_js_ __users_garyhewett_documents_github_play_icrmbeta

I'm sure it could be genericized properly with a small amount of effort.

techmag commented 8 years ago

Slightly better...

We need to change 'code' to an array of acceptable things to look for, do a "contains" and grab the index and then instead of hard coding 'code' just use the index on the initial array to grab the element we wanted and that should do it.

bootstrap-wysiwyg_js_ __users_garyhewett_documents_github_play_icrmbeta

codewithtyler commented 8 years ago

Ok, I noticed a couple of things. First, the code you're looking at is a bit outdated. In the most recent release we removed the underscoreThrottle function from the project. The section you're looking at currently looks like this:

var commandArr = commandWithArgs.split( " " ),
      command = commandArr.shift(),
      args = commandArr.join( " " ) + ( valueArg || "" );

var parts = commandWithArgs.split( "-" );

if ( parts.length === 1 ) {
      document.execCommand( command, false, args );
} else if ( parts[ 0 ] === "format" && parts.length === 2 ) {
      document.execCommand( "formatBlock", false, parts[ 1 ] );
}

So if we take you're changes and apply it to this section it would look like the following:

if ( parts.length === 1 ) {
      if ( parts[ 0 ] === "code" ) {
            document.execCommand( "insertHTML", false, "<code>" + document.getSelection() + "</code>" );
      } else {
            docoument.execCommand( command, false, args );
      }
}
else if ( parts[ 0 ] === "format" && parts.length === 2 ) {
      document.execCommand( "formatBlock", false, parts[ 1 ] );
}

Second, while I agree that validating custom elements against an array would work. It would be an unnecessary validation until we have a need for more than just the <code></code> elements.

techmag commented 8 years ago

There are no booleans :)

I have a design principle that says there is either precisely one thing or an infinite number of things. (Or at least Integer.MAX...) There is never a case that stands the test of time with "two" things. Or in this case something that looks like one thing...

If we look to JSoup (https://github.com/jhy/jsoup/blob/master/src/main/java/org/jsoup/safety/Whitelist.java) we see a few real world "other" examples in their list.

I'll (ahem) cite "q" as the real world use case for me as one project currently in dev is a user-editable Bibliography :)

Using an array allows for both documentation and expansion without anyone bugging you -- how's that for a sales job? :)

I have lots of other choices I would need to use but that are more non-standard.

Using a default array (even if only a single value) and making it replaceable upon initialization by passing an alternative in through the constructor as an optional parameter would mean no more hacking the code to get extras to work. There will always be yet one more use case in a situation like this.

Alternatively just make the array malleable so it can be added/subtracted from by the end users code...

My $0.02

codewithtyler commented 8 years ago

The one use case we still haven't covered is the combination of pre and code tags. The code snippet we currently have will work so long as we are only looking at one element at a time. But let's say the user is using highlight.js which means they will need to use <pre></code></code></pre> in order to get their highlighting to work. In the html this might look something like data-edit="pre code". We will need a way to accommodate this type of use case. While we're at it we should probably consider putting a limit on the number of elements one can add. That way someone doesn't try something like this:

data-edit=" pre code span pre span code"

I really don't see any library requiring their uses to write anything that complicated in order to use their library. Actually for syntax highlighting adding the pre and code tags is probably one of the more involved methods I've seen.

techmag commented 8 years ago

If we modify the defaults:

 var defaults = {
            hotKeys: {
            "Ctrl+b meta+b": "bold",
            "Ctrl+i meta+i": "italic",
            "Ctrl+u meta+u": "underline",
            "Ctrl+z": "undo",
            "Ctrl+y meta+y meta+shift+z": "redo",
            "Ctrl+l meta+l": "justifyleft",
            "Ctrl+r meta+r": "justifyright",
            "Ctrl+e meta+e": "justifycenter",
            "Ctrl+j meta+j": "justifyfull",
            "Shift+tab": "outdent",
            "tab": "indent"
            },
            toolbarSelector: "[data-role=editor-toolbar]",
            commandRole: "edit",
            activeToolbarClass: "btn-info",
            selectionMarker: "edit-focus-marker",
            selectionColor: "darkgrey",
            dragAndDropImages: true,
            keypressTimeout: 200,
            fileUploadError: function( reason, detail ) { console.log( "File upload error", reason, detail ); },
            approvedRawTags: ["q", "code"]
        };

The the block of code we add is as follows:

        if ( parts.length === 1 ) {
            if ( $.inArray(parts[ 0 ], options.approvedRawTags) > 0) {
              document.execCommand( "insertHTML", false, "<" + parts[ 0 ] + ">" + document.getSelection() + "</" + parts[ 0 ] + ">" );
            } else {
              document.execCommand( command, false, args );
            }
        } else if ( parts[ 0 ] === "format" && parts.length === 2 ) {
            document.execCommand( "formatBlock", false, parts[ 1 ] );
        }

I'm sure this is far from elegant :)

However as it is based on "user world timing" (click and update) the extra checks will be negligible to the end user experience...

With regards to the pre code problem it won't be hard to modify the single line to accept an array and a array reversed instead of just part[0] -- I'll see what I can do but not a high priority for me. (If this were Scala it would be done by now... not that Scala is better just that I understand it more)

IF I understand this line correctly:

        var options = $.extend( true, {}, defaults, userOptions );

THEN that means our goal of have a user supply their list of tags without mucking with the code is achieved. They just supply a replacement within the userOptions object.

Speaking of lines -- any docs on this line please?

        return html && html.replace( /(<br>|\s|<div><br><\/div>|&nbsp;)*$/, "" );

Want to make sure it doesn't shoot us in the foot along the way. Seems to be playing with BREAKS and either DIVing them (which is a huge PITA for one of my uses cases) and/or inserting a NO-BREAK -SPACE.

spreadred commented 7 years ago

return html && html.replace( /(<br>|\s|<div><br><\/div>|&nbsp;)*$/, "" );

It appears to be a regular expression which removes all instances of <br>, whitespace (\s and &nbsp;), and <div><br></div> starting from the beginning of html

spreadred commented 7 years ago

Just thought I'd mention that if you only wrap your "code" in <code> tags, and not <pre><code>, your line breaks will not be honored. This is likely NOT the desired behavior for most use cases.